(Part 2 here)
The best way to learn something is to write about it. So here I am, trying to learn functional programming.
My tool of choice was Racket, a former Scheme dialect that is growing to be called a language by itself.
I’m trying to bend the language to compose functions the way Factor does. So, here are my first two experiments.
What am I trying to fix?
In functional style it’s common to “pipe” data through more than one function. Let’s say I want to get the first column of a CSV file, I could do something like that:
(get-first (split-columns (open “some.csv”)))
To understand it, we have to read that back to front or keep in mind all the operations until we reach “…csv”. Get a slightly longer set of operations and things get really difficult to understand.
I want to write the same thing as:
(-> (open “some.csv”) split-columns get-first)
Simple composition using macros
Let’s try a simpler thing, add one to a number and convert it to string. It’s fast and easy with macros once you figure it out.
This is a recursive macro. When Racket founds “->” as the first thing after parenthesis it tries to match against two patterns:
; if there is just one argument after "->", return it
[(-> a) a]; if there is more arguments, apply (execute) the last one
; and call "->" again with remaining arguments as its parameter
[(-> b ... a) (a (-> b ...))]
Macros in Racket are amazingly simple once you get it, besides there is no performance penalty as they are expanded in compiling phase. However, there is one limitation: it’s not a function. That means I can’t pass it around as an argument to functions.
So, let’s try again, but before that, what are macros?
Interlude: what are macros?
Macros look like functions when you use them, but actually they modify the code before execution. Think you can modify your AST before the program runs.
This is incredibly powerful and the main use is to extend the language in ways that functions can’t.
Simple compositions using functions
It works exactly the same as the macro version, but it’s slightly different inside.
Again, it’s a recursive function. The “. ps” means “any number of arguments”.
; if I have just one argument, return it
[((list a)) a]; if I have more arguments, call the last one (as a function)
; and then call "->" again with the remaining arguments
[((list b ... a)) (a (apply -> b))]
They are simple and not yet what Factor does, but it’s a (baby) step!
Particularly in Racket there is “compose1” that is similar, but the functions are applied in reverse order. Also, in my version all functions are limited to just one argument.
These are tasks for the part 2.