Archive for the 'Functional' Category

Agile Code in OCaml

First, a quick bit of background: I’m working on RunOrg [fr], a start-up that provides communities with their own online private social networks à la facebook. The technology stack is Linux-Apache-CouchDB-OCaml, and this has some implications that I will discuss below.

Facebook has it easy in terms of user management: an user starts existing on their platform the instant they sign up, at which point they fill in their first name and last name, and these are displayed to anyone who is allowed to see any hint of the user’s existence. So, making the first name and last name mandatory are quite acceptable.

At RunOrg, we cannot do this for several reasons:

  • User profiles may be created by communities as part of the membership management toolbox: we have to rely on user A to provide data about user B, and user A usually relies on an email-only source (such as newsletter or mailing-list registrations) where no first name or last name is available.
  • A given user may be part of several independent communities, and may choose to manage their identity separately for each one: appear as John Doe in an innocuous community they trust and John Censored in a more critical community.
  • We also allow users to keep control over whether a community is allowed to publish their name on the internet (as part of the online directory, or as comments on public articles).

Our needs for advanced privacy controls involve a more complex management of both what data is available and how we display it. The good news is, it’s certainly possible to handle all of this elegantly in terms of implementation. The bad news — we didn’t plan everything ahead.

It was only a few days ago that our customer requirements sessions brought up the issue of email-only sources: community managers were frustrated by the fact that our mass import functionality required first names and last names. The problem is, in almost every single programming language out there, making a required field become optional is a very dangerous endeavor because the development team must audit the entire code base to identify which parts of the code assume that the field is required, and describe what should happen when the field is null.

In your average PHP project, try making the user name optional and I can assure you that sentences like «You have been invited by  to this event» will appear. Someone failed to audit the who-invited-you-to-events code. At least with Java or C# you will get a Null Reference Exception of some kind that will show up in the logs and give you the opportunity to hunt down the mistake.

The good news is that our implementation language OCaml, does not allow null values. Instead, optional values are handled using a different value type, known as 'a option, which changes everything. An optional value simply cannot be accessed in the same way as a non-optional value. Trying to do so anyway will cause a type error that is picked up by the compiler, so a programmer can rely on these errors to quickly identify all locations the code that assume the value to be present.

I’ll say it again: in OCaml, a field being optional or mandatory is an assumption that is build into the type of that field, so changing the assumption involves changing the type and breaks all code that does not match the new assumption. Applying breaking changes to an OCaml code base is usually as simple as following a trail of compiler errors.

So, that’s what we did. We already knew that the behavior we wanted was to construct the “display name” of users like this:

  • If either the firstname or the lastname are present, use them (if both are present, use firstname-whitespace-lastname).
  • If none were present, then the private display name (visible only to the user themselves on their profile page and in the e-mails they receive) should be their e-mail address and the public display name (visible to everyone else) should be the username part of their e-mail address (so john.doe@gmail.com is shown as john.doe@…)

First, we defined two functions that compute the private and public display names based on the first name, last name and e-mail of the user. Then, the compiler error trail led us to all locations where a change was required, where we quickly identified whether the public or private name was to be displayed and replaced the existing code with our new display name functions. In total, a full audit of 40kLOC was done in less than an hour and I have proof that any code that uses the user name now handles the case where the user name is not provided.

The Rules

When working on any OCaml project, and especially on RunOrg, I follow these few rules:

  • Any assumption must cause a compiler error when broken. Either the code determines on the spot that the assumption is true, or I use the type system to prove that another part of the code already did. This rule took a massive toll on my early productivity, and I attributed it to an inherent cost of making compiler-enforced assumptions, but the real reason was that I was still pretty new at it — the elementary assumption enforcement from my smaller projects was too crude for the needs of the richness of RunOrg functionality, and it took me six months to refactor my early approach into an elegant and streamlined strategy of encoding assumptions into types.
  • Don’t work around the compiler or cheat with semantics. The initial reaction to a system that complains about every little change you make is to try and work around it by using more generic types or storing information where it does not belong. For instance, an easy solution to the optional name conundrum would have been to store john.doe@… as the name, but doing so would have been semantically incorrect (that’s a placeholder, not a name) and would have polluted the database “name” field with things that are not names and that will be treated differently from names at some point in the future.
  • Don’t accept mediocre code or patterns. Sometimes, design choices in the interface of module A will lead to ugly code in modules B, C and D because an unforeseen usage pattern happens to apply 95% of the time and the interface of module A was not designed with that usage pattern in mind. No amount of cleanup or refactoring in modules B, C and D will solve the problem, the only solution is to go back to the design of module A and change the interface even if it means that two hundred client modules will break. Keeping my code clean, elegant and short is worth wading through two hundred modules.
  • Perform lazy payments on your technical debt. I can propagate new design changes through your entire code base in one coding session, but this doesn’t mean I should. Instead, I keep a mental todo-list of all the changes that need to be applied, and apply all of them at once, locally, whenever I have to rework a given piece of code for any reason. While it may seem that such a todo-list is hard to keep and I will inevitably forget parts of it, remember that those design changes came around in order to solve the problem of ugly or mediocre code — by noticing that the code is ugly, I am reminded of the strategies that I set up in order to clean it up.
  • But be eager with small payments. If it’s a matter of moving a few functions around or refactoring a small piece of code, I do it as soon as I am done writing or rewriting it. Cleaning up little odd bits in a mostly clean code base is extremely rewarding.
  • Discover code by trying changes out. If the assumptions are correctly laid out, then the easiest way to determine the implications of a change — whether it will work and how long it will take — is simply to try it out. Following the compiler error trail will quickly reveal how many things are impacted by the change, as well as any unforeseen massive consequences. If it turns out that the change is too impacting, I just roll back my edits.
  • Keep interface patterns to a minimum. The basic idea behind having few different interfaces implemented by many parts of the system is usually expected to be «code is easier to reuse» but I disagree. Yes, that is a frequent benefit, but certainly not the most essential. Having few different interfaces means that most of my code can be described using a small vocabulary of interface patterns, and that looking at some code immediately reveals the pattern being used there. It also means that any design changes can be expressed in term of pattern changes, and can be applied almost blindly to all locations where that pattern was used. Last but not least, by using a simple shared vocabulary for large sections of the applications, I make it easier to recognize patterns in the more chaotic sections based on how they interact with the cleaner code. It’s easier to determine that two sentences have the same meaning if they share some words.
  • Love your code. In the RunOrg code base, priority 3 is making sure the code is well-designed, clean and free of technical debt, priority 2 is adding new features, priority 1 is making sure there are no bugs, and the drop-everything-you-do-and-work-on-this priority zero is that I should never hate working on the software. Motivation is paramount to keeping the code clean, feature-rich and bug-free, and even to working on the start-up in the first place, so anything that might make me question my dedication to the project or cause me pain while working on it must and will be corrected as soon as possible, regardless of other priorities.

I’m pretty certain that all of the rules are important, but I do believe the last one is an absolute prerequisite.

Article image © Ergonomik — Flickr

Monads and Asynchronous Javascript

A minor yet interesting syntax extension for Javascript would help solve the eternal problem of too many nested anonymous functions when writing asynchronous code. For instance, reacting to an AJAX request in standard jQuery looks like this:

$.getJSON('/status.php', {id : the_id}, function(data) {
  if (data.deleted) {
    $('#' + the_id).hide(500, function(){
      $(this).remove();
    });
  }
}

The function nesting is ugly, but necessary. In a monadic writing style, it would look like this instead :

var! data = $.getJSON('/status.php', {id : the_id});
if (data.deleted) {
  do! $('#' + the_id).hide(500);
  $(this).remove();
}

This style restores the imperative look of the function, hiding away the asynchronous nature of the code in the special keywords.

In itself, the rewriting is pretty simple to perform based on two unambiguous rules :

var! a, b, c, d = expr(x,y,z);
more code

Becomes :

expr(x,y,z,function(a,b,c,d) {
  more code
});

And there is a shorthand notation:

do! expr(x,y,z);
more code

That becomes :

expr(x,y,z,function(){
  more code
});

As a final example, here is how the “Rate Me” jQuery example would be written :

do! $(document).ready();

// generate markup
$("#rating").append("Please rate: ");

for ( var i = 1; i <= 5; i++ )
  $("#rating").append("<a href='#'>" + i + "</a> ");

{
  // add markup to container and apply click handlers to anchors
  var! e = $("#rating a").click();

  // stop normal link click
  e.preventDefault();

  // send request
  var! xml = $.post("rate.php", {rating: $(this).html()});

  // format and output result
  $("#rating").html(
    "Thanks for rating, current average: " +
    $("average", xml).text() +
    ", number of votes: " +
    $("count", xml).text()
  );
}

Doesn’t that look nicer ? I certainly look forward to something similar being included in ECMAScript.

CouchDB-OCaml

I spent two days upgrading from the previous CouchDB library to a new one. The change involved eliminating a lot of kinks that made writing code a bit awkward, as well as a massive transition from previous imperative style to a new monadic style. As a result, the code became shorter and more elegant.

As a simple example, this is a standard library module from the Ozone framework that was originally implemented on top of the old CouchDB library. It’s a library for atomically upholding an uniqueness constraint on a value :

  • module MyUnique = Unique(MyDatabase) initializes the unique constraint in the provided database.
  • MyUnique.get "Hello" gets an unique identifier bound to the Hello string, or uses an existing one if available, as an atomic operation. The software is then expected to work with the document tied to that unique identifier (to take advantage of CouchDB transactions).
  • MyUnique.get_if_exists "Hello" only returns the identifier if it exists.
  • MyUnique.remove "Hello" releases the value, so that subsequent calls will receive a different unique identifier. This is has the elegance of a sledgehammer.
  • MyUnique.remove_atomic "Hello" old_id releases the value only if it is still owned by old_id (if it is not, then it does nothing).

The original code for the module looked like this:

type id    = Id.t
type value = string

(* We're manually constructing a format that we will need to pass to
   every database function. Not elegant. *)
type json join = < id : Id.t > ;;
let fmt = json_of_join, Fmt.protect "Unique.join" join_of_json

module Make = functor (DB:CouchDB.CONNECTED) -> struct

  let get_if_exists value =
    (* Also, we need to turn every value into an identifier, because this is
       what the database expects... *)
    let id = Id.of_string value in
    match DB.get fmt id with None -> None | Some join -> Some (join # id)

  let get value =
    let id = Id.of_string value in
    (* Transactions are raw "apply this function to the existing data" hammers,
       even though "insert if not exists" is not a nail, it's a screw, and
       a common one too. *)
    DB.transaction fmt id begin function
      | None -> let newid = Id.gen () in
                newid, CouchDB.Put (object method id = newid end)
      | Some o -> o # id, CouchDB.Keep
    end

  let remove value =
    let id = Id.of_string value in
    (* Deletion is fairly common, too... *)
    DB.transaction fmt id (fun _ -> (), CouchDB.Delete)

  let remove_atomic value current_id =
    let id = Id.of_string value in
    DB.transaction fmt id begin function
      | None -> (), CouchDB.Keep
      | Some o -> if o # id = current_id then (), CouchDB.Delete else (), CouchDB.Keep
    end

end

The new version looks like this instead:

(* For the |>> operator *)
open Breathe.Include

type id    = Id.t
type value = string

(* Now, formats are represented as modules *)
module Join = Fmt.Make(struct
  let name = "Unique.Join.t"
  type json t = < id : Id.t >
end)

(* And there's a wrapper to perform the value-to-id conversion
   automatically *)
module Value = struct
  type t = value
  let to_id = Id.of_string
  let of_id = Id.to_string
end

module Make = functor (DB:Breathe.DATABASE) -> struct

  (* Instead of relying on low-level "use identifiers and formats on
     every call" primitives as was done previously, we can now
     define a table with its identifier and value types described by
     modules, so conversions happen automatically. *)
  module MyTable = Breathe.Table(DB)(Value)(Join)

  let id_of x = x # id

  let get_if_exists value =
    (* Using tables and a map operator makes this shorter. *)
    MyTable.get value |>> BatOption.map id_of

  let get value =
    (* The ensure primitive eliminiates the need for specific code
       to handle this case. *)
    let fresh = lazy (object method id = Id.gen () end) in
    MyTable.transaction value (MyTable.ensure fresh) |>> id_of

  let remove value =
    (* So does the remove primitive... *)
    MyTable.transaction value MyTable.remove |>> ignore

  let remove_atomic value current_id =
    (* And the remove_if primitive... *)
    let has_id uniq = uniq # id = current_id in
    MyTable.transaction value (MyTable.remove_if has_id) |>> ignore

end

Using a monadic style for the code also has a number of advantages that did not exist with the imperative:

  • There is now a cache for values retrieved from the database. This cache is carried around when the monad-based code is evaluated, so its lifetime and scope are closely guarded by the Breathe.execute function that actually runs everything. There is no risk of an element staying in cache for hours, as the cache is discarded after every HTTP request. It also means several caches can co-exist within the application without stepping on each other.
  • Database failures are now handled by the monad : as soon as an error is encountered, monad evaluation stops and Breathe.execute reports the error instead of the expected result. This lets write-only operations report errors and interrupt the flow even though they return unit without spamming exceptions around.
  • Individual operations can be collected together and batched with the bulk API. This is something that is theoretically possible given the architecture of the library, but not implemented yet.
  • Code that involves database access is now clearly indicated as such, which helps increase its visibility and the propagation of “this might access the database” hints across the software.

99 OCaml Problems

I find that when learning the basics of a new functional language, or introducing oneself to functional programming in general, the 99 Lisp Problems are a good start. You don’t have to run through all of them, but how fast you can solve them says a lot about your fluency in the language, and has many things to teach even to otherwise experienced programmers.

I’ve started committing solutions to those 99 problems in the Objective Caml language to GitHub ; the repository is:

https://github.com/VictorNicollet/99-Problems-OCaml

If you’re interested in starting OCaml programming, have read the manual, and are looking for exercises to get up to speed, consider spending a little time trying to come up with the solutions on your own, using my solutions as a reference. And if you’re an OCaml expert, feel free to propose corrections and improvements : this is what GitHub is for.

Other solutions for OCaml do exist, for instance from Christian Kissig, which is currently more complete than mine. Note that we don’t share the same philosophy as far as writing the code goes, so you can probably look at both sides of the issues. For example, on the “find the last element of a list” problem, Christian throws an exception if no last element exists, while I return None, because I hate exceptions.

Enjoy.

It’s Payback Time

I have another problem for you. The European Union has banknotes of values 500€, 200€, 100€, 50€, 20€, 10€ and 5€, and coins of values 2€, 1€, 0.5€, 0.2€, 0.1€, 0.05€, 0.02€ and 0.01€. As you might expect, when you need to pay a certain amount (say, 118€), you give the cashier a few bills and coins (1×100€ + 1×20€) and you get some change back (1×2€). For every amount, there exists a perfect transaction which minimizes the number of notes and coins that are exchanged. For 118€, that number is 3 (as shown above). For 5€, it’s 1 (give a single banknote). For 9€, it’s 2 (give a 10€, get 1€ back).

Here’s the question: what is the amount, smaller than 500€, which causes the highest number of notes and coins to be exchanged?

While this could be solved mathematically, I am going to go for a brute-force algorithm because it’s easier on the mind. The approach consists in exploring all possible transactions of size one (only one note or coin is exchanged) and remember what amounts were available, then construct all transactions of size two (by adding a coin or note to a transaction of size one), and so on until all new transactions end up being for amounts that have already been explored (there are 50001 possible amounts, so the algorithm will inevitably terminate, even if it takes a while).

First, I need a representation of all available coins and notes (in both positive and negative form, because in a transaction they can go either way). I will save some typing by writing some code that generates them:

let values =
  List.concat
    (List.map
       (fun v -> [ 5*v ; 2*v ; v ; -v ; -2*v ; -5*v ])
       [ 100_00 ; 10_00 ; 1_00 ; 10 ; 1 ])

This code turns every power of 10 between 10000 (100€) and 1 (0.01€) into a six-item list (for instance, 10 becomes 50, 20, 10, -10, -20, -50), then concatenates all these lists together. The resulting 30-element list contains all 15 notes and coins twice: once positive and once negative.

To represent a transaction (a node in my exploration algorithm) I will define a brand new type that contains the current total in the transaction, the change (a list of notes and coins), and the transaction size (this is actually equal to the length of the change list, but storing it is faster than having to compute it from scratch). The type looks like this:

type t = { total : int ; change : int list ; count : int }

Before I continue, a quick word about complexity: in my algorithm, I expect to do a lot of “has this node already been explored” queries, because this is what determines whether I need to stop processing. I also expect the list of explored nodes to grow to a size of 50001, and if I have to run such a query for every single node, I am looking at a minimum of one billion comparisons. This is starting to get a bit too large for my tastes (probably several minutes worth of computations), so I will look into an alternative storage method: sets.

Sets are optimized for quick “add this element to the set” and “is this element in the set?” operations, running in logarithmic time rather than linear time. This makes the above queries several orders of magnitude faster (expect a 50000-element set to behave about as fast as a 50-element list). In Objective Caml, sets are available as part of the standard library, and can be used by defining a module containing the type you wish to store in the set, then call Set.Make to create a specialized set module dedicated to your type:

module Node = struct
  type t = { total : int ; change : int list ; count : int }
  let compare node_1 node_2 = compare node_1.total node_2.total
end

module NodeSet = Set.Make(Node)

Nothing very complex here: my module defines a type named t (as expected by the set module) and a compare function that relies on the built-in compare function to compare the totals (because my actual query is “was a node with this total explored?”) so that nodes with equal totals but different transactions are still considered the same.

While I’m doing this, I will also add two other functions to my node module: a function which adds a coin to a node (this changes the total, prepends the coin to the transaction list, and increments the count), and a function which returns whether the node is valid (its total is between 0€ and 500€). I will also add an empty node from which the exploration will start:

module Node = struct

  type t = { total : int ; change : int list ; count : int }

  let compare node_1 node_2 = compare node_1.total node_2.total

  let add node coin = {
    total  = node.total + coin ;
    change = coin :: node.change ;
    count  = node.count + 1
  }

  let is_valid node = node.total >= 0 && node.total <= 500_00

  let empty = { total = 0 ; change = [] ; count = 0 }

end

Next, I use the set module to define a “node is not explored yet” function:

let is_unknown known node = not (NodeSet.mem node known)

Here NodeSet.mem element set is a function which returns true if the element is inside the set (“mem” means “member”). Based on this function, I can write the “explore one node” routine, which consists in adding all possible coin and note values to the node, eliminating any nodes that end up outside the 0€ – 500€ range and any nodes that have already been explored, and then returning those nodes. Elimination will use the built-in List.filter predicate list function, which keeps only the elements in the list for which the predicate function returns true. My predicates are “is not explored” and “is valid”:

let explore known node =
  let reached    = List.map (Node.add node) values in
  let valid      = List.filter Node.is_valid reached in
  let unexplored = List.filter (is_unknown known) valid in
  unexplored

The exploration process is fairly similar to what happened in the previous installment. An unexplored list is kept, and one item is taken from it and explored, which creates new unexplored items. Any items added to the list of unexplored items is also added to the set of known items to avoid duplicates. When the unexplored list runs out (everything has been explored), the set of known items is returned, because it contains all reachable nodes including the largest transaction. In order to make sure the smallest transaction is kept for every amount, the algorithm processes shorter transactions before longer transactions (so that the first transaction for any given total is always the shortest transaction for that total). This is done by appending newly found unexplored elements at the end of the unexplored list. The complete code is:

let rec process known unexplored =
  match unexplored with
    | [] -> explored
    | next :: rest ->
      let found = explore known next in
      let known = List.fold_left (fun known node -> NodeSet.add node known) known found in
      process known (rest @ found)

This is the hardest function of the bunch, and the most complex line is the one that uses List.fold_left to add several new nodes to the set of known nodes. As an interesting side note, the order of the parameters to NodeSet.add is not adapted to the List.fold_left function, which forces me to write an anonymous function that swaps the two arguments it receives.

Once this is written, it’s a simple matter of calling the function:

let explored = process NodeSet.empty [Node.empty]

And extracting the maximum using NodeSet.fold, the set equivalent of List.fold_left:

let maximum =
  NodeSet.fold
    (fun node best -> if node.Node.count > best.Node.count then node else best)
    explored Node.empty

Since the set is sorted in ascending order of node totals, I know that if several transactions have the maximum size, the one with the smallest total amount will be found first and kept as the best. And the result is 333.33€, for which the optimal transaction consists in giving 500€ and receiving 100€, 50€, 10€, 5€, 1€, 0.5€, 0.1€, 0.05€ and 0.02€ back (a total of ten coins and banknotes). The computation, when compiled as an optimized executable, takes 58 seconds on a computer that was average by early 2008 standards.

Initial Sequence

Let’s play another little game. I’ll give you a few integers, for instance 225, 318, 529 and 713. I need you to compute all numbers that can be computed by adding those integers together any number of times (for instance, 450 = 225 + 255, 543 = 225 + 318 …). Of course, there’s an infinite number of them, so I just want you to be able to provide me with, say, the 1000 smallest ones.

How can one solve this? There’s no simple formula for determining what number comes next, so it boils down to computing enough numbers to be certain that the 1000 smallest values have all been computed, and then sort the result to extract the first 1000.

Finding these numbers looks a lot like exploring a map or maze. From every number, there are four paths one can follow, which consist in adding one of the four initial integers to the current number. So, the paths from 225 lead to 450, 543, 754 and 938. Exploring a number means to write down all four destinations on the map, to be explored later. By definition, all paths lead to higher numbers, which provides a helpful property: if the 1000 smallest integers on the map are all explored, then no path from an unexplored integer can lead to a number smaller than one of those already explored (because that unexplored integer is greater than those 1000 explored ones, and so the path would lead to an even greater integer).

So, the algorithm consists in keeping a list of unexplored numbers and then repeating the following process a thousand times: pick the smallest integer, explore it (adding new numbers to the list of unexplored numbers) and add it to the list of explored items. In the end, the explored list contains 1000 items, sorted from least to greatest, with the guarantee that no unexplored integers can lead to smaller ones.

You might recognize this as a variation on Dijkstra’s Algorithm.

Writing this down in Objective Caml requires a preliminary decision: how are we going to extract the smallest number from the unexplored list? One way is to keep the list sorted at all times—this makes extraction easy (pick the first element) but insertion hard (you have to keep the list sorted). Conversely, one may keep the list in random order—this makes insertion easy (just prepend) but extraction hard (you have to find and remove the minimum). Since the latter is marginally harder than the former, I’ll go with the first option. Inserting an element into a sorted list is a textbook example of recursive function:

let rec insert sorted element =
  match sorted with
    | [] -> [element]
    | head :: tail -> if element < head then element :: sorted
                      else if element = head then sorted
                      else head :: insert tail element

The algorithm is fairly straightforward: if the list is empty, return a one-element list containing only the element ; if the first item in the list is greater than the element, then the element goes in first and the rest of the list follows ; if the element is equal to the first item in the list, then return the list as-is to avoid duplicated ; if the element is greater than the first item in the list, then insert recursively that element in the remainder of the list and prepend the original first element to the result.

Then, there’s the exploration function, which is recursive as well, and expects two arguments: the number of items left to explore, and the list of unexplored elements. The list of explored elements is returned. It is written as:

let values = [ 225; 318; 529; 713 ]

let rec explore n = function
  | [] -> []
  | current :: unexplored ->
    if n = 0 then [] else
      let new_values = List.map ((+) current) values in
      current :: explore (n-1) (List.fold_left insert unexplored new_values)

The exploration function extracts the first element of the unexplored list (if that is empty for some reason, it returns no elements, because there’s nothing to explore) and computes new_values by adding all the original values to the explored element. This uses the built-in List.map func list which calls func on every item of the list and places it in a new list that is then returned. Here, the function is (+) current, which is a function that adds “current” to its argument. So, if the explored element was 225, the returned list would contain 450, 543, 754 and 938.

The code then inserts those four values into the unexplored list by using List.fold_left (which I have already covered yesterday), and recursively calls explore (asking for one less element). It then prepends the current element to the returned list (because it’s smaller than all the elements to be explored next) and returns everything.

That’s all! To find the 20 smallest values, one writes:

let smallest_20 = explore 20 values

These are:

[225; 318; 450; 529; 543; 636; 675; 713; 754; 768; 847; 861; 900; 938; 954;
 979; 993; 1031; 1058; 1072]

Any questions? Ask away in the comments section!

Heads and Tails

Let’s play a little game. You have nine coins laid out as a 3×3 square in front of you. Three of them are tails while the other six are heads, in the following shape:

T T H
H T H
H H H

You are allowed to flip coins (heads becomes tails and tails becomes heads) but only in groups of three: all three coins on a line, column or diagonal can be flipped together. So, flipping the top line from the previous example would result in this:

H H T
H T H
H H H

Your objective is to determine whether this configuration can be flipped into an all-heads (or all-tails) configuration, and if it can, provide a possible flip sequence.

I’m going to find the solution using a short program written in Objective Caml. The first thing I’m going to do is translate as much information about the problem into data that the program can manipulate. The nine coins will be numbered from one to nine in reading order:

1 2 3
4 5 6
7 8 9

So, there are eight possible moves (three lines, three columns, two diagonals) and we can represent each as a list of three integers:

let moves = [
  [ 1 ; 2 ; 3 ] ; [ 4 ; 5 ; 6 ] ; [ 7 ; 8 ; 9 ] ;
  [ 1 ; 4 ; 7 ] ; [ 2 ; 5 ; 8 ] ; [ 3 ; 6 ; 9 ] ;
  [ 1 ; 5 ; 9 ] ; [ 3 ; 5 ; 7 ]
]

Each coin will have its state represented by a boolean: true for tails, false for heads. The current state of the board will be represented by key-value pairs, where the first element of the pair is the number of the coin and the second is its state (true, or false). A missing coin is treated as false for conciseness. So, the initial state looks like this:

[ 1, true ; 2, true ; 5, true ]

Be careful: semicolons separate items of a list, while commas separate items of a tuple (such as a key-value pair).

To get the current state of a coin, I can use the built-in Objective Caml function List.assoc key list which returns the value associated to a given key in a list of key-value pairs, and raises a Not_found exception if the key was not found. The function that returns the current state is then simply:

let state board cell =
  try List.assoc cell board with Not_found -> false

If you’re not familiar with Objective Caml notation function for functions, just remember that function arguments are not wrapped in parentheses like many other languages require. Instead, they simply appear after the function name. When you provide a function with all its argument, it is called and replaced by its return value. If you’re confused by the fact that the function above returns no value, remember that Objective Caml does not need a return keyword—it always returns whatever its body evaluates to (in this case, the value returned by List.assoc or, if it raises an exception, false).

I will also need the ability to flip a coin. This is done by prepending the new key-value pair to the existing list. It is not necessary to remove the previous binding of the key, because List.assoc stops as soon as it finds the key, so subsequent occurrences are simply ignored. The code is simply:

let flip board cell =
  (cell , not (state board cell)) :: board

The function simply reads the current value of the cell in the board, binds it to the cell (as a key) and prepends it to the list using item :: list notation. This does not change the value of board: instead, a new list is returned with the appropriate values inside, so that board can be reused elsewhere if necessary. This is a common occurrence in functional programming languages, and is extremely useful because values are guaranteed to be remain the same at all times.

One last thing we need before writing the actual algorithm is a function that determined whether the board has been solved. Since an all-false board can be trivially turned into an all-true board (simply flip all lines), I am just going to check whether all coins are true. This is done by using the List.for_all predicate list built-in, which returns true if and only if predicate returns true when called on every item in the list. The code is:

let is_solution board =
  List.for_all (state board) [ 1 ; 2 ; 3 ; 4 ; 5 ; 6 ; 7 ; 8 ; 9 ]

You might be confused about why state is only given one argument when it expects two. This is known as currying and is another common occurrence in functional programming languages. The basic idea is that if you give a two-argument function its first argument, then it becomes a one-argument function that expects its second argument. In this situation, state board becomes a function which expects a cell as an argument and returns the state (true or false) of that cell within the board. This precisely what we want our predicate to be.

The algorithm itself needs an initial board to work on (the one we are trying to solve) and a list of moves that it can try to solve the board. It then tries every combination of moves until it runs out or finds a solution (it makes no sense to apply a move twice, and the order in which they are applied is irrelevant, so for eight possible moves, there are 28 = 256 combinations: something that can be processed by a modern computer in the blink of an eye).

Actually trying every possible combination might seem a hard thing to do until you notice that it’s an inherently recursive process: if you have eight possible moves, you can try to apply the first one and try solve the resulting board with the other seven possible moves, or not apply the first one and try to solve the initial board with the other seven possible moves. Repeat this process until you have no moves left—at which point, if the board is all-true, you win, otherwise you lose.

let rec solution_exists board moves =
  match moves with
    | [] -> is_solution board
    | head :: tail -> solution_exists board tail ||
                      solution_exists (List.fold_left flip board head) tail

The rec keyword denotes that the function is recursive (which allows it to call itself). It examines the list of possible moves using match, which allows here two different possibilities: either the list is empty, in which case a solution exists if and only if the board is the solution ; or the list contains one element (the head) along with zero or more other elements (the tail, which is itself another list). In the latter case, the function recursively calls itself twice: once while ignoring the value of the current element, and once by applying it. The function returns true if either of the recursive calls return true.

Applying the current move relies on a built-in function List.fold_left func accumulator list which is perhaps one of the most useful functions in Objective Caml. It works by reading items from the list in order, calling func accumulator x for every item in the list and storing that result back into the accumulator, and finally returns the value of the accumulator. Let’s see an example with the first possible move, [1;2;3].

let result = List.fold_left flip board [1;2;3]

(* Is equivalent to: *)

let result =
  let board = flip board 1 in
  let board = flip board 2 in
  let board = flip board 3 in
  board

In short, this applies all flips determined by the current move and returns the resulting board. But, of course, it is much shorter than the version written by hand—this was made possible by the tendency of many functional languages to manipulate lists of values instead of code. List.fold_left is one of the many ways of applying a single piece of code over a list of values and obtaining some results.

Now that all of this is done, let’s look for our solution:

let find = solution_exists [ 1, true ; 2, true ; 5, true ] moves

The result is false, meaning that no solution was found. Let’s try with another board:

T T H
H H H
T H H

Or, in terms of code:

let find = solution_exists [ 1, true ; 2, true ; 7, true ] moves

This returns true, meaning that a solution exists. How do we adapt our code to return a solution instead of just true? First, we need a way to give a name to every move, which we are going to do by associating a bit of text to each in the list of possible moves. This looks like this:

let moves = [
  "L1", [ 1 ; 2 ; 3 ] ; "L2", [ 4 ; 5 ; 6 ] ; "L3", [ 7 ; 8 ; 9 ] ;
  "C1", [ 1 ; 4 ; 7 ] ; "C2", [ 2 ; 5 ; 8 ] ; "C3", [ 3 ; 6 ; 9 ] ;
  "D1", [ 1 ; 5 ; 9 ] ; "D2", [ 3 ; 5 ; 7 ]
] 

This means that the solving function also needs to be rewritten to take this into account. Instead of returning true or false, we are going to use another Objective Caml built-in type which allows one to either return a value, or return nothing—we return the solution if it exists (Some ["C1";"D2"]) and nothing if there was no solution (None). The code becomes this:

let rec solve board moves =
  match moves with
    | [] -> if is_solution board then Some [] else None
    | (name, move) :: tail ->
      match solve (List.fold_left flip board move) tail with
        | Some solution -> Some (name :: solution)
        | None -> solve board tail

This is a bit more complex, but the recursive algorithm is the same: if there are no moves left, then either the board is solved (return a zero-move solution Some []) or it isn’t (return an absence of solution None. If there’s a possible move, remember its name, apply it and check whether a solution was returned: if there was one (Some solution), then just prepend the name of the move to the solution and return it (Some (name::solution)) ; if trying the move did not allow for a solution, then try solving the board without that move and return whatever solution or absence of solution this results in.

Applying this to the original problem returns None as expected, while applying it to the second problem returns Some ["L1";"L2";"C2";"D1"] — a valid solution indeed.

The complete (and quite short!) code is:

let moves = [
  "L1", [ 1 ; 2 ; 3 ] ; "L2", [ 4 ; 5 ; 6 ] ; "L3", [ 7 ; 8 ; 9 ] ;
  "C1", [ 1 ; 4 ; 7 ] ; "C2", [ 2 ; 5 ; 8 ] ; "C3", [ 3 ; 6 ; 9 ] ;
  "D1", [ 1 ; 5 ; 9 ] ; "D2", [ 3 ; 5 ; 7 ]
] ;;

let state board cell =
  try List.assoc cell board with Not_found -> false

let flip board cell =
  (cell , not (state board cell)) :: board

let is_solution board =
  List.for_all (state board) [ 1 ; 2 ; 3 ; 4 ; 5 ; 6 ; 7 ; 8 ; 9 ]

let rec solve board moves =
  match moves with
    | [] -> if is_solution board then Some [] else None
    | (name, move) :: tail ->
      match solve (List.fold_left flip board move) tail with
        | Some solution -> Some (name :: solution)
        | None -> solve board tail

let find = solve [ 1, true ; 2, true ; 7, true ] moves

Any questions? Let me know in the comments!

Do Variant Types in OCaml Suck?

I recently stumbled once more on Tomasz Wegrzanowski’s blog, on one of the early articles he wrote in 2006 about Objective Caml — unequivocally titled Variant Types in OCaml Suck. Hence the title of this article.

Objective Caml polymorphic variants have the benefit of being created on the fly: you just write ` kitten and you have successfully created a value of type [> `kitten] (do note that you have also created a singleton type out of nothing — this can be quite useful in some cases). Of course, the ability to define types on the fly everywhere puts a strain on the type inference algorithm, who must then determine whether you are mistaken or not.

Type Inference : Damas-Milner

Type inference in ML language strives to determine the type of every value and variable in the code. It does so by giving every variable a completely general type, and then examining any constraints that apply to progressively turn it into the most general type that satisfies all constraints. Let’s try a quick example:

  • When you write fun x -> fst x + snd x, the type of x is initially 'x.
  • The type of fst is 'a * 'b -> 'a, which means that the type of x must have the shape 'a * 'b (because it’s provided as an argument to fst). So, the constraint implies that 'x = 'xa * 'xb (and that fst x is of type 'xa).
  • The type of operator + is int -> int -> int, from which the type of fst x is constrained to be 'xa = int and thus 'x = int * 'xb.
  • The type of snd is 'a * 'b -> 'b, but since 'b = int in the present context, this means that its actual type is 'a * int -> int and thus 'x = int * int

So, by applying constraints deduced from the code, the type inference algorithm computes the type of variable x. Applying a type constraint is known as unification: two types are determined to be equal, so they are superimposed on top of one another in order to construct a new unified type. This happened in the last step above: x had been determined to be of type int * 'b and this type was unified with type 'a * int. Unification is a simple pattern-matching exercise: one explores the two types as if they were trees, and…

  • If identical nodes are encountered (such as a _ * _ 2-tuple node or a leaf-less int basic type node), the unification proceeds recursively.
  • If a type variable (such as 'a or 'b) is encountered, it is declared equal to the corresponding element in the other type.
  • If two different nodes are encountered (such as a _ * _ constraint being unified with an int variable), an error happens.

Polymorphic Variant Types

Polymorphic variant types are lists of possible constructors. For instance, [`kitten | `walrus] represents a type that contains the constructors `kitten and `walrus.

When you write `kitten, the resulting type is [> `kitten]: an open variant type that can be unified with other variant types. Consider, for instance, the array [| `kitten ; `walrus |]: it is of type 'a array where 'a can be unified with both [> `kitten] and [> `walrus], which results in [> `kitten | `walrus].

Now, let’s consider the reverse operation: pattern matching. You write a simple match animal with `kitten -> "meow" | `walrus -> "bukkit": the type of `animal will be computed as [< `kitten | `walrus].

The difference between [< ..], [..] and [> ..] is obvious in terms of unification:

  • [< `a | `b | `c] can only be unified with variant types that contain less constructors than itself. So, it can be unified with [ `a | `b ] (which yields [ `a | `b ]) but not [ `a | `d ]. The reason is quite plain: if you were allowed to introduce `d into the variant type, then you could run the above match with animal = `owl and break the code. In short, [< ..] means “any others are forbidden”.
  • [> `a | `b | `c ] can only be unified with variant types that contain more constructors than itself (including other open types). So, it could be unified with [> `a | `d] (which yields [> `a | `b | `c | `d]) but not with [ `a | `b ] (which does not allow `c). In short, [> ..] means “these are allowed”.
  • [`a | `b] (a closed type) is both [< `a | `b] and [> `a | `b] at the same time. It can only be unified with variant types that allow those two but no others, and cannot be unified with any closed types other than itself.

This works, but it’s not perfect.

One major issue is the fact that every variable has a single type. So, suppose you want to transform `b into `c but leave any other constructors untouched. You would then write function `b -> `c | x -> x, but that would be a mistake: being present in the pattern matching means that x is of type [> `b] (it can accept any value, including `b) and being returned means it is unified with the type [> `c] of the other possible return value. So, the type of this function is actually ([> `b | `c] as 'a) -> 'a: even though you replaced every `b with a `c, the type inference insists that the result type can still contain a `b… In fact, there is no way to write such a function because there is no way to express the type of that function in Objective Caml.

Type Coercion

Another issue is that sometimes, you need unification between two different closed types. Consider this short example:

let string_of_vehicle : [`car | `train | `bike]       -> string = (* ... *)
let is_heavy       : [`car | `bike | `cake | `anvil ] -> bool   = (* ... *)

List.map (fun x -> if is_heavy x then string_of_vehicle x else "") [ `car ; `bike ]

This code should work, because I’m working on a car and a bike, both of which can be passed safely to both string_of_vehicle and is_heavy. However, due to external factors, the types of string_of_vehicle and is_heavy are closed and x has to unify with both. In such a situation, the solution in Objective Caml is to allow the user to make the conversion explicit by use of type coercion. In this example, it would look like this:

List.map (fun x ->
  if is_heavy (x :> [`car | `bike | `cake | `anvil ])
  then string_of_vehicle (x :> [`car | `train | `bike])
  else ""
) [ `car ; `bike ]

Coercion works by allowing a [<..] to be treated temporarily as a [..] — the solution above works by using two coercions to unify x as being of type [< `car | `bike], which is compatible with both types that it is coerced to. This is, for all purposes, merely an explicit way to convert a value from a type to a supertype (“any square is a rectangle”) that is not otherwise possible in the unification-based world of Objective Caml types — unification can only work with type equality, not with types being supertypes of other types.

Supertypes, and coercion, are intricately tied to loss of information. When you convert from [<`car | `bike] to [`car | `train | `bike], you are discarding the information that your value can not be a `train. While this loss of information might seem innocuous, it does prevent you from passing that same [<`car | `bike] for a [`car | `bike | `cake | `anvil], because the latter does not allow `train and you just discarded the information that `train cannot happen…

Since loss of information can be deadly to the soundness of a type inference algorithm, the Objective Caml design decision was that all loss of information (converting a type to a supertype) should happen explicitly (by means of a coercion).

Of course, coercion is only usually required if you have closed types in your program. Try avoiding them as much as you can, and you will find that you don’t actually need coercions all that often.

The Original Problem

Back to Tomasz Wegrzanowski’s issue: he was writing a compiler that needed to manipulate both scalars and vectors. So, he defined a value type:

type value = [`Scalar of float | `Vector of float * float * float]

He also defined specific types in order to ensure type-safety of some operations:

type scalar = [`Scalar of float ]
type vector = [`Vector of float * float * float]

type operation = [ `Muliply of scalar * scalar | `DotProduct of vector * vector ]

What he wanted to achieve was to extract all the values available within an operation:

let values : operation -> value list = function
| `Multiply (a,b) ->  [a;b]
| `DotProduct (a,b) -> [a;b]

Of course, this function is discarding precious type information about the values, which requires a type cast. The naive solution would be to individually type-cast a and b throughout the function, but this would be quite tedious. Instead, let’s strike at the root by coercing the function argument itself: by discarding information before the pattern matching occurs, we allow the code to compile with a single coercion. So, what do we coerce the argument to?

It’s not easy to tell. I guess I could create a copy of the operation type and replace all types with value, but that would be quite tedious. Instead, let’s rely on a simple property : when you need to coerce a value in order to pass it to a function, you need to coerce it to the type of that function argument. And the type of the function argument can be obtained through type-inference. Simply write this and compile it:

let values : 'a -> value list = function
| `Multiply (a,b) ->  [a;b]
| `DotProduct (a,b) -> [a;b]

let _ = values ()

The compiler determines the type of 'a through unification, knows that it’s not unit, and generates an error that describes the actual expected type. We can copy-paste it from the compiler output directly!

type general_operation = [ `Multiply of value * value | `DotProduct of value * value ]

let values x = match (a : operation :> general_operation) = with
| `Multiply (a,b) ->  [a;b]
| `DotProduct (a,b) -> [a;b]

let _ = values ()

Should the type operation change, an error will immediately happen in the definition of values (as the coercion from operation to general_operation would fail), which provides the new type that should be pasted on top of general_operation.

Exception Avoidance

From what I gather, many significant users of Objective Caml are moving away from exceptions, opting to rely on return types instead. To the more traditional C#/Java/C++ worlds, this must certainly sound as a heresy — exceptional situations should be handled using exceptions in order to avoid mudding up the control flow. Right?

Wrong.

Exceptions are a powerful tool that lets the author of a function say «heck, no way I can do this, let’s tell the caller» while letting the caller of that function say «I don’t care about your problems, tell my caller» and so the exception, once thrown, bubbles up through the hierarchy of function calls until it reaches a point where it can be handled. Which, in the vast majority of cases, is in some sort of catch-all handler that basically amounts to «drat, it didn’t work, let’s do something else»

As the writer of the calling function, this makes your life easier: if any errors happen in the functions you call, they will nicely bubble up, so you only have to care about situations where everything works fine. As a human being, you are passing the responsibility for handling that failure to someone else on your team who will happen to write the code that calls your code. And a major problem with responsibilities being passed around is that they end up on the floor.

This is further compounded by the fact that the presence of exception handlers is not adequately verified by compilers, and you end up with code in a mess that can only be solved in two ways:

  • Read through all the code again, remember what functions might throw which exceptions, and add any missing handlers to treat them on the spot. This is hard.
  • Add a generic catch-all handler that silences errors but doesn’t really react to exceptions in an acceptable manner.

Maybe your team does handle all exceptions appropriately in the places where they are needed. That would be quite surprising (where do you work?) but let’s assume for a moment that this is the case.

What happens when a change of specifications mandates that a new kind exception needs to be thrown? Suppose, for instance, that a given database resource may be now locked by an user, making all attempts to access that resource (by other users) fail. How do you change your code to handle this new requirement?

Adding a new exception is perhaps one of the single most painful kind of code change in modern programming, because it is woefully ill-supported by both languages (by design) and tools. It would effectively require the «functional specs guy» to think of all the ways an user might trigger that lock condition and how the software should react, then provide the developers with a detailed plan of action that would let them jump into dozens of different code locations to add the appropriate try-catch block. If one such location goes unnoticed, it will be filed as a bug when a tester or customer noticed («I tried to save my document, but it said document locked and I lost my data!»).

Consider the following example:

let get_age (person : Person.t) =
  if Person.is_dead then raise PersonIsDead ;
  Date.subtract Date.today (Person.birth_date person)

I just added the exception-throwing line — how can I easily detect locations that are affected by this change? There is no way. So, what is the alternative?

Eliminating Exceptions

Objective Caml allows the use of polymorphic variants. You can create a tag on the fly and attach it to a value that you’re returning (the tag is called a polymorphic variant constructor). You can also return tags without attached values, which is helpful when signaling error conditions. Here is how I would rewrite the above:

let get_age person =
  if Person.is_dead then `person_is_dead
  else `age (Date.subtract Date.today (Person.birth_date person))

I used the tag `age to represent a valid age, and `person_is_dead to represent the fact that a dead person has no age. Looking at the compiler output confirms that my function was indeed identified as returning a tagged value, and all possible tags were described by the compiler in the function signature:

val get_age : Person.t -> [ `person_is_dead | `age of int ]

Since the return type of the function changed (after all, it used to be int), all the functions that call it now cause a compiler error, which makes them quite easy to find and correct. Most of them will usually correspond to what the «functional specifications guy» wrote in his document, but the compiler will also find those that were overlooked. For instance, there’s this line that displays the age of a person:

 printf "%s %s (age %d)"
  (Person.firstname)
  (Person.lastname)
  (get_age person)

Seeing this code, but not seeing anything about it in the specification document, the programmer asks the stakeholder in the next scrum meeting, and the latter decides that showing deceased would be enough. So, the code becomes:

printf "%s %s (%s)"
  (Person.firstname person)
  (Person.lastname person)
  (match get_age person with `age a -> sprintf "age %d" a | `person_is_dead -> "deceased")

Pattern matching is a powerful tool from the ML family of languages. It behaves like a switch, but with several added benefits:

  • It extracts any values attached to the tags, and the compiler checks the presence and types of those values. So, if I tried to access the a value from the `person_is_dead tag, the compiler would complain that no such value exists — the type signature of get_age specifies that no value is attached to `person_is_dead.
  • It verifies that all possible tags are handled by the construct. So, if I had to hide the age of all female users, I would return `is_woman and the compiler would point out that said tag is not handled in the above code — an interesting tool when adding more exceptional cases.

Preventing Exceptions

But that is not all. Having the compiler as an ally also helps the programmer prevent exceptions in the first place. For instance, suppose that, on the online profile page of every user, I have a tab that shows a countdown to the next birthday of that user. The code looks like this:

let show_user_profile person url = (* ... *)

let show_birthday_countdown person url =
  printf "%d minutes left to age %d"
    (get_minutes_to_birthday person)
    (get_age person)

let profile_page person url =
  (* This renders both tabs, then checks which one is 
   * active based on url and renders its content 
   *)
  Tabs.render url
    [ "profile",   show_user_profile person ;
      "countdown", show_birthday_countdown person ]

Obviously, a lot of functions called to render the contents of that tab will have to handle exceptions when the user is deceased — but the sane thing to do would be to hide the tab in the first place. If you don’t try to render the tab contents, no errors will happen while rendering it… The problem, of course, is that if we check whether the user is alive outside the tab contents (in order to decide if the tab is present) we still have to check whether the user is alive inside the tab contents, even though we know that the tab contents would not have been rendered if this were not the case:

let show_user_profile person url = (* ... *)

let show_birthday_countdown person url =
  printf "%d minutes left to age %d"
    (get_minutes_to_birthday person)
    (match get_age person with `age a -> a | `person_is_dead -> (* Should never happen! *))

let profile_page person url =
  (* This renders all available tabs, then checks which one is 
   * active based on url and renders its content (default is the first one) 
   *)
  Tabs.render url
    [ "profile", show_user_profile person ]
    @ (if Person.is_dead person then []
       else [ "countdown", show_birthday_countdown person ])

Above, an if-then-else makes sure that show_birthday_countdown will only ever be called if the person is not dead (otherwise the tab is not available and the profile tab is shown instead). However, the show_birthday_countdown function does not know that, which means that pattern matching must still be used even though the error is known not to happen — and I hate “should never happen” comments in code. Should another programmer reuse the birthday countdown in another context where the unwritten assumption that the person is alive is not verified, the compiler will not be able to detect it, and the “should never happen” will happen. Constructs like the above should be avoided like the plague. Instead, the unwritten assumption should be written into a type.

Let’s build a different type that is known to be a living person:

module AlivePerson : sig
  type t = private Person.t
  val is_alive : Person.t -> t option
  val get_age  : t -> int
end = struct

  type t = Person.t

  let is_alive person =
    if Person.is_dead person then None
    else Some person

  let get_age person =
    Date.subtract Date.today (Person.birth_date person)

end

How does this work? First, we define a private type — this means that within the definition of the AlivePerson module, AlivePerson.t and Person.t are the same type, but outside, they are different and incompatible types. Being incompatible means that we need to define a function that creates an AlivePerson.t from a Person.t — this is what AlivePerson.is_alive does : if the person is alive, it is returned with type AlivePerson.t, and otherwise no value is returned. This proves that only a Person.t who is not dead can be turned into an AlivePerson.t. Finally, we define the AlivePerson.get_age function which, on living persons, always returns the age without checking for them being alive.

Next up, we must re-define the original get-age function, because we don’t want to repeat ourselves: only write the age calculation code once! The good news is that the AlivePerson module provides everything we need to do this:

let get_age person =
  match AlivePerson.is_alive person with
    | None       -> `person_is_dead
    | Some alive -> `age (AlivePerson.get_age alive)

The function signature remains the same, so it’s a safe bet to say that the behavior did not change (and unit tests should make sure that it did not). Then, we can finally rewrite the offending tab-rendering code so that the countdown tab expects an alive person as an argument :

let show_birthday_countdown alive url =
  printf "%d minutes left to age %d"
    (get_minutes_to_birthday (alive :> Person.t))
    (AlivePerson.get_age alive)

let profile_page person url =
  Tabs.render url
    [ "profile", show_user_profile person ]
    @ (match AlivePerson.is_alive person with
       | None       -> []
       | Some alive -> [ "countdown", show_birthday_countdown alive ])

No compiler error occurs, and there is no more “should never happen” case in the first function. Also, should that function be re-used in a context where the assumption about the person being alive is not available, the compiler will issue an error (this value has type Profile.t but a value was expected of type AliveProfile.t) because type inference has determined (based on the call to AlivePerson.get_age) that alive is of type AlivePerson.t! Your unwritten assumption has been detected and checked by the compiler.

In Conclusion

In this article, I demonstrated how to turn exceptions into compiler-checked return values, this turning the compiler into a valuable ally that detects any locations where the addition of a new error might require altering existing functionality. I also illustrated how this enables the programmer to pre-empt the possibility of exceptions by injecting a compiler-verified proof that «this will not throw an exception» into a new module. The consequence of these coding practices is fewer mishandled exceptional situations, the ability to detect all consequences of a new kind of error, and a clean documentation of both error types and assumptions about what errors might or might not happen in a given piece of code.

Which, I believe, leads to better code and happier developers. What do you think?

Avoid Side Effects

Perhaps one of the single most useful aspects of functional programming is the absence of side-effects. In a pure functional language, there is no assignment — once created, you cannot change the members of an object or the value of a variable. All you can do is create new values based on old values. Advocates of these so called immutable values argue that there are many benefits related to what you can do with them: you can keep around both the new version and the old version (implementing an “undo” feature of sorts), often at a reduced memory cost due to sharing of sub-elements between the two versions.

While I agree with these, I believe there’s something far more interesting about immutable values — what you cannot do with them, and the consequences this has on feeling secure about your code.

In my current project, I use a coding style that absolutely forbids any form of side-effects with the exception of 1° a clearly delimited set of create/update functions that affect the database, 2° any side-effects that affect local variables and 3° during initialization. These are of course exceptions — while allowed, they are avoided in the vast majority of the code.

Reordering code

One simple consequence: the order in which calls are made is only constrained by things that the compiler can detect — the function that uses x is called after the function that returns x — instead of subtle «open must be called before send» errors, simply because there’s no way for «open» to affect «send» unless the former returns a value used by the latter. So, I can rearrange lines freely because the compiler will warn me when I make a mistake.

Protecting against invalid states

A corrolary of the above is that it becomes possible to design your types so that only valid states can be represented. For instance, consider a list that must contain at least one element — in a mutable context, there is no way to represent this within the type system because I could always write this:

for (i = 0 ; i < 10 ; i++)
  list.pop();

There is no way here for the pop function to indicate that it might cause a problem, because it returns nothing — the only way out is an exception, and this happens outside the type system in most languages. On the other hand, if we keep the original list unmodified and return the new list, this opens the opportunity for indicating whether additional pops are available:

type 'a one_or_more =
  | One of 'a
  | More of 'a two_or_more

and 'a two_or_more = 'a * 'a one_or_more

let pop : 'a two_or_more -> 'a one_or_more = function (_,rest) -> rest

By design, the pop function will never attempt to pop from a one-element list because it can only be called on a two_or_more value, and it returns a value which may or may not be a two_or_more — the code needs to check this, and the compiler will detect if you forget it:

let pop_n n list =
  if n = 0 then list else pop_n (n - 1) (pop list)

Here, the type of the list variable is inferred to be two_or_more (because it’s passed as an argument to pop) which means that pop list (of type one_or_more) is not a valid argument to pop_n. An appropriate solution (popping up to n, if available) takes this into account:

let pop_n n = function
  | (One _) as keep -> keep
  | more -> if n = 0 then more else pop_n (n - 1) (pop more)

Let’s summarize what happened here: I used a different representation for one-element lists and more-than-one-element lists, which let me selectively define a pop operation only for more-than-one-element lists, and this allowed the compiler to detect when the «must contain at least one element» constraint might be violated. Have fun achieving this in your non-functional language of choice! Using a different representation in this way was made possible by the fact that pop returns a new list instead of altering its argument — so the new list can be of a different type than the old one.

This is a fundamental principle in compiled functional languages: if a given state is not valid, design your types so that it may not be represented. When this is done correctly, not only does it become harder to write code that tries to create an invalid state, but when that code is written, the compiler rejects it.

Memoization

If you are certain that a given function is pure but involves computations that you would rather not repeat too often (such as database reads), you can resort to memoization: wrap the function in a piece of code that memorizes the result of every call. In my current project, this is as simple as:

let get_pic_url = Util.memoize Picture.get_url

From then on, asking ten times for the URL of a given picture will result in a single database request. Had the function involved a side-effect, memoization would have changed the program behavior: knowledge that the function is pure lets me apply memoization without any risk of creating a bug.

Retry until successful

Yet another situation is when dealing with CouchDB transactions, which involve the following process:

  1. Fetch current version of the document from the database
  2. Modify the document
  3. Write the document back to the database
  4. If a collision happened, repeat from 1.

In short, the application must repeat steps 1-2-3 until a write succeeds. Correctness of these algorithms is difficult to test because development servers seldom have enough active users to actually cause conflicts (and when conflicts do happen, they are hard to trace). By deciding that steps 1-2-3 are handled by pure functional code, I have the guarantee that the loop can be executed any number of times and yield the expected result. Right now, a transaction looks like this:

let accept id = DB.transaction id
  begin function
  | None -> (* No object in database *) DB.Keep
  | Some post ->
    if post # accepted then
      (* Post is already accepted *) DB.Keep
    else
      (* Accept post *)
      DB.Put (object
        method accepted = true
        method author   = post # author
        method text     = post # text
      end)
  end

By applying a “never use side-effects” convention, I know without even looking at the function contents that there are no side-effects inside.

These four examples are original in that they don’t try to showcase the power of immutable values and how they can be used. Instead, they simply illustrate how a program without mutable values has interesting properties that make refactoring easier, help the compiler detect many more errors, and allows the application of patterns that can only be safely applied in the absence of side-effects. Like the monks of old, I write my software without the mundane facilities of mutable values, but I derive significant benefits out of it.



1170 feed subscribers
(readers who polled a feed this week)