F# in the browser

I've been working on this project off and on for about a month and a half, and feel that it's finally at a point where it's usable. It's my first real project in F#, and I can only say good things about the language at this point. "What is it?", you ask. It's an F# quotations to javascript converter. You can also take F# modules and compile them down to javascript.

To start I'll just point you to a few links so you can play with the project as we go.

First, you can run all of the examples I give here in the browser on this mini-site I created - here. The project itself is hosted on github and is currently called FSharp.Javascript (creative, I know).

Lets get started with some code.

module ExampleScript

 

open FSharp.Javascript.Dom

open FSharp.Javascript.Jquery

 

[<ReflectedDefinition>]

let print x = jquery("#output").html(x) |> ignore

 

[<ReflectedDefinition>]

let rec factorial n =

    if n=0 then 1 else n * factorial(n - 1)

 

[<ReflectedDefinition>]

let fact() =

    let result = factorial 2

    print result

 

 

[<ReflectedDefinition>]

let init() = jquery(document)

              .ready(fun x ->

               fact())

There are a couple of things that we should notice in the above code. The first is that each method is decorated with the [<ReflectedDefinition>] attribute. This is a standard F# attribute that tells the compiler to keep track of the quotation definition for the method it decorates. The second is that F# has a limitation with quotations in that we can't get access to the initialization method of our module class, hence the necessity for using an init() method (if anyone has any ideas on how to get around this limitation I would love to hear about it).

If we run the above code on this page we see that we get the desired output of 2.

To use the converter in your own project it's as easy as referencing the FSharp.Javascript assembly and using code like the below

open FSharp.Javascript.Converter

open FSharp.Javascript.Dom

 

let javascript = convert <@ let i = 0

                            alert(i) @>

To compile a module to javascript you can simply use:

open FSharp.Javascript.Converter

 

let javascript = convertModule (System.Type.GetType("MyModule, MyAssembly"))

One of the other limitations that slightly inhibits elegant use of F# to javascript conversion is the lack of anonymous types. Take the below ajax code:

module ExampleScript

 

open FSharp.Javascript.Dom

open FSharp.Javascript.Jquery

 

[<ReflectedDefinition>]

let print x =

    let output = jquery("#output")

    let currentHtml = output.html()

    output.html(currentHtml + x.ToString()) |> ignore

 

type myOptions = {

    success: System.Object -> unit;

    dataType: string;

    url:string;

}

 

[<ReflectedDefinition>]

let ajax() = jquery(document)

              .ready(fun x ->

               jquery.ajax({ success = (fun x -> print x |> ignore); dataType = "HTML"; url = "/home/index" }))

 

[<ReflectedDefinition>]

let init() = jquery(document)

              .ready(fun x ->

               ajax())

Here we see that to call jquery's ajax method we must make a type to pass in with our arguments. Considering jquery's ajax method has around 26 different options it can get a little verbose declaring many of them at once. Our only other option is to declare a default ajax object and then use F#'s "with" syntax to create a new instance of it. In general I don't see this as much of a problem as most projects will only use a few sets of various options and those can be declared in a seperate file and used throughout. The lack of anonymous types may limit us more in other applications of this project, such as using native F# to query MongoDB (which is a project I may start up fairly soon, if I can find the time).

To finish up I think I'll just show off a few F# features in the browser that I know everyone is curious about.

Sequences

module ExampleScript

 

open FSharp.Javascript.Dom

open FSharp.Javascript.Jquery

 

[<ReflectedDefinition>]

let print x =

    let output = jquery("#output")

    let currentHtml = output.html()

    output.html(currentHtml + x.ToString()) |> ignore

 

[<ReflectedDefinition>]

let iter() = seq {for i in 0..10 -> print i } |> Seq.toArray

 

[<ReflectedDefinition>]

let init() = jquery(document)

              .ready(fun x ->

               iter())

Unions with pattern matching

module ExampleScript

 

open FSharp.Javascript.Dom

open FSharp.Javascript.Jquery

 

[<ReflectedDefinition>]

let print x =

    let output = jquery("#output")

    let currentHtml = output.html()

    output.html(currentHtml + x.ToString()) |> ignore

 

type test =

| First of string

| Second of int

| Third of double

 

[<ReflectedDefinition>]

let matchOnUnion() = let testItem = First("Some String")

                     match testItem with

                     | Third(d) -> print "Third"

                     | Second(i) -> print "Second"

                     | First(s) -> print "First"

 

[<ReflectedDefinition>]

let init() = jquery(document)

              .ready(fun x ->

               matchOnUnion())

Active Pattern matching

module ExampleScript

 

open FSharp.Javascript.Dom

open FSharp.Javascript.Jquery

 

[<ReflectedDefinition>]

let print x =

    let output = jquery("#output")

    let currentHtml = output.html()

    output.html(currentHtml + x.ToString()) |> ignore

 

[<ReflectedDefinition>]

let (|NullCheckPattern|_|) x =

    match x with

    | null -> None

    | _ -> Some(x)

 

[<ReflectedDefinition>]

let activePatternMatch() = let value = "a string"

                           match value with

                           | NullCheckPattern(x) -> print "Was not null"

                           | _ -> print "Was null"

 

[<ReflectedDefinition>]

let init() = jquery(document)

              .ready(fun x ->

               activePatternMatch())

I think that's enough for now. There's quite a bit you can do with the project as it stands, but there's also a lot left to do. Library support is lacking in a lot of areas, mostly dealing with sequences, lists, and arrays. But for now I feel it's a pretty good start.

«February»
SunMonTueWedThuFriSat
31123456
78910111213
14151617181920
21222324252627
28123456
78910111213