Phillip Trelford's Array

POKE 36879,255

Sixes and Sevens

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

TimesTable

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

Times Tables Java Script

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:

12x3

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

Compiling F# to JavaScript with Pit

JavaScript is the old new thing.

First shipped in Netscape Navigator 2.0 in 1995, JavaScript is supported by most popular browsers. Because of this, JavaScript has become an intermediate language for other languages to target. CoffeeScript (late 2009), ClojureScript (2011) and Dart (2011) are relatively recent examples of languages targeting JavaScript.

The F# programming language makes it relatively easy to transform F# code to another form through a feature called code quotations. Simply annotating F# code with the ReflectedDefinition attribute makes it available as a quotation at runtime. Aliasing this attribute to say JavaScript or Js allows you to declaratively specify which code to generate.

There are a number of tools providing support for targeting JavaScript from F#. FSharp.WebTools by Tomas Petricek, among other things, targeted JavasScript from F# way back in 2007. FSharp.JavaScript simply converts F# to JavaScript. While WebSharper is a commercial product with advanced features.

Enter Pit

Pit is a new F# to JavaScript compiler community project released just a few days ago. And it’s early days at version 0.1, but it’s already very useable.

Check out the samples and documentation.

Download and run the setup project, and within seconds a new Pit application project type is added to Visual Studio. This produces a simple hello world app:

let [<Js>] main() =        
    alert("Hello World!!!")

On top of the usual alert box debugging for JavaScript, Pit lets you set breakpoints!

So far I’ve found Pit very intuitive to develop against.

This was my first app in Pit, a simple calculator:

PitCalc

Which runs happily on iPhone, Blackberry and WP7! All from 80 odd lines of code:

namespace Pit

open Pit
open Pit.Dom

module Calculator =

    let [<Js>] (?) (el:DomElement) name =
        el.GetAttribute(name)
    let [<Js>] (?<-) (el:DomElement) name value =
        el.SetAttribute(name,value)
    type DomAttribute = { Name:string; Value:obj }
    let [<Js>] (@=) name (value:'a) =
        { Name=name; Value=box value }
    let [<Js>] tag name (attributes:DomAttribute list) =
        let el = document.CreateElement(name)
        for a in attributes do el.SetAttribute(a.Name,a.Value.ToString())
        el

    let [<Js>] display =
        tag "input" ["type"@="text";"value"@=0;"style"@="text-align:right"]
    let [<Js>] mutable operation : (int -> int) option = None
    let [<Js>] mutable append = false

    let [<Js>] clear () =
        display?value <- "0"
        append <- false

    let [<Js>] enter s =
        s, fun () ->
            let value = display?value
            if append then display?value <- value + s
            else display?value <- s; append <- true

    let [<Js>] calculate () =
        let value = int display?value
        operation |> Option.iter (fun op ->
            let newValue = op value
            display?value <- newValue.ToString() 
        )
        operation <- None
        append <- false

    let [<Js>] operator op () =
        calculate ()
        let value = display?value |> int
        operation <- op value |> Some
   
    let [<Js>] add = (+) 
    let [<Js>] sub = (-)
    let [<Js>] mul = (*)
    let [<Js>] div = (/)

    let [<Js>] buttons =
        [[enter "7"; enter "8"; enter "9"; "/", operator div]
         [enter "4"; enter "5"; enter "6"; "*", operator mul]
         [enter "1"; enter "2"; enter "3"; "-", operator sub]
         ["C", clear; enter "0"; "=", calculate; "+", operator add]]

    [<DomEntryPoint>]
    let [<Js>] main() =
        let table = (tag "table" [] |> DomTable.Of)  
        let tr = tag "tr" []
        let td = tag "td" ["colspan"@="4"]
        td.AppendChild display
        tr.AppendChild td 
        table.AppendChild tr 
        buttons |> List.iter (fun row ->
            let tr = tag "tr" [] 
            row |> List.iter (fun (text,action) ->
                let td = tag "td" []
                let input = 
                    tag "input" 
                        ["type"@="button";"value"@=text;"style"@="width:32px"]
                input |> Event.click |> Event.add (fun _ -> action ())
                td.AppendChild input
                tr.AppendChild td
            )
            table.AppendChild tr
        )
        let div = document.GetElementById "calculator"
        div.AppendChild table

Operator Overload


For convenience the code above makes use of some F# operator overloading.

The dynamic lookup operator is overloaded for getting and setting attributes:

let [<Js>] (?) (el:DomElement) name =
    el.GetAttribute(name)
let [<Js>] (?<-) (el:DomElement) name value =
    el.SetAttribute(name,value)

This can be used to get and set the value of the input tag of the calculator’s display:

let value = display?value
display?value <- value

The HTML input element for the calculator’s display is created dynamically:

let [<Js>] display =
    tag "input" ["type"@="text";"value"@=0;"style"@="text-align:right"]

The tag function creates a HTML element from a specified name and a list of attributes:

let [<Js>] tag name (attributes:DomAttribute list) =
    let el = document.CreateElement(name)
    for a in attributes do el.SetAttribute(a.Name,a.Value.ToString())
    el

And a custom operator (@=) creates an attribute record:

type DomAttribute = { Name:string; Value:obj }
let [<Js>] (@=) name (value:'a) =
    { Name=name; Value=box value }

JavaScript as an Intermediate Language?


Q: Why use an expressive statically typed high-level language like F# over plain JavaScript?

A: This will probably come down to a mixture of problem context and personal preference.

Certain problems, like parsing, are easy in F#. Alexander Stojanovic sums it up well:

You must understand a (programming) language as a way of seeing. Languages don't just express thoughts, they shape them.

Validation code written in F# can be run on both the client, via JavaScript, and the server, via .Net IL code.

An area of particular interest for the F# community is generating live samples from code snippets. The F# Snippets site let you target your snippet against Silverlight via tryfsharp.org or an echo of the console via tryfs.net. For example here a game of Missile Command or Tetris running as scripts.

Pit’s build libraries offer the potential to run F# snippets via JavaScript.

Resources


Times Tables

As part of his maths homework my eldest has to practice the times table under the clock to improve times. This looked like a good opportunity to combine maths homework with some programming. Over about half an hour and 43 lines of code we came up with a Times Table game as an F# console application.

TimesTable

First off some helper functions:

open System

let readln () = Console.ReadLine()
let readkey () = Console.ReadKey()
let tryParseInt = Int32.TryParse 
let using color f = 
    Console.ForegroundColor <- color
    f ()
    Console.ResetColor ()
let red = ConsoleColor.Red
let green = ConsoleColor.Green
let beep () = Console.Beep ()
let wait (n:int) = Threading.Thread.Sleep n
let now () = DateTime.Now
let rand = Random()

Then a function to play a single game:

let play times = 
    for message in ["Get Ready";"3";"2";"1";"Go"] do 
        printfn "%s" message
:        wait 1000
    let begun = now ()
    [1..times] |> Seq.sumBy (fun i ->
        let a, b = rand.Next 13, rand.Next 13
        printf "%d x %d = " a b
        let entered = readln ()
        match tryParseInt entered with
        | true, answer when answer = a * b -> 
            using green (fun () -> printfn "Correct")
            1
        | _, _ ->
            beep ()
            using red (fun () -> printfn "%d x %d = %d" a b (a*b))
            0
    ) |> (fun score ->
        let taken = (now() - begun).ToString("c")
        printfn "%d correct out of %d in %s" score times taken
    )

Finally a while loop to rinse and repeat:

while true do 
    play 20
    wait 5000
    printfn "Hit a key to play again?"
    readkey () |> ignore

He’s been playing the game quite a lot since and his times keep coming down.

Attwood’s Law:

any application that can be written in JavaScript, will eventually be written in JavaScript.

And so it was, you can now play a JavaScript version of the Times table game:

    TimeTableJavaScript

The code for the JavaScript version:

var form = document.multiplication
var count = 0, correct = 0
var begun
var a, b

function element(name) { return document.getElementById(name) }

function setQuestion() {
 a = Math.floor(Math.random()*13)
 b = Math.floor(Math.random()*13)
 element("question").innerText = a + " x " + b
 form.answer.focus()
}

function nextQuestion() {
 ++count
 form.answer.value = ""
 setQuestion()
} 

function completed() {
 form.answer.disabled = false
 form.answer.value = correct + " / " + count
 var complete = new Date()
 var seconds = Math.round((complete-begun)/1000) 
 element("question").innerText = seconds + " Seconds"
 form.submit.value = "Play again"
 count=0
 correct=0
}

function next() {
 if(count == 0)
 {
   form.submit.value = "Next"
   form.answer.disabled = false
   begun = new Date()
   nextQuestion()
 } 
 else
 {
  var answer = form.answer.value
  if (answer == a * b) ++correct
  else alert(a + " x " + b + " = " + a*b)

  if (count < 10) nextQuestion()
  else completed()
 }
}