F#'s Condtional Expressions require a condition to check, a branch for true, and a branch for false. For example:
let x = if ("hello" = null) then true else false //error if else branch missing
However, something gets weird when
(), is involved.
let y = if ("hello" = null) then raise <| new ArgumentNullException() else () //happy with or without else branch
And more simply:
let z = if ("hello" = null) then () else () //happy with or without else branch
Why isn't an
else branch required when
unit is returned?
Consider the following code:
let f a = if a > 5 then true
If you call
f 10, it returns
Now, ask yourself this: what should
f 2 return? I know you're going to say
false, but how does the compiler know that? I mean, it's just as likely that you meant it to return
true in both cases, isn't it? Or even, perhaps, crash in the
a <= 5 case, who knows?
So in order for the program to be "complete" (i.e. contain instructions for what to do in every situation), you always have to specify an
unit, however, is special.
unit means that there is no meaningful return value. Essentially
unit stands for side-effect: it means that the thing that returned it was meant to produce some effect in the external world. Since F# is not a pure language, such
unit-returning things are quite ubiquitous. For example, debug logging:
let f x = if x < 42 then printfn "Something fishy, x = %d" x x + 5
With such statements, there is no ambiguity: it's always known that the
else branch is meant to return
() as well. After all, there are no other values of
unit, are there? At the same time, always adding
else () at the end would be very tiresome and obfuscating. So, in the interest of usability, the compiler doesn't require an
else branch in this specific case.