Search code examples
ocamlhigher-order-functionsfold

Understanding the mechanics of fold_right


Problem:Define the function mapf whose first argument is a unary function, and second argument list. The result is a list of the function applied to each element of the argument list. Write a one-liner using fold_right, and not a recursive function.

Solution:

let mapf fn list = fold_right (fun h t -> fn h :: t) list []

I don't understand how that solves the problem because fold_right computes a function with the list arguments in a recursive way, so it returns a value and not a list. I neither understand what the following notation means:

fun h t -> fn h :: t

Solution

  • Your two questions are related. It's true that fold_right returns a value, but a list is a value!

    The :: operator adds a new element to the beginning of a list. So the value computed by this application of fold_right is indeed a list.

    Another way to think of this might be as follows. If you use fold_right with the + operator you compute a value like so:

    fold_right (+) [1; 2; 3] 0 =>
    1 + 2 + 3 + 0 =>
    6
    

    If you use fold_right with the :: operator, you compute a value like so:

    fold_right (::) [1; 2; 3] [] =>
    1 :: 2 :: 3 :: [] =>
    [1; 2; 3]
    

    This isn't theoretical, it works exactly like this in the REPL:

    # List.fold_right (+) [1; 2; 3] 0;;
    - : int = 6
    # List.fold_right (fun a b -> a :: b) [1; 2; 3] [];;
    - : int list = [1; 2; 3]
    

    (You need to write fun a b -> a :: b because the :: notation can't actuallly be used as a stand-alone function. Unfortunately.)

    Note that it's completely legitimate to write a list using the :: operator yourself:

    # 1 :: 2 :: 3 :: [];;
    - : int list = [1; 2; 3]
    

    Update

    First, fun x y -> expr is the notation for a "lambda" in OCaml, i.e., for a function literal.

    The function fun h t -> fn h :: t takes two values h and t. It applies the function fn to h, and returns a new list with this new value at the front of t.

    From a typing standpoint, the value h must be the right type to pass to fn, and fn must return a value of the right type to be in the list t.

    You could parenthesize it like this: fun h t -> (fn h) :: t, if that makes it clearer.

    The function fun a b -> a :: b is similar except that it just puts a directly onto the list b. It doesn't apply any function. In essence, it does just what the :: operator does.

    From a typing standpoint, a has to be the right type to be an element of the list b.

    Update 2

    If you're trying to understand what a lambda is, one way of looking at it is that it's just a handy way to write a function that's reasonably small. You can easily write your given code without the lambda:

    let mapf fn list =
        let helper h t = fn h :: t in
        List.fold_right helper list []
    

    Instead of a lambda, this version has a locally declared function named helper.

    Another way of looking at it is that all functions are lambdas. The idiomatic way of writing a function:

    let f x y = x + y
    

    is just a handy abbreviation for a version with an explicit lambda:

    let f = fun x y -> x + y
    

    So, really, a lambda isn't a special kind of function. It's exactly like any other function. It's just a notational choice.