8

Are these functions exactly same? That is, are 1st and 2nd syntax just convenient shorthand for the last syntax? Or is there some theoretical or practical difference, and if so, what is it?

let f1 a b = a + b

let f2 a = (fun b -> a + b)

let f3 = (fun a -> (fun b -> a + b) )

They seem the same to me, f1 5, f2 5 and f3 5 seem to return identical value, for instance. Just checking I'm not making invalid assumption here. In other words, I hope a knowledge-based answer, not one saying "Yeah, I believe they are the same".

hyde
  • 60,639
  • 21
  • 115
  • 176
  • 2
    Check [my answer here](http://stackoverflow.com/questions/2175940/int-int-int-what-does-this-mean-in-f/2176428#2176428), could help you understand the difference – Bruno Reis Oct 30 '12 at 13:17
  • I augmented the question to contain 3rd case from an answer. – hyde Oct 30 '12 at 13:33

3 Answers3

8

Your assumption is correct, in this case, the functions are exactly the same.

You can see that by inspecting the generated IL code (as demonstrated by Craig) and you can also see that by looking at the type inferred by the F# compiler. In both cases, you'll see int -> int -> int. The F# language views that as a function that takes int and returns int -> int but it is actually compiled as a method with multiple arguments (for efficiency).

If you write fun immediately following let .. = then the compiler turns that into a standard function. However, you can write code that is a bit different if you do some computation before returning the function:

let f1 a b = printfn "hi"; a + b
let f2 a = printfn "hi"; (fun b -> a + b)

Now the two functions are very different, because the second one prints "hi" when you give it just a single argument (and then it returns a function that you can call):

> let f = f2 1;;
hi                      // The body is called, prints 
val f : (int -> int)    // and returns function

> f 2;;                 // This runs the body of 'fun'
val it : int = 3        // which performs the additiion

You can write the same code using f1, but the first command will just create a new function and the second command will print "hi" and do the addition.

In this case, the generated IL code for f2 will be different. It will be a function that returns a function (of type FSharpFunc<int, int>). The type displayed by F# is also different - it will be int -> (int -> int) instead of int -> int -> int. You can use values of these two types in exactly the same ways, but it hints you that the first one may do some effects when you give it a single argument.

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
5

Here's the IL for f1:

.method public static int32  f1(int32 a,
                                int32 b) cil managed
{
  .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = ( 01 00 02 00 00 00 01 00 00 00 01 00 00 00 00 00 ) 
  // Code size       5 (0x5)
  .maxstack  4
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldarg.1
  IL_0003:  add
  IL_0004:  ret
} // end of method Program::f1

...and for f2:

.method public static int32  f2(int32 a,
                                int32 b) cil managed
{
  .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = ( 01 00 02 00 00 00 01 00 00 00 01 00 00 00 00 00 ) 
  // Code size       5 (0x5)
  .maxstack  4
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldarg.1
  IL_0003:  add
  IL_0004:  ret
} // end of method Program::f2

As you can see, it's essentially identical, so yes, they're the same.

Craig Stuntz
  • 125,891
  • 12
  • 252
  • 273
  • Is it really relevant that they produce the same byte code for some test case? That is, no matter how you use the function, does it always end up calling that "real" method in the end when it finally does something with the function? – hyde Oct 30 '12 at 13:36
  • Yes it does do that. Tomas explained why. – Craig Stuntz Oct 30 '12 at 16:46
5

Two functions are the same. They could be considered as syntactic sugars for

let f = fun a -> fun b -> a + b

There is small practical difference. f1 emphasizes that the function returns a value while f2 returns a closure, which in turn produces a value. The use of f2 is a little more attractive in creating combinators e.g. parser combinators.

On a side note, there is no equality on functions in F# so f1 5 and f2 5 are different values but they produce the same outputs on the same inputs.

pad
  • 41,040
  • 7
  • 92
  • 166
  • +1 I think this is a useful answer and good way to understand F#. Although technically speaking, they are both syntactic sugars for `let f a b = ...` because F# (and other ML languages) treat functions defined using `let` and `fun` differently (functions defined using `let` can be generic and functions using `fun` cannot). This code is treated as if it was using `let f a b = ...` as long as the body contains `fun` without any other code. – Tomas Petricek Oct 30 '12 at 13:20