I thought it might be fun to explore a little bit of CS as it applies to functional programming, by looking at the idea of Functional Data Structures. This is actually an area that is still getting a lot of active research, and is pretty interesting stuff overall. The general idea is to try and figure out ways to provide immutable data structures which can be efficiently implemented in a functional setting. So you look at some standard data structures, like a linked list, and find a way to implement that as an immutable linked list. One of the really cool features of Functional Data Structures is that because your dealing with them in an immutable setting, you can actually get a lot of re-use out of them….specifically for something like a list, you can add an item to the list, and return a “new” list that consists of the old list and the new item, and literally provide a structure that points to the old list instead of copying items. Even if you have other parts of the code referencing older versions of the list without the new item, you don’t have to worry since none of them can mutate the list.
The biggest body of research on this topic was published by Chris Okasaki in 1998, and is still the definitive reference on the subject today. Just for fun I’m going to look at some of the structures discussed in the original book and see what the implementations would look like in F# and C#. The original text provided samples in Standard ML, with an eppendix containing Haskell versions. I won’t go into too much depth on the theory behind the structures, but I will try to point out the interesting bits.
Without further ado, lets get rolling with our first data structure, which is also Okasaki’s first: Lists
Specifically, we’re going to implement a singly-linked list, which can be used rather effectively as a LIFO stack. To start off lets look at the F# version of the list, which is closest to what Okasaki listed in his book. The basic list type looks like this:
[fsharp] type List<‘a> = | Empty | Cons of ‘a * List<‘a> [/fsharp]
This is a simple Discriminated Union, with two options, Empty, and something I’ve called Cons in honor of the Lisp folks. The Cons option is basically a tuple containing an element of type type ‘a, and a List of ‘a. This by itself is reasonably uninteresting, so lets actually do something with this. [fsharp] let isEmpty = function | Empty -> true | _ -> false
let cons head tail= Cons(head,tail)
let head = function | Empty -> failwith “Source list is empty” | Cons(head,tail) -> head
let tail = function | Empty -> failwith “Source list is empty” | Cons(head,tail) -> tail
let rec (++) leftList rightList = match leftList with | Empty -> rightList | Cons(head,tail) -> Cons(head,tail ++ rightList)
let rec update list index value = match (list,index,value) with | (Empty,_,_) -> failwith “Source list of empty” | (Cons(_,tail),0,v) -> Cons(v,tail) | (Cons(_,tail),i,v) -> update tail (i - 1) v [/fsharp] Here we have some basic functions, an isEmpty check, a cons method (which creates a list), the head and tail functions, along with a ++ function, which appends two lists, plus an update method which changes the value of a particular element in the list. Notice the update and ++ functions are both recursive, and in the case of the ++ function, it is not tail recursive. This is probably ok in this case since the performance of the ++ function is O(n) where n = length of the left list. Both of these functions are also interesting because the F# compiler is unable to optimize them by converting them into a loop.
If we look at the C# version of these same structures things look pretty much the same: [csharp]public static class List { public static List
public static List
public class List
public bool IsEmpty { get { return false; } }
public List
}
public static List
return List.Cons
One very nice use for this particular structure is the LIFO stack. Rather than the typical “push” and “pop” operations, we have the “cons” and “head”/”tail” operations (in the case of pop, you have “head” which gives you the elements, and “tail” which gives you the rest of the list). This works well because pushing and popping are O(1). This structure is not all that different than the built-in List type in F#, without the benefit of the additional functions (filter, map, tryFind, etc). Thought it would be reasonably trivial to implement these in a recursive fashion.
That’s it for this segment…up next we’re going to look at using an immutable binary tree to implement a Set….good stuff for sure.