(Mostly) everything is functions all the way down. If it's more than a unary function i.e. (a, b) => a + b
, they should be curried.
Good functions are pure functions — pure functions are deterministic, meaning that with the same inputs they return the same outputs, they have no side-effects, meaning they do not make change external to the body of the function and they are referentially transparent so if you know the inputs you can compare them transparently with their outputs.
When choosing an order for parameters in a curried function, in general it makes most sense to order them by order-of-likely availability, with the last parameter as the one which is the primary subject of the function going into the pipe.
Prefer function composition and transformation via higher-order functions over inline function complexity. (See more examples in the glossary)
Avoid mutating or destructing data. Instead, make a copy of the data, and do whatever you want to that. This prevents hard-to-track errors and adheres to creating no side-effects.
Prefer map
over other loops. Discrete iterable values are pure. For instance, a for
loop which conditionally pushes into an array, especially an array (potentially accessible from a shared context which could also then mutate it).
Functions should cleave towards being as generic as possible. This aids both readability and logical reasoning.
There's a form of FP which is called tacit or point-free which attempts to eschew defining parameters where possible, instead relying upon explicit function arity. This is not mandatory to be writing functional code. However, as it is often less code to write and helps reinforce the idea that everything is a function (often a composed one). An example of a tacit function:
pipe(slice(0, 3), toUpper)
(For a lot more, you could check out our references page or see the wiki page on category theory)