F# tips weekly #3: piped lambda function

F# tips weekly #3: piped lambda function

Usage of lambda functions as parameters for another function is well known. Less commonly used is the application of lambda functions in other contexts. We can leverage the fact that a lambda function is a value and use it wherever we can use a normal function. Let's explore the combination of lambda functions with the pipe |> operator.

One great application is to call a function with a parameter other than the last, which can be quite common with Map or Set:

let updateWithDefault key def (m: Map<_, _>) =
    m
    |> Map.tryFind key
    |> Option.defaultValue def
    |> fun x -> Map.add key x m

When we have a value that we want to add to a Map, we can't use |> Map.add directly. However, with a lambda function, we can pass the value from the pipe as any parameter.

💡
Note that we don't need to use parentheses around fun x -> Map.add key x m when used together with the |> operator. This is because the |> operator has lower precedence than function application.

Another application is inlining single-case pattern matching as we seen in previous week:

createTriple
|> fun (a, b, c) -> a + b + c

Also, we can use an active pattern:

value
|> someCSharpMethod
|> function FromNull o -> o
|> Option.defaultValue ...

Here, we use the function keyword, which is a shortcut for fun x -> match x with .... This can also be used, of course, to handle multiple cases:

maybeValue
|> function
    | Some v -> v
    | None -> defaultValue

Another related technique is to inline an if condition in the pipe. We can use the id function to do nothing and pass the value through:

[1..10]
|> if filterEnabled then List.filter (fun x -> x % 2 = 0) else id

A nice advantage of this technique is that List.filter is not called for the else case, in contrast to:

[1..10]
|> List.filter (fun x -> if filterEnabled then x % 2 = 0 else true)

Overall, I believe using lambda functions with the pipe is a useful tool that can make code more readable and concise, especially when there is no need to create a new binding. When using pipes, don't forget that the value in the pipe is only one |> fun x -> away!