F# is a strongly typed language. As a result, it is less error-prone. Here are some of the most important properties of F#, in my opinion:
A function with multiple parameters can be rewritten as a series of new functions, each with only one parameter, by the compiler. This process is called "currying,” named after Haskell Curry.
Say we have a function called printTwoNumbers
:
let printTwoParameters x y =
printfn "x=%i y=%i" x y
The compiler compiles the function like this:
//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
So, we can use the function like the following:
// step-by-step version
let x = 6
let y = 99
let intermediateFn = printTwoParameters x // return fn with
// x "baked in"
let result = intermediateFn y
// inline version of above
let result = (printTwoParameters x) y
// normal version
let result = printTwoParameters x y
Currying is very useful when it comes to working with callback functions. Say we are calling function B from A, and from inside B we want the following callback function to be called.
let callback(x: int)(y: int) =
printfn "x=%i y=%i" x y
callback
needs two parameters. Say that A has x and B has y. So, in order to call callback
from inside B without currying, x also needs to be passed to B from A along with the reference to callback
. But with currying, we can just do the following:
let B (callbackWithX: int -> unit) =
let y: int = 3
callbackWithX y
let A =
let x: int = 2
let callbackWithX = callback x
B callbackWithX
The following is an example of a discriminated union -
type Person = {first:string; last:string} // define a record type
type IntOrBool = I of int | B of bool
type MixedType =
| Tup of int * int // a tuple
| P of Person // use the record type defined above
| L of int list // a list of ints
| U of IntOrBool // use the union type defined above
This comes in very handy in handling illegal state transitions of a component. Consider the following:
type State = {
fruit: list<string>
isInProgress: bool
maybeErrorMessage: option<string>
}
if (!state.maybeErrorMessage) {
if (state.isInProgress) {
<Spinner/>
}
else {
for (currFruit in state.fruit) {
<Fruit value=currFruit/>
}
}
}
else {
<Error message=state.maybeErrorMessage.get/>
}