Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F#: Why do I have to explicitly specify 'unit' for functions taking no arguments?

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?

like image 560
J Cooper Avatar asked Jan 30 '10 00:01

J Cooper


2 Answers

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.

like image 93
Juliet Avatar answered Nov 05 '22 00:11

Juliet


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.

like image 21
Brian Avatar answered Nov 05 '22 00:11

Brian