Since I’ve dissed patterns and OOP recently (and rightly so) I thought I’d be a little positive and recycle an email I sent a friend a while ago. This someone was curious about functional languages after I had mentioned them over dinner as he had no experience with them. I absolutely think that if all you have ever used is an imperative language (exemplified by C++, Java, C# and other similar crap) I highly recommend spending (at least) several days learning a functional language. You’ll be a better programmer for it. (And why not learn some logic programming too, while you’re at it.)

What follows is a somewhat edited version of the 30-minute stream-of-consciousness crash-course email I sent to my friend. (So it’s not the best tutorial in the world, but hopefully it gets someone interested in playing around with a new language.)

Over the years I’ve played around with a lot of different functional languages (including Miranda, Standard ML, Haskell, FP, LISP, and Scheme). Here I’m using Standard ML (SML) because it nicely illustrates some of the cool concepts I wanted to outline (and other than LISP, is the one I know best). Haskell is another good choice for a first functional language and would be worth exploring in addition to SML, notably because Haskell features lazy evaluation (as well as several other important concepts).

To follow along, begin by downloading and installing an SML interpreter.

A smattering of SML

First, some fundamentals. In SML we bind values to variables using “val” and functions to variables using “fun”.

- val m = 42;

val m = 42 : int

- fun inc x = x + 1;

val inc = fn : int -> int



The “- ” is the prompt, so the rest of the line is what was typed. Note that as we type a line and hit return, the system interactively responds with a result on the following line. Note also how SML derives types automatically (known as type inference). “1” is an int and “+” takes two arguments of the same type (both ints or both floats) so “x” must be an int and therefore “inc” must be a function that takes an int as an argument and returns an int (“int -> int”).

The other thing we need to know is that SML natively supports tuples:

- val n = (1,2,3);

val n = (1,2,3) : int * int * int



Note again how it derives the types, using “*” to denote a tuple (or cartesian product if you prefer).

What I want to get to is one of the more powerful concepts of SML: the ability to define recursive data types, by cases. So, diving right in, let us define a binary tree data structure:

- datatype 'a BinTree = Leaf of 'a | Node of 'a BinTree * 'a * 'a BinTree;

datatype 'a BinTree = Leaf of 'a | Node of 'a BinTree * 'a * 'a BinTree



This oneliner(!) says that a data type “BinTree” that contains elements of type “‘a” (a special notation of a polymorph type; think templated type) is either (|):

a “Leaf” of one element, or

a “Node” of a 3-tuple, where the first value is a BinTree of the same type, the second value is an element of the type, and the third value is a BinTree of the type.

But more than that, we can now also use “Node” and “Leaf” as constructors to create a tree on the fly:

- val t = Node(Leaf(1), 2, Node(Leaf(3), 4, Leaf(5)));

val t = Node (Leaf 1,2,Node (Leaf #,4,Leaf #)) : int BinTree



Note how the SML interpreter prints the structure for us, but caps it at a certain depth (showing “#” to show it capped it).

We can also construct a tree with floats

- val u = Node(Leaf(1.0), 2.0, Leaf(3.0));

val u = Node (Leaf 1.0,2.0,Leaf 3.0) : real BinTree



which nicely shows how the data type is polymorphic (“type abstracted”). And again it derived the type (“real” as floats are called in SML).

We can now e.g. declare a recursive function “height” that computes the height of a BinTree using declaration by cases:

- fun height (Leaf(x)) = 1

| height (Node(lft, x, rht)) = 1 + Int.max(height lft, height rht);

val height = fn : 'a BinTree -> int



The first case (the first line) is that height is called with an argument that pattern matches “Leaf(x)”, i.e. something constructed using “Leaf” and with a single argument “x”. The second case is that height is called with an argument matching “Node(lft, x, rht)” where lft, x, rht are arbitrary variable names that will bind to the actual values in the supplied Node argument. In the first case, we just return 1 (as a tree with just a leaf has height 1). In the second case, we recursively compute the heights of the subtrees, take the maximum, and add 1. Again the system infers the type, saying height is a function that takes a BinTree of whatever type and returns an int.

We can try height now on “t” and “u”:

- height t;

val it = 3 : int

- height u;

val it = 2 : int



SML also has lists built in:

- val r = [1,2,3];

val r = [1,2,3] : int list

- val s = [4];

val s = [4] : int list

- r @ s;

val it = [1,2,3,4] : int list



The “@” is the built-in append function.

We can now e.g. write a function that takes a BinTree, visits it in depth-first order, and collects all the node values into a list:

- fun values (Leaf(x)) = [x]

| values (Node(lft, x, rht)) = (values lft) @ [x] @ (values rht);

val values = fn : 'a BinTree -> 'a list



(This function usually exists in functional languages, normally known as “flatten.”) And we can use it directly:

- values t;

val it = [1,2,3,4,5] : int list



Note how if we don’t bind the value, the system always binds the last result to “it”. We can use “it” directly if we want.

Now lets create a function “sq” that squares its argument, and we’ll “map” this sq function across every element of the list that is the flattened tree:

- fun sq x = x * x;

val sq = fn : int -> int

- map sq (values t);

val it = [1,4,9,16,25] : int list



“map” is a built in function that takes a function (of type “‘a -> ‘a”) and a list (of type “‘a”) and then applies the function to every element of the list. It is a very useful function that you can now find in a lot of languages, not just functional languages (e.g. in Python and several others.)

BTW, we don’t need to actually bind the function. We can create the sq function on the fly (a so-called lambda function) too, using “fn”:

- map (fn x => x * x) (values t);

val it = [1,4,9,16,25] : int list



And for the last example, SML also handles higher-order functions in the sense we can apply just partial arguments. To show this, first we define a function in two arguments:

- fun add x y = x + y;

val add = fn : int -> int -> int



Note how the type is “int -> int -> int” which is really saying that we have a function that takes an “int” and returns as result a function of type “int -> int”! We can see this is the case by passing only one argument to “add” and bind it to the variable “inc”:

- val inc = add 1;

val inc = fn : int -> int



Lo and behold, inc is a function, of type “int -> int”.

We can now use “inc” in e.g. the map function we used earlier:

- map inc [1,2,3];

val it = [2,3,4] : int list



Yup, it works!

To recap, these 4(!) lines of code implements a (templated) binary tree, declares an instance of a binary tree, and defines a function to compute the height of a binary tree:

datatype 'a BinTree = Leaf of 'a | Node of 'a BinTree * 'a * 'a BinTree;

val t = Node(Leaf(1), 2, Node(Leaf(3), 4, Leaf(5)));

fun height (Leaf(x)) = 1

| height (Node(lft, x, rht)) = 1 + Int.max(height lft, height rht);



How many lines of code do you need in C++ to do the same thing?!

In SML we can encapsulate the data type and the operations on it, just as is done with classes (objects) in object-oriented languages. In SML, such an encapsulation is called an abstract datatype (and created with the keyword abstype). I recommend following the link and reading about abstract data types if you are not familiar with them (and also reading the SML link at the top, to see how they relate to the module concept in SML). OO is not the be-all end-all of encapsulation if you ever thought that.

Edit: Based on a comment that was made (hi Vesa), I should mention that SML in addition has an advanced module system that can be used in preference of abstype. The point of mentioning abstract data types is that they’re an important concept (outside of their use in SML), so they are worthy of a study on their own.

In real life

Sadly, most functional languages are not industrial strength, in part because they never received industry financial backing (like Sun pushing Java, and Microsoft pushing C#). Functional languages in general lack IDEs, debuggers, libraries, and all those other things you need for real development, so the information provided here is mostly for expanding your mind and not for direct practical use. (Yes I know about F#, which is a commendable effort.) A notable exception is Erlang, which has been used and developed by the Swedish phone giant Ericsson (no relation) for many many years and is known to be very robust. This comparison between Erlang and Haskell is quite interesting. Overall, Erlang seems to be an up-and-coming language, which is cool, because we need something to break the dominance of the imperative crowd of languages. Erlang is also very interesting for its support for concurrency, which is highly relevant to this increasingly parallel world.

Further readings

Another few posts worth reading about learning more languages are Jim Tilander’s Are you bilingual and David Pollak’s Functional languages will rule. There’s also several quite good books available for learning more about functional languages (many free for the download):