A few weeks back I built a simple console application with my 9yo using F# for practicing the times tables.

So that he could share the game with his friends at school I followed Attwood’s law and created a JavaScript version.

However I wasn’t particularly satisfied with this JavaScript version. Among other things, in the F# version I was able to easily do a timed count down before the game starts:
for message in ["Get Ready";"3";"2";"1";"Go"] do
printfn "%s" message
Thread.Sleep 1000
To do the same thing in JavaScript would require nesting calls to Window setTimeout, which is neither readable or maintainable. I decided to skip this, but in doing so lost some of the aesthetic of the original implementation.
With the recent release of the Pit project that compiles F# code to JavaScript, just over a week ago, I thought I’d revisit the game. Using F# computation expressions (a mechanism used to implement F# Async workflows), it was possible to implement the get ready sequence relatively cleanly:
delay {
root.InnerHTML <- "3"
do! Delay 1000
root.InnerHTML <- "2"
do! Delay 1000
root.InnerHTML <- "1"
do! Delay 1000
root.InnerHTML <- "Go"
do! Delay 1000
play total root
}
This generates the following quite heavily nested JavaScript:
SevenSixes.App.countdown = function (total) {
return function (root) {
return function (builder) {
return builder.Delay(function () {
root.innerHTML = "3";
return builder.Bind({
Item1: new SevenSixes.App.Delay.Delay(1000),
Item2: function (_arg5) {
root.innerHTML = "2";
return builder.Bind({
Item1: new SevenSixes.App.Delay.Delay(1000),
Item2: function (_arg4) {
root.innerHTML = "1";
return builder.Bind({
Item1: new SevenSixes.App.Delay.Delay(1000),
Item2: function (_arg3) {
root.innerHTML = "Go";
return builder.Bind({
Item1: new SevenSixes.App.Delay.Delay(1000),
Item2: function (_arg2) {
SevenSixes.App.play(total)(root);
return builder.Zero();
}
});
}
});
}
});
}
});
});
} (SevenSixes.App.get_delay);
};
};
Basically code gets mapped to a Bind method that runs a function after a specified timeout:
type Delay = Delay of int
type DelayBuilder() =
[<Js>] member x.Bind(Delay t, f:unit->unit) = window.SetTimeout(f, t)
[<Js>] member x.Return(t) = fun () -> t
[<Js>] member x.Delay(f:unit->'a) = f()
[<Js>] member x.Zero () = ()
let [<Js>] delay = DelayBuilder()
So without further ado, click below to play the game:

As far as I’ve tested the game seems to run on IE9, Mozilla and Chrome browsers, along with Blackberry, iPhone and Windows Phone 7 (Mango).
P.S. If you have an Android phone, please let me know if the game works for you.
Resources