-
Immutability
-
(as much as possible)
-
Purity
-
(as much as possible)
-
Composability
-
(making lots of small good things and combining them instead of big things)
That's because React has been slowly smuggling FP ideas into the mainstream for years
-
pure functions (like, you know, render)
-
separating effects from pure code (hooks)
-
state machines and stuff (redux, xstate, blah)
So pretty boring, basically.
So, yes, and no.
-
There is a lot of cross over between formal maths and the more academic corners of functional programming.
-
Dependently-typed programming languages like Idris or Agda can be used as mathematical proof solvers
-
We can enjoy all the great bits without caring about any of that if we don't want to.
Spot the pure function:
let horses = []
const goodHorseNames = ['SNOWBALL','NAPOLEON','SPIRIT']
const functionOne = (name: string) => {
if (goodHorseNames.contains(name)) {
horses.push(name)
}
}
const functionTwo = (horses: string[], name: string) => {
if (goodHorseNames.contains(name)) {
return [...horses, name]
}
return horses
}
const functionThree = (name: string) => {
console.log(`checking for ${name}`)
return (goodHorseNames.contains(name))
}
- Then bad news
const functionOne = (name: string) => {
if (goodHorseNames.contains(name)) {
horses.push(name) // bad function
}
}
Mutating an external variable is a side effect and NOT PURE
- Then great job
const functionTwo = (horses: string[], name: string) => {
if (goodHorseNames.contains(name)) {
return [...horses, name]
}
return horses
}
Although this uses an external var, goodHorseNames
, it doesn't mess with it,
so it's a totally sick and good function.
- (Although somebody else could still mutate
goodHorseNames
externally and we'd lose referential transparency, but we're doing our best here, it's Javascript ffs)
- Then kinda OK job
const functionThree = (name: string) => {
console.log(`checking for ${name}`) // badness
return (goodHorseNames.contains(name))
}
Although it doesn't mess with any variables or do anything seriously bad, logging is still a side effect. You think you don't care now until it makes your test output look like messy garbage.
Now hang on.
There is one more thing that divides the FP community bitterly.
-
Are they totally sick?
-
Or are they just slowing down my 10x-ing?
-
If you are happy gluing lots of small
-
and sometimes rather abstract bits of code together
-
without a compiler to tell you whether you're messing it up
-
then you are braver than I.
- What does this function do?
const urgh = (key: string, items: Record<string, number>): number | undefined => {
// ...?
}
- Or better still, what's the only sane thing that this function could possibly do?
const blah = <A>(a: A): A => {
// ...?
}
- Or this one?
const bleugh = <A,B>(a: A, b: B): A => {
// ...?
}
When talking about the type signatures of functions, we can use Hindley-Milner
notation.
urgh :: String -> Record<string, number> -> (number | undefined)
blah :: A -> A
bleugh :: A -> B -> A
We won't always do this, but sometimes it's less noisy than a Typescript
functional signature.
- The docs for
Ramda
have a great guide for these type signatures.