Lab: Quaratine Style in JavaScript

Quarantine style in JavaScript - Introduction

In this lab you will implement a solution of the Quarantine style in JavaScript for the term frequency task.

(The unchanged task description is available in the Prelude of the first lab.)

These are the constraints for the Quarantine style as specified in the book:

  • Core program functions have no side effects of any kind, including IO.
  • All IO actions must be contained in computation sequences that are clearly separated from the pure functions.
  • All sequences that have IO must be called from the main program.

The goal of this lab is to write code that follows (a clean version of) this style.

Create and Clone Your GitHub Repository

To create the repository for this lab, fork this starter repository.

Your repository now exists on the GitHub servers. If you want to work on it, you first have to “clone” it on your computer.

Implement Your Program

You can find Crista’s Python implementation of the Term Frequency task in Quarantine style in her GitHub repository.


⚠️ WARNING ⚠️

Although Crista’s implementation obeys to all the constraints above, the implementation deviates in important ways from the mathematical concept of a monad. (A quick glance at the implementation of bind also reveals some Python “hacks”.)

We provide here a modified version of Crista’s implementation in Python that both (i) respects the style constraints, and (ii) properly models the behavior of a monad.

Carefully peruse the following implementation before working on yours.

#!/usr/bin/env python
import operator
import re
import string
import sys


#
# The Quarantine class for this example
#
class TFQuarantine:

    def __init__(self, fn_to_run):
        self.fn_to_run = fn_to_run

    def map(self, f):
        def new_fn_to_run():
            return f(self.run())
        return TFQuarantine(new_fn_to_run)

    def bind(self, f):
        def new_fn_to_run():
            return f(self.run()).run()
        return TFQuarantine(new_fn_to_run)

    def run(self):
        return self.fn_to_run()

#
# The functions
#
def get_input():
    def _f():
        return sys.argv[1]
    return TFQuarantine(_f)

def extract_words(path_to_file: str):
    def _f():
        with open(path_to_file) as f:
            data = f.read()
        pattern = re.compile('[\\W_]+')
        word_list = pattern.sub(' ', data).lower().split()
        return word_list
    return TFQuarantine(_f)

def remove_stop_words(word_list: list[str]):
    def _f():
        with open('stop_words.txt') as f:
            stop_words = f.read().split(',')
        # add single-letter words
        stop_words.extend(list(string.ascii_lowercase))
        return [w for w in word_list if not w in stop_words]
    return TFQuarantine(_f)

def frequencies(word_list: list[str]):
    word_freqs : dict[str, int] = {}
    for w in word_list:
        if w in word_freqs:
            word_freqs[w] += 1
        else:
            word_freqs[w] = 1
    return word_freqs

def sort(word_freq: dict[str, int]):
    return sorted(word_freq.items(), key=operator.itemgetter(1), reverse=True)

def top25_freqs(word_freqs: list[tuple[str, int]]):
    top25 = ""
    for tf in word_freqs[0:25]:
        top25 += str(tf[0]) + ' - ' + str(tf[1]) + '\n'
    return top25

def print_stdout(output: str):
    def _f():
        print(output)
    return TFQuarantine(_f)

#
# First we compose our entire program
#
main = get_input() \
.bind(extract_words) \
.bind(remove_stop_words) \
.map(frequencies) \
.map(sort) \
.map(top25_freqs) \
.bind(print_stdout)

# And only then we run it
main.run()

Use the modified version of Crista’s implementation as a starting point for your JavaScript code. You can also make use of the functions’ implementations you wrote for the JavaScript Pipeline lab (or the code snippets provided in the description of that lab).

Once you have the repository (a directory) on your computer, you can open it in your IDE (e.g., in VS Code). You can easily do this using the code command, passing as an argument the path to the appropriate directory:

code ~/lab-07-quarantine-javascript

Before you start “hacking”, please read the README (file README.md).

Then create a new JavaScript file named TermFrequency.js.

Implement a solution to the term frequency task in JavaScript following the Quarantine style.

Run, Test, and Debug the Program

Read the README.md and the first part of this lab to figure out how to run the program.

Test

We strongly recommend to use the test script, because it shows you whether your solution is functionally correct. For this, it compares your output with the reference output we provided. Obviously, this only works if you strictly follow the rules (e.g., don’t print out debug output), otherwise the script will tell you that you don’t produce the correct output.