In a prior post I mentioned that I was working my way through the exercises in If Hemingway Wrote Javascript. The semester is over, so I finally have some time to return to it.

I was writing solutions to all the exercises in Haskell, when I basically hit a brick wall when Exercise 5 asks the student to “Write a chainable function that accepts one word per function call but, when called without arguments, will report back all the previously passed words in order.” In Haskell…

In case you are uninitiated, Haskell is a strongly-typed, purely-functional programming language, which implies that data types for functions’ inputs and outputs must be clearly defined, and furthermore that variables cannot be mutated in place. Exercise 5 asks us to write a function that can accept as input a string or nothing, and then will either produce an IO action or mutate(!) a running list of strings, depending on the type of the input.

I’m not saying that this can’t be done in Haskell—It totally can be done in Haskell. It’s just that the notions of function in Haskell and function in other programming languages don’t exactly overlap. Solving this problem in the most elegant way will involve clever use of the State monad and the IO monad and maybe even defining some of my own monads. You can see my work in progress on my github repo.

The problem Exercise 5 poses makes much more sense in an object-oriented paradigm: I decided I’d write my Python solution first. Here was my first go:

# sayIt.py
words = []

def sayIt( x = None ):
    global words
    if x is None:
        message = ' '.join(words)
        print(message)
    else:
        words = words + [x]

Here, we define the global variable words and the function sayIt operates more or less how the problem statement requires, except for one detail: sayIt isn’t chainable.

In order to make it chainable, we need to (ed: can) make it a method of an object. If it returns itself upon method calls, then we can just dot subsequent method calls on the end. Here’s my second go:

# sayItClassy.py
class Words:
    def __init__(self):
        self.words = []
    def _(self, x = None):
        if x is None:
            message = ' '.join(self.words)
            print(message)
        else:
            self.words = self.words + [x]
        return self

When an instance of Words is declared, it’s given an internal variable called words which stores the current state, ie, the list of all the words that we’ve passed to the object via its sole method _. The method _ has the behavior of sayIt from above, but applies changes to the instance rather than to a global variable. Thus, in the Python interactive prompt, we get the desired behavior:

>>> sayIt = Words()
>>> sayIt._("my")._("name")._("is")._("Daniel")._()
my name is Daniel