In my journey of learning F#, I have come across the concept of "everything is an expression" which was an eye-opening experience for me. Now before I go into more details, let's just clear up the difference between an expression and a statement.
I like to think of an expression as anything that evaluates to a value. This includes arithmetic operations, ternary operators, function results, etc.
// arithmetic expression that evalues to 36
5 * 6
// function call that evalues to 120
factorial(5)
// ternary operator that evalues to 9
isEven(11) ? 8 : 9
Notice how all of these evaluate to a value that could be stored in a variable. Now let's look at some examples of a statement.
// assignment
let b = 7
// branching
if (x % 7) {
// some code goes here
} else {
// some code goes here
}
// loops
while (true) { /* some code */ }
for (let i = 0; i < 10; i++) { /* some code */ }
let's say you are designing a new language and you don't like this arbitrary distinction between expressions and statements, what can we do to fix this without making the language unusable?
We can start by combining if-statements and ternary expressions into one by making if-statements return the last value in their bodies.
let a = if (b > 10) {
/* some code */
5
} else {
/* some code */
3
}
You can refer to Binding and continuations in ML for why blocks expressions evaluate to the last value in their bodies.
What about while/for loops? their functionality can be easily replicated by recursion so let's get rid of them. For example, the following:
let i = 0;
while (i < 10) {
print("hello world")
i++
}
could be converted to use recursion like this:
let loop = (i, end) => {
if (i < end) {
print("hello world")
loop(i + 1, end)
}
}
loop(0, 10)
This leaves us with let-statements. in ML languages, we have let-in expressions which is inspired by Mathematics
(* this is the same as (5 * 5) + 1 *)
let x = 5 in (x * x) + 1
Notice that in our previous example, we are not assigning to a variable. Instead, we say that x
will stand for the value 5
in the expression after the in
keyword.
Since let-statements take expression after the in
keyword and since they are themselves expressions, we can do something like this:
let x = 5 in (let y = 6 in x + y)
if we remove the parenthesis and do a little bit of formatting, we would end up with something like this:
let x = 5 in
let y = 6 in
x + y
So why all of this is important? Well, I think grokking the concept "everything as an expression" is vital to understanding why pure functional programming languages tend to avoid mutations and side effects.
Top comments (0)