Properties creditable for cleanliness and readability

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:

Currying:

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

Discriminated union:

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/>
}