Make Expressions, not Statements
Tags: programming, practices, functional
Published: 2023-08-17 23:42:49

I was chatting to a colleague about Haskell the other day and some of the qualities it has, such as everything being an expression and how that can encourage the use of smaller functions.
The drive to use expressions over statements is something that I've advocated for for quite a while at work as I think it has a strong set of benefits over merely making statements.  Although not all languages make this a first class citizen, it's still usually possible to do.

[TL;DR](#Summary) at the bottom

I'll use a couple of languages, but let's start with Typescript.  Say we want to get the age bracket of an individual, based on their age range.  We might do something like:
```typescript
var ageBracket: string

if (age <= 12) ageBracket = "junior"
else if (age >= 19) ageBracket = "teen"
else ageBracket = "adult"
```

So what's could we say is a problem with that?  We could wrap the logic up in a function and avoid the mutability, but I think the aspect that we're still dealing with statements remains.  A statement is a bit of code that does not return a value and so can't be chained very easily.  An expression on the other hand returns some value and can easily be built into a larger expression to achieve something more complex.
Scala is a more expression based language and so allows us to do things like:
```scala
val ageBracket = if (age <= 12) "junior"
  else if (age >= 19) "teen"
  else "adult"
```

One mode of thought is that statements describe *how* to do things, expressions state *what* to do.  Here's another example:
```typescript
const nums = [1, 1, 2, 3, 5, 8, 13]
var total = 0
for (var i = 0; i < nums.length; i++)
  total = total + nums[i]
```
It's quite low level in that is describing how to loop through an array of elements and then compute a running total.  The `for` statement also does not return anything, if we wanted to do something afterwards, we must do so with a new statement.  Compare with:
```typescript
[1, 1, 2, 3, 5, 8, 13].reduce((total, n) => total + n, 0)
```
In Scala we get something ever more pleasant:
```scala
List(1, 1, 2, 3, 4, 5, 8, 13).sum
```
or
```scala
List(1, 1, 2, 3, 4, 5, 8, 13).fold(0)(_ + _)
```

Great, so what?  Let's add a couple more example steps, again in Typescript, well filter for even numbers, double them and then get the word for the number (2 => "two"):
```typescript
const nums = [1, 1, 2, 3, 5, 8, 13, ...]
var odds = []
for (var i = 0; i < nums.length; i++)
  if (nums[i] % 2 === 0) odds.push(num[i])

var doubles = []
for (var i = 0; i < odds.length; i++)
  doubles.push(odds[i] * 2)

var names = []
for (var i = 0; i < doubles.length; i++)
  names.push(getNumberWord(doubles[i]))
```
We end up with quite a lot of intermediary variables.  If we change the functionality we need to keep track of whether we should get rid of them or not, IDEs help with this of course, but it can still be pretty easy to end up with unused code lying around.  Compare with:
```typescript
[1, 1, 2, 3, 5, 8, 13, ...]
   .filter(n => n % 2 === 0)
   .map(n => n * 2)
   .map(getNumberWord)
```

I find it to be much more *descriptive and declarative*, we talk about what it is we want the code to do and don't have to be concerned with the how.

Related to the intermediary variables point, expressions also allow us to more tightly group related bits of functionality or logic together.  To go back to the `sum` example:
```typescript
var total = 0
for (var i = 0; i < nums.length; i++)
  total = total + nums[i]
```
Here the initial value for total and indeed the logic to do the addition is free to be moved around, away from the loop implementation.  Not implicitly a bad thing but it does make code harder to reason about when it's spread apart.  What if we also forgot to initialise `total`? Depending on the language, we could get an exception or we could get behaviour we didn't expect.  Javascript isn't as well behaved, but in the Scala version 
```scala
List(1, 1, 2, 3, 4, 5, 8, 13).sum
List(1, 1, 2, 3, 4, 5, 8, 13).fold(0)(_ + _)
```
it's impossible to forget the initial `0` value for the `fold` as the compiler would yell at us and in the `sum` version it's implicit.

Obviously these are really contrived examples but generally I think they illustrate the point.  When we have statement orientated code we usually end up with less of a set of , declarative building blocks and more of a longer set of lower level instructions that are much harder to compose.

There's a great [talk](https://www.youtube.com/watch?v=xwDhKV7CqAY&t=3320s) by Paul Louth, the writer of the C# functional programming lib [language-ext](https://github.com/louthy/language-ext) where he talks about statements vs expressions that's well worth a watch., he talks about much more than I'll go into here.  I love his examples (around 25:30) of how statements are much more open the insertion of side-effecting changes in a more uncoordinated way, whereas the expression based approach forces you to be more structured and deliberate.

I'm a big fan of FP and FP languages, but lots of the patterns and practices that I think make them great can be applied to other languages too and this is definitely one of them, I hope you give it a try and see how it feels.

## Summary
In my experience, expression leaning code makes for a much nicer experience in writing and reading code.  I find it helps to create more maintainable and composable solutions to problems.

Statements 
 - describe how to do it
 - leave the code open to modification
 - intermediate variables/state
 - tend towards more mutability
 - easier to rot

Expressions
- return values
- describe what to do
- keeps related logic/functionality contained and together
- leave the code open for extension
- composable