DEV Community

Mohamed Dahir
Mohamed Dahir

Posted on

Everything as an expression

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 
Enter fullscreen mode Exit fullscreen mode

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 */ }
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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++
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)