The very central concept in functional programming is an expression.
Expression is a piece of code that can be evaluated, which is to say that it can be used to produce a value. In functional programming, everything is an expression and everything produces or returns a value.
There are two types of expressions:
- plain values
- function calls
Plain values are the elementary expressions, e.g. in JavaScript.
2
true
"hello"
{sugar: 2, coffee: 1, water: 0.5, milk: 0.5}
Function calls are complex expressions that receive other expressions as parameters, produce a result based on them and then return it as another value.
makeEspresso(2, true);
//=> {sugar: 2, coffee: 1, water: 0.5, milk: 0.5}
In this example we supply the function with two values as parameters that specify coffee options and the function gets evaluated to the value that represents the coffee cup.
We can represent the relationship between elementary values and functions with an obligatory UML class diagram.
See the recursive link from function back to expression - that’s because function parameters may be either plain values or nested function calls. Basically, an expression has a tree structure, so let’s also draw it as a tree.
Expressions are practically trees, where children are parameters to the sub-expressions (i.e. functions) and plain values are leaves.
Note that in many languages you can build complex expressions not only with functions, but also with operators. JavaScript is one of those languages, e.g. if you wanted to get a sum of two numbers you’d do this:
5 + 4
//=> 9
You supply two numerical values, put the +
operator between them and the whole expression gets evaluated to the
sum of those numbers. Although operator expressions are not necessarilly implemented in the same technical way as
actual functions are, logically they serve the same purpose. The main difference is in notation. JavaScript
functions use prefix notation - function name goes first, then parameters. Operators, on the other hand use
infix notation - parameter, operator, then the other parameter.
Some languages, like Clojure and other Lisps, make no such distinction and exclusively use prefix notation, even where other languages would have used infix operators:
;clojure
(+ 5 4)
;=> 9
In other languages, like Haskell, operators are just functions with two parameters whose names consist of symbols rather than alphanumeric characters, and may be used in either infix or prefix notation.
--haskell
--infix operators
4 + 5
--=> 9
--prefix operators
(+) 4 5
--=> 9
Likewise, Haskell functions with alphanumeric names are usually called with prefix notation, but can also be called infix-style using a special syntax:
--haskell
--prefix functions
mod 3 2
--=> 1
-- infix function
3 `mod` 2
--=> 1
We can also use the value of an evaluated expression as the input parameter of another expression, e.g. another JavaScript infix operator expression or a function:
10 - (5 + 4)
//=> 1
makeEspresso(10 - (5 + 4), true)
//=> {sugar: 1, coffee: 1, water: 0.5, milk: 0.5}
We’ve got a handle of the two central concepts of functional programming. However, it does not end there - there are also some constraints required for all the magic to happen. We’ll show that, whenever possible, functions have to be pure and values have to be immutable. So, let’s demystify that!