Skip to content
AgileLogics
5 min read

Lambdas and Higher-Order Functions

Prerequisites: Currying and partial application

A lambda is a function without a name, written with fun:

let square = fun x -> x * x
printfn "%d" (square 4)

[1; 2; 3]
|> List.map (fun x -> x * 10)
|> printfn "%A"
16
[10; 20; 30]

let square x = x * x is just a shorter way of writing the first line.

Higher-order functions

A higher-order function takes a function as a parameter or returns one. List.map is one; you can write your own:

let applyTwice f x = f (f x)
printfn "%d" (applyTwice (fun n -> n + 3) 10)
printfn "%s" (applyTwice (fun s -> s + "!") "hey")

let makeGreeter greeting =
    fun name -> $"{greeting}, {name}!"

let hello = makeGreeter "Hello"
let hola = makeGreeter "Hola"
printfn "%s" (hello "Ada")
printfn "%s" (hola "Linus")
16
hey!!
Hello, Ada!
Hola, Linus!

applyTwice has the type ('a -> 'a) -> 'a -> 'a: its first parameter is itself a function. makeGreeter returns a function that remembers greeting — a closure.

Shorthand: _.Property

Since F# 8, _.Member is a shortcut for a lambda that reads a property or calls a method:

["kiwi"; "banana"; "fig"]
|> List.map _.Length
|> printfn "%A"

["kiwi"; "banana"; "fig"]
|> List.map _.ToUpper()
|> printfn "%A"
[4; 6; 3]
["KIWI"; "BANANA"; "FIG"]

Operators are functions

Wrap an operator in parentheses to use it as a function:

printfn "%d" ((+) 2 3)
printfn "%d" (List.reduce (+) [1; 2; 3; 4])
printfn "%A" (List.map ((*) 2) [1; 2; 3])
5
10
[2; 4; 6]

Practice

Write a function compose3 f g h that returns a function applying f, then g, then h. Use it to build a function that adds 1, doubles, and converts to a string with a "#" in front. Apply it to 4.

Show solution
let compose3 f g h = f >> g >> h
let label = compose3 (fun x -> x + 1) (fun x -> x * 2) (fun x -> $"#{x}")
printfn "%s" (label 4)
#10