fsharp-cheatsheet @ GitHub
Home | F# for fun and profit
F# Software Foundation (fsharp.org)
Try F#
F# Workshop
F# Programming - Wikibooks, open books for an open world
F# language has an interesting feature called currying.
Curry is a spice from India region, as well as a tree whose leaves a used a spice.
Curry - Wikipedia, the free encyclopedia Curry tree - Wikipedia, the free encyclopedia
Curry leaves are in a sequence, one by one, same as F# function arguments.
But that is not a reason why the currying feature is named...
"Breaking multi-parameter functions into smaller one-parameter functions
...if a mathematical function can only have one parameter, then how is it possible that an F# function can have more than one? The answer is quite simple: a function with multiple parameters is rewritten as a series of new functions, each with only one parameter. And this is done automatically by the compiler for you. It is called "currying", after Haskell Curry, a mathematician who was an important influence on the development of functional programming."
To see how this works in practice, let’s use a very basic example that prints two numbers:
//normal version
let printTwoParameters x y =
printfn "x=%i y=%i" x y
Internally, the compiler rewrites it as something more like:
//explicitly curried version
let printTwoParameters x = // only one parameter!
let subFunction y =
printfn "x=%i y=%i" x y // new function with one param
subFunction // return the subfunction
// example: function: distance (x, y) { return Math.Abs(x - y); }
let distance x y = x - y |> abs
// val distance : x:int -> y:int => int
distance 5 2
3
is equivalent to:
(distance 5) 2
or:
let distance5 = distance 5 // a new function with one argument
// val distance5 : (int -> int)
distance5 2
// val it : int = 3
Functions as First-Class Values (F#)
Prefix vs Infix Functions:
(+) 1 3
is equivalent to:
1 + 3
// function that adds 1 to its argument:
let add1 = (+) 1
// val add1 : (int -> int)
Infix functions can help make expressions more readable, i.e.:
1 + 2 + 3
// is same as
(1 + 2) + 3
// is same as
((+) ((+) 1 2) 3)
// "associative" if order of applying arguments does not change result
(1 + 2) + 3 == 1 + (2 + 3)
// "non-associative"
(36 / 6) / 2 != 36 / (6 / 2)
// same example in Lisp / Clojure
// (+ (+ 1 2) 3)
// infix function must use symbols; distance function equivalent:
let (|><|) x y = x - y |> abs
// usage:
5 |><| 2
// val it : int = 3
5 |><| 2 |><| 10
// val it : int 7
Lambda Expressions (anonymous functions):
(fun x -> x * x)
usage as a function argument to another function; lambda is symbol ->
List.map (fun x -> x * x) [1;2;3]
// distance function
(fun x y -> x - y |> abs) 5 3
Recursion:
let rec length = function // no-name argument is immediately passed to matching
| [] -> 0 // if argument is an empty list
| x::xs -> 1 + length xs // else x: head of list; xs: tail of list
let rec factorial n =
if n < 2 then 1
else
n * factorial (n - 1)
Pipe Operators:
sin 2.
is equivalent to:
2. |> sin
"pipe" operators simplify "chaining" functions
2. |> sin |> ((*) 3) // == sin(2) * 3
pipe operator can be in both direction, this is same as above
sin <| 2.
pipe operator can be combined:
min 12 7
is equivalent to:
12 |> min <| 7
is equivalent to (multiple arguments, double pipe operator):
(12,7) ||> min
Function composition:
let minus1 x = x - 1
let times2 x = x * 2
is equivalent to:
let minus1 = (+) -1 // !! not the same as (-) 1
let times2 = (*) 2
times2 (minus1 9)
is equivalent to:
(times2 << minus1) 9
is equivalent to:
times2 << minus1 <| 9
is equivalent to:
let minus1times2 = times2 << minus1
minus1times2 9
is equivalent to:let minus1times2 = minus1 >> times2
minus1times2 9