21st Dec 2009

Functions in F#

Wikipedia says that

…functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data. It emphasizes the application of functions…

The point I wish to make here is that functions are the  most important “things” in a functional programming language. You can say that functions are “first class citizens” in a functional programming language. This means that functions can be passed around as parameters and return values of other functions, just like any other data. Functions which can take other functions as parameters and/or return values are called Higher-Order Functions.

Functions and Type Inference

Here is an example of a simple function in F#.

let triple x = 3 * x

Here triple is the name of the function and x is the name of the parameter passed to the function. Contrary to C# naming convention for function, F# uses camel casing. Even though we have not specified any type information, the compiler is able to infer that this function takes an integer as parameter and returns an integer. This is called Type Inference and makes your F# program more concise.

How did I know that the compiler inferred the parameter and return value as int? I typed in the above functional declaration in F# interactive and terminated it with ;; and hit Enter. F# showed me:

val triple : int –> int

This means that triple is a function which takes an integer as parameter and returns an integer. If we try to call the function with a float as parameter

triple 3.0

it will spit out an error message as follows.

stdin(4,8): error FS0001: This expression was expected to have type
    int   
but here has type
    float   

We know that * (multiplication operator) works with floats as well, so why this error ? The reason for this is that in the absence of any additional type information the compiler simply defaulted the type to integer. So triple is a function which can take an integer as parameter and return an integer which is equal to three times its input. It is equivalent to the following C# function.

static int Triple(int x)
{
      return 3 * x;
}

Since the function expects integer, a parameter of type float will produce an error.

So what do we do when we need a datatype other than the default type that the compiler is able to infer? We give a hint to the compiler via type annotation.

let triple (x: float) = 3.0f * x

Note: In the above example type annotation is not strictly required because the compiler would infer x as float because 3.0f is float. But it does show you how to annotate a function parameter with its type.

 

Higher Order Functions

The functions mentioned above are not a higher-order functions because they do not take a function as parameter, neither do they return one. List.map and List.filter are examples of higher order functions. List.map takes a function and applies it to each item in the list (also passed as parameter to it) and returns a new list with the output values. List.filter takes a function and list as parameters and returns a new list. The output list contains those values from the input list for which the input function returned true. An example is shown below which makes use of both these higher order functions.

let triple x =
    3 * x

let isEven x =
    x % 2 = 0

let actOnEven action numbers=
    List.map action (List.filter isEven numbers)

[<EntryPoint>]
let main(args:string[]) =
    let results = (actOnEven triple [1 .. 10])
    Console.WriteLine (results)
    0

Here actOnEven is itself a higher order function.

Recursive Functions

Recursive functions are functions that call themselves. An important tenet of functional programming is the use of recursion instead of looping. We can create recursive functions using the following syntax.

let rec factorial num =
    if( num <= 1) then
        1
    else
        num * factorial (num - 1)

The rec keyword is a helping hand to the compiler for type inference. Personally, I don’t like it but I think they couldn’t find a way around it.

Function Currying

Currying (named after Haskell Brooks Curry) is an interesting technique in which you pass one of the arguments to a function and it returns another function which takes the rest of the arguments. For eg., consider the function

let add x y = x + y

If you run this in F# interactive, you will see the following message:

val add : int -> int –> int

This means that add is a function which takes an integer and returns a function which takes another integer as parameter and returns an integer. So if we call add with just one parameter, it will return another function which takes the second parameter.

let addToTwo = (add 2)

To be clear, addToTwo is a function that takes one integer parameter and returns an integer.

For eg,

addToTwo 8

will return 10.

Here addToTwo is the curried function and this technique is called function currying. Although this may look like a strange and useless language feature, it has interesting and powerful applications in functional programming (like the pipe forward operator). I will write more about this in future blog posts.

Nested Functions

In F#, a function can contain another function. Here is a simple example.

let printList numbers=
    let print num=
        printfn "%i" num
    List.map print numbers

Here print is a function nested in printList.

Some of the feature that I have mentioned here would be new to C# programmers and might feel a little strange. But have hope, for all will become more clear with time. We need to see some solid examples to appreciate the power of some of them. I have mostly presented useless examples but I hope a reader would have gleaned atleast a little information about functions and its usage in F# from this post. There is more to write but it is getting late (3 am) and tomorrow is a working day, so I gotta go. Will be back with more on F#.

References:

Programming F#

Expert F#

7 Responses to “Functions in F#”

  1. Binil Thomas Says:

    Few points:

    1) I am surprised that the compiler infers the type of 3 * x to be int. What is the type of x * 3?

    2) From the code you wrote, I think F# uses significant whitespace – you might want to explain that.

    3) Functional programmers often use (tail) recursion to implement looping structures you might see in many other languages. The example you showed, I think, is not tail-recursive. Maybe something like:

    let factorial num =
    let rec factInternal num prod =
    if (num <= 1) then
    prod
    else
    factInternal(num-1, num*prod)
    factInternal(num, 1)

    is more appropriate.

    4) I am going to assume that the F# syntax for defining a temporary binding is:

    let somefunc arg =
    let x = 1
    arg +x

    Couple this with the statement that functions are 'first class' and I think the nested function syntax follows naturally.

    BTW, I don't know Jack about F#, so .. :-)

  2. Binil Thomas Says:

    Also, instead of:
    let add2 = (add 2)

    you might want to use the form:
    let addToTwo = (add 2)

    which makes the subsequent expression
    addToTwo 8
    clearer (readers might think that the ’2′ in ‘add2′ has some significance).

  3. Pradeep Says:

    Thanks for the suggestion, mashe. It does make sense. I will make that change now.

  4. pc Says:

    Your first comment went to the spam queue. I have fixed it now.

    You do make some interesting points.

    For #1, in the context of the function that I wrote (i.e. in the absence of additional type information), x * 3 and 3 * x are both inferred as int. Why are you surprised with that? What else would you expect it to be ?

    I will address your questions #2 and #3 in a separate post. You are correct in your assumptions on both.

    #4 you are correct in your assertion. It would have been a good way to explain both the concepts in a way they relate to each other.

    Thanks for the comments mashe.

  5. Binil Thomas Says:

    I would have expected the type of 3 * x to the type of x.

  6. Pradeep Says:

    I don’t know the correct answer to this, but here is what I think it could be.

    In the expression (3 * x), 3 is an integer. For an integer type the * operator is defined only to work with ints. So unless we have specified x to be of another type (in which case the compiler will throw an error) the compiler assumes the type of x to be int. The return value of int * int is int.

    Now let us consider the expression x * y, where no type information has been specified for x and y. The compiler would expect the type of x and y to be something that has the operator ‘*’ defined for the type. If we haven’t overloaded the * for any custom types, the compiler would have to select from a list of types for which * is predefined. Those would be the numerical types like int, float etc.
    In the absense of any additional type information to chose between them, the compiler defaults to int.

    I should clarify that this is my guess, not a documented fact :-)

  7. .entrypoint » Blog Archive » Functions in F# Part 2 Says:

    [...] had written a post on Functions in F# a couple of days ago and Binil posted a comment which prompted me to post a follow up to add some [...]

Leave a Reply