Friday, March 6, 2009

F#: Haskellers beware!

Equational reasoning may fail you. F# land is the land where nuclear missiles are launched all the time, and puny gnomes carry wands of death.

let g = new System.Random()
let s = Seq.init_infinite (fun _ -> g.NextDouble())

/// You thought 'a seq is Haskell [a]? Not quite! It does not memoize:
printfn "%A" (Seq.hd s, Seq.hd s) (* Surprise! fst and snd differ! *)

type X = 
    static member X = g.NextDouble()

/// You thought X is a value? Not quite! It is a property (think function):
printfn "%A" (X.X, X.X) (* Surprise! fst and snd differ! *)


  1. The first example can be brought back to the land of the lazy with a cache call:

    open System
    let g = Random()
    let s = Seq.init_infinite (fun _ -> g.NextDouble()) |> Seq.cache
    printfn "%d, %d" (Seq.hd s) (Seq.hd s) (* performs like a Haskell lazy list, at least in this example *)

    The PowerPack type LazyList<'a> I'm sure would also work but I'm not as familiar with using it.

    As for the second example, I agree that behavior is peculiar. However it's apparently another feature :)
    From the 1.9.6 Draft Language spec, section 8.4.1 Property Members:


    [static] member [self-ident .] ident = expr

    is equivalent to:

    [static] member [self-ident .] ident with get () = expr

    Oh well. Let's just hope the AreNukesLaunched property doesn't accidentally press the button.

  2. Right :)

    One problem I used to have with Seq.cache is its type (it's not returning a seq).

    But now that you mention it, I gave it a second look, and the return type can be easily mended:

    let cache<'a> : 'a seq -> 'a seq = fun x -> upcast (Seq.cache x)

    Concerning properties, it's just, well, one of those .NET things :)

    I wish .NET designers read the "Lambda Papers" from the 1970s. One concept, lambda, is quite enough!