Question

Possible Duplicate:
Functional programming: currying

I'm reading the free F# Wikibook here:

http://en.wikibooks.org/wiki/F_Sharp_Programming

There's a section explaining what Partial Functions are. It says that using F# you can partially use a function, but I just can't understand what's going on. Consider the following code snippet that is used an example:

#light
open System

let addTwoNumbers x y = x + y
let add5ToNumber = addTwoNumbers 5

Console.WriteLine(add5ToNumber 6)

The ouput is 11. But I'm not following. My function 'add5ToNumber' doesn't ask for a paramter so why can I invoke it and give it it one?

I really like learning about F# these days, baby steps!

Was it helpful?

Solution

Basically, every function in F# has one parameter and returns one value. That value can be of type unit, designated by (), which is similar in concept to void in some other languages.

When you have a function that appears to have more than one parameter, F# treats it as several functions, each with one parameter, that are then "curried" to come up with the result you want. So, in your example, you have:

let addTwoNumbers x y = x + y

That is really two different functions. One takes x and creates a new function that will add the value of x to the value of the new function's parameter. The new function takes the parameter y and returns an integer result.

So, addTwoNumbers 5 6 would indeed return 11. But, addTwoNumbers 5 is also syntactically valid and would return a function that adds 5 to its parameter. That is why add5ToNumber 6 is valid.

OTHER TIPS

Currying is something like this:

addTwoNumbers is a function that takes a number and returns a function that takes a number that returns a number.

So addTwoNumbers 5 is in fact a function that takes a number and returns a number, which is how currying works. Since you assign addTwoNumbers 5 to add5ToNumber, that make add5ToNumber a function that takes a number an returns a number.

I don't know what type definition looks like in F# but in Haskell, the type definition of functions makes this clear:

addTwoNumbers :: (Num a) => a -> a -> a

On the other hand, if you wrote addTwonumbers to take a two tuple,

addTwoNumbers :: (Num a) => (a, a) -> a

then is would be a function that takes a two tuple and returns a number, so add5ToNumber would not be able to be created as you have it.

Just to add to the other answers, underneath the hood a closure is returned when you curry the function.

[Serializable]
internal class addToFive@12 : FSharpFunc<int, int>
{
    // Fields
    [DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated, DebuggerNonUserCode]
    public int x;

    // Methods
    internal addToFive@12(int x)
    {
        this.x = x;
    }

    public override int Invoke(int y)
    {
        return Lexer.add(this.x, y);
    }
}

This is known as eta-expansion : in a functional language,

let f a = g a

Is equivalent to

let f = g

This makes mathematical sense : if the two functions are equal for every input, then they're equal.

In your example, g is addTwoNumbers 5 and the code you wrote is entirely equivalent to:

let add5toNumber y = addTwoNumbers 5 y

There are a few situations where they are different:

  • In some situations, the type system may not recognize y as universally quantified if you omit it.
  • If addTwoNumbers 5 (with one parameter only) has a side-effect (such as printing 5 to the console) then the eta-expanded version would print 5 every time it's called while the eta-reduced version would print it when it's defined. This may also have performance consequences, if addTwoNumbers 5 involved heavy calculations that can be done only once.
  • Eta-reduction is not very friendly to labels and optional arguments (but they don't exist in F#, so that's fine).

And, of course, unless your new function name is extremely readable, providing the names of the omitted arguments is always a great help for the reader.

addTwoNumbers accepts 2 arguments (x and y).

add5ToNumber is assigned to the output of calling addTwoNumbers with only 1 argument, which results in another function that "saves" the first argument (x -> 5) and accepts one other argument (y).

When you pass 6 into add5ToNumber, its passing the saved x (5) and the given y (6) into addTwoNumbers, resulting in 11

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top