views:

105

answers:

2

So I've just about finished my first F# program, with my only functional background being a little bit of knowledge of Haskell (read: Haven't really produced any programs in it).

After experiencing some boggling behavior, I came to realize that F# makes a differentiation between:

prepareDeck = allSuits |> List.collect generateCards |> shuffle

and

prepareDeck() = allSuits |> List.collect generateCards |> shuffle

I noticed that it "caches" the former, never recalculating it if it's called again, whereas it treats the latter like a normal function. You can't tell the difference if the function in question doesn't have side effects, obviously, but my shuffle did!

Was this supposed to be common knowledge? I haven't seen it mentioned on any tutorial materials yet. Is the reason just a weakness in the parser, kinda like how you have to declare a function before you use it?

+13  A: 

Most F# material does explain that all top-level statements in a module are executed from top-down on declaration. In other words, what you've declared isn't a function, but a value which is bound once when the program runs.

It really helps to see the reflected code. I have a simple file:

let juliet = "awesome"
let juliet2() = "awesome"

The compiled code looks something like this:

public static string juliet
{
    [CompilerGenerated, DebuggerNonUserCode]
    get
    {
        return "awesome";
    }
}

//...

public static string juliet2()
{
    return "awesome";
}

So one is a static property, the other is a function. This is a desirable property, because imagine if we had something like this:

let x = someLongRunningDatabaseCall()

We only want x to be bound once, we don't want it to invoke database function everytime we access x.

Additionally, we can write interesting code like this:

> let isInNebraska =
    printfn "Creating cities set"
    let cities = set ["Omaha"; "Bellevue"; "Lincoln"; "Papillion"; "La Vista"; "Ralston"]
    fun n -> cities.Contains(n);;
Creating cities set

val isInNebraska : (string -> bool)

> isInNebraska "Omaha";;
val it : bool = true

> isInNebraska "Okaloosa";;
val it : bool = false

Since isInNebraska is a value, its evaluated immediately. It just so happens that its datatype is (string -> bool), so it looks like a function. As a result, we only fill our cities set once even if we invoke the function 1000 times.

Let's compare that code to this:

> let isInNebraska2 n =
    printfn "Creating cities set"
    let cities = set ["Omaha"; "Bellevue"; "Lincoln"; "Papillion"; "La Vista"; "Ralston"]
    cities.Contains(n);;

val isInNebraska2 : string -> bool

> isInNebraska2 "Omaha";;
Creating cities set
val it : bool = true

> isInNebraska2 "Okaloosa";;
Creating cities set
val it : bool = false

Oops, we're creating a new cities set everytime we invoke the function.

So there is definitely a legitimate and real distinction between values and functions.

Juliet
Nice example. For anyone who's interested, the "Expert F#" book (chapter 8) develops this discussion a bit further in the context of designing functions for efficient partial application.
itowlson
I think there's a flaw here: there's not much difference between a static property and a static method in that **both are methods** behind the scenes, and an access to a property is actually a method call (that's why you can mark a property as *virtual*). I can't see your "desirable property".
Bruno Reis
I believe that the difference is that when you have `let julietNoFunction = ...` the static property will simply return a value stored on a static field, and this value is calculated only once at the static constructor of the generated type for the module. I think I've seen this in Reflector, but I'm not sure anymore. Anyways, the way you present it does not show any advantages at all on using `let juliet = ...` over `let juliet() = ...`.
Bruno Reis
+6  A: 

This is how things work in practically every language with side effects.

let name = expr

runs the code 'now', and may cause side-effects if expr has effects. Subsequent references to name have no effects. Whereas

let name() = expr

defines a function, has no effects now, and will evaluate (and have effects) every time name() is invoked.

Brian
Gotcha. Guess I was confused because F# is the first language I've used that is functional but side-effecty.
J Cooper