Launch the Missiles

If you’ve been programming in F# for any length of time then undoubtedly you’ve run into the term “call by value”.  Another name for “call by value” is eager evaluation as opposed to lazy evaluation and the corresponding “call by name”.  Let’s throw together a fun little program to demonstrate the difference between the two.

 

Call By Value

 

type MissileCommand =

    | Launch

    | Disable

 

let a1 = printfn “%A” “KABOOM!”

let a2 = printfn “%A” “WE LIVE!”

 

let run cmd a1 a2 =

    match cmd with

    | Launch -> a1

    | Disable -> a2

   

run Disable a1 a2

 

So what do you think gets printed to the screen when we run our Disable command?  You may be surprised to see the following written to the console.

 

“KABOOM!”

“WE LIVE!”

 

So how did the human race get obliterated when we sent the Disable command?  In a “call by value” language functions arguments are evaluated eagerly, so both a1 and a2 are evaluated before the run function is applied to them.  Scary!

 

Call By Name

 

To simulate “call by name” semantics in F# we are forced to explicitly wrap our functions in a lazy wrapper.

 

let lazy_a1 = lazy (printfn “%A” “KABOOM!”)

let lazy_a2 = lazy (printfn “%A” “WE LIVE!”)

 

let safe_run cmd (a1:Lazy<_>) (a2:Lazy<_>) =

    match cmd with

    | Launch -> a1.Force()

    | Disable -> a2.Force()

 

safe_run Disable lazy_a1 lazy_a2

 

And now the human race is saved from nuclear annihilation as we get what we expect printed to the console.

 

“WE LIVE!”

 

The lazy wrapper acts as a thunk for the actual computation which only gets computed once we force it.

 

Call By Value With Side Effects

 

Now I hope you don’t lie await at night worrying about a computer bug accidently leading to Armageddon.  I’m sure our nuclear missile system is written in a lazy language. But I do hope you get a feeling for what a noxious combination “call by value” and undocumented side effects are.

 

What if instead of printing to the console our action updated a global counter?  Or deleted records from a database?  Functional languages allow us to pass functions around as if they were data, but do we really want them evaluating them behind the scenes when they do?

9 comments to Launch the Missiles

  • Hello,

    You can also use “unit -> unit” type functions as a sort of poor man’s lazy. For simple cases like this it probably more appropriate (although I do appreciate that it’s a simple example to illustrate the point). I’d probably have written:

    type MissileCommand =
    | Launch
    | Disable

    let a1() = printfn “KABOOM!”
    let a2() = printfn “WE LIVE!”

    let run cmd a1 a2 =
    match cmd with
    | Launch -> a1()
    | Disable -> a2()

    Cheers,
    Rob

  • Robert Nielsen

    I think I missed your point. If I copy your ‘Call By Value’-example to say a F# Script File (.fsx) the signature of a1 is unit, the signature of a2 is unit and the signature of run is MissileCommand -> ‘a -> ‘a -> ‘a . What is supposed to be the link between the a1 and a2 values and the function run ?
    If we want a1 and a2 to be treated like funktions and not (unit) values we should add parentheses. For readability I have renamed a1 and a2 to b1 and b2. The result is as one would expect; only b2 is evaluated.

    #light

    type MissileCommand =
    | Launch
    | Disable;;

    let b1 () = printfn “%A” “Kaboom!”;;
    let b2 () = printfn “%A” “We live!”;;

    let run cmd b1 b2 =
    match cmd with
    | Launch -> b1
    | Disable -> b2

    run Disable b1 b2 ()

    System.Console.ReadKey() //just so that the console is not closed right away

    Best regards,
    Robert Nielsen

  • mattdoig

    Hello Mr Nielsen,

    See Mr Pickering’s comment above for an answer to your question. Adding a () to your function creates a thunk to a function and not an actual function, you’re example is a simpler way to do lazy evaluation described above.

    I would argue that lazy evaluation should be the default as opposed to the edge case. Do we really want programmers deciding whether to create functions or thunks to functions and then reasoning about how they will behave?

  • Dmitry Tsygankov

    I wonder, I wonder…
    Now what happens if we really really want to launch three missiles?
    run Launch a1 a2
    run Launch a1 a2
    run Launch a1 a2
    . How many “Kabooms” do we get?
    A simple experiment shows that we get only one. So our potential enemy still has most of its nuclear weapons targeted at us. In this simple case the problem is simple to resolve, but what if we have those calls nested somewhere deep in our code?
    After messing with Haskell for a while I’m pretty much convinced that lazy evaluation with side effects is evil.

  • Dmitry Tsygankov

    In Haskell, the only way you could do IO inside a pure lazy function is making use of the function “unsafePerformIO”. And I believe it is called “unsafe” for a reason…

  • Robert Nielsen

    Hi Anonymous,

    I guess it depends on your definition of an actual function. If I use .Net Reflector (freely available from http://reflector.red-gate.com/download.aspx) I see that b1 and b2 are indeed functions in the MS dotnet IL (Intermediate Language) sense.

    I definitely agree with your comment “Do we really want programmers deciding whether to create functions or thunks to functions”; I am completely happy with the b1- and b2-functions the F#-compiler creates for me, even if they are thunks by your yardstick.

    Yup, Rob’s comment is more concise than mine, but his comment wasn’t yet visible when I wrote my comment. :-)

    From my perspective the connection between the original variables a1, a2 and the evaluation of printfn have only little to do with ‘Call By Value’. Even if you never call ‘run’ a1 and a2 will still be initialized (evaluated) early on in the program/script. Do you guys compile the program/script in some way so that a1 and a2 are allowed to be ‘optimized’ away? When I write a let-expression I expect the compiler to initialize my variable (and so it does).

    In such a simple example I would probably have written:

    #light

    type MissileCommand =
    | Launch
    | Disable

    let b1() = printfn “%A” “Kaboom!”
    let b2()= printfn “%A” “We live!”

    let run cmd =
    match cmd with
    | Launch -> b1()
    | Disable -> b2()

    run Disable

    System.Console.ReadKey() //just so that the console is not closed right away

    which emphasizes that it is the ‘cmd’ parameter that determines wheter to go “boom” or not.

    Now also you get the obvious result (three times “Kaboom!”) if you want to launch three missiles:

    run Launch
    run Launch
    run Launch

    I appreciate we now have such a powerful language (F#) where we can express algorithms beautifully and yet relatively easily figure out how the compiler will interpret what we are writing. On a side note though, if I move the functions b1 and b2 inside of run like this:

    let run cmd =
    let b1() = printfn “%A” “Kaboom!”
    let b2()= printfn “%A” “We live!”
    match cmd with
    | Launch -> b1()
    | Disable -> b2()

    then b1 and b2 are no longer static functions, but rather object methods. Do any of you have a good link to somewhere I can read about the reason behind that?

    Thank you all for the examples and comments
    Robert Nielsen

  • mattdoig

    Robert Nielsen

    There’s a good talk by Simon Peyton Jones introducing Haskell at OSCON. In the talk he mentions trying to write a conditional function in a “call by value” language. He says it can’t be done becuase “call by value” languages evaluate their parameters before applying the function to them.

    The reason the functions are passed as parameters was to test whether this is really true. The example was a bit of a humorous attempt to test out the concept as opposed to a rather boring conditional function.

  • Markus Müller

    a1 and a2 are values of type unit and regarding their scope, they are assigned before the execution of the run function. Of course would they be initialized beforehand. They’re not functions, they just store the unit value.

    I didn’t know F# so this puzzled me, but now I’m just disappointed.

  • Markus Müller

    sorry for that comment, you did show call by name evaluation and i was hindered by limited knowledge.

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>