• RSS
  • Facebook
  • Twitter

Nunc rutrum nunc in enim bibendum at pellentesque est pellentesque. Pellentesque sodales bibendum elit non pulvinar. Mauris fermentum lectus ac risus dapibus cursus eu non ipsum.

  • Example Title 1

    Replace this sample description with your own description. A free premium template by NewBloggerThemes.com.

  • Example Title 2

    Replace this sample description with your own description. A free premium template by NewBloggerThemes.com.

  • Example Title 3

    Replace this sample description with your own description. A free premium template by NewBloggerThemes.com.

    Friday, January 9, 2009

    Finally, the type that gives name to the whole functional programming is a function. In F#, similarly to other functional languages, functions are first-class values, meaning that they can be used in a same way as any other types. They can be given as an argument to other functions or returned from a function as a result (a function that takes function as an argument or returns function as a result is called high-order function) and the function type can be used as a type argument to generic types - you can for example create a list of functions. The important aspect of working with functions in functional languages is the ability to create closures – creating a function that captures some values available in the current stack frame. The following example demonstrates a function that creates and returns a function for adding specified number to an initial integer:

    > let createAdder n = (fun arg -> n + arg);;
    val createAdder : int -> int -> int
    > let add10 = createAdder 10;;
    val add10 : int -> int
    > add10 32;;
    val it : int = 42


    In the body of the createAdder function we use a fun keyword to create a new unnamed function (a function constructed in this way is called a lambda function). The type of createAdder (int -> int -> int) denotes that when the function is called with int as an argument, it produces a value of type function (which takes an integer as a parameter and produces an integer as a result). In fact, the previous example could be simplified, because any function taking more arguments is treated as a function that produces a function value when it is given the first argument, which means that the following code snippet has the same behavior. Also note that the types of the function createAdder declared earlier and the type of the function add are the same):

    > let add a b = a + b;;
    val add : int -> int -> int
    > let add10 = add 10;;
    val add10 : int -> int

    When declaring the function value add10 in this example, we used a function that expects two arguments with just one argument. The result is a function with a fixed value of the first argument which now expects only one (the second) argument. This aspect of working with functions is known as currying. Many functions in the F# library are implemented as high-order functions and functions as an arguments are often used when writing a generic code, that is a code that can work with generic types (like list<'a>, which we discussed earlier). For example standard set of functions for manipulating with list values is demonstrated in the following example:

    > let odds = List.filter (fun n -> n%2 <> 0) [1; 2; 3; 4; 5];;
    val odds : list = [1; 3; 5]
    > let squares = List.map (fun n -> n * n) odds;;
    val squares : list = [1; 9; 25]

    It is interesting to note that the functions that we used for manipulating with lists are generic (otherwise they wouldn’t be very useful!). The signature of the filter function is ('a -> bool) -> list<'a> -> list<'a>, which means that the function takes list of some type as a second argument and a function that returns a true or false for any value of that type, finally the result type is same as the type of the second argument. In our example we instantiate the generic function with a type argument int, because we’re filtering a list of integers. The signatures of generic functions often tell a lot about the function behavior. When we look at the signature of the map function (('a -> 'b) -> list<'a> -> list<'b>) we can deduce that map calls the function given as a first argument on all the items in the list (given as a second argument) and returns a list containing the results. In the last example we will look at the pipelining operator (|>) and we will also look at one example that demonstrates how currying makes writing the code easier - we will use the add function declared earlier:

    > let nums = [1; 2; 3; 4; 5];;
    val nums : list
    > let odds_plus_ten =
    nums
    |> List.filter (fun n-> n%2 <> 0)
    |> List.map (add 10)
    val odds_plus_ten : list = [11; 13; 15];;

    Sequences of filter and map function calls are very common and writing it as a single expression would be quite difficult and not very readable. Luckily, the sequencing operator allows us to write the code as a single expression in a more readable order - as you can see in the previous example, the value on the left side of the |> operator is given as a last argument to the function call on the right side, which allows us to write the expression as sequence of ordinary calls, where the state (current list) is passed automatically to all functions. The line with List.map also demonstrates a very common use of currying. We want to add 10 to all numbers in the list, so we call the add function with a single argument, which produces a result of the type we needed - a function that takes an integer as an argument and returns an integer (produced by adding 10) as the result.

    0 comments: