Adam Rosien @arosien
Inner Product LLC inner-product.com
27 Apr 2019
A => B
i eat dependencies for breakfast
Extract Class
( ಠ ʖ̯ ಠ)
Push the dependency “later”
val getThenSave: Db => Unit =
db =>
saveUser(
getUser(uid)(db))( // <-- *same* db here
db) // <-- and here
Write a helper
We need our dependencies.
In OOP we can share them via scope (class). Composition is just function composition.
In FP, we want composition for everything: monadic composition for dependency sharing, function composition for logic.
Recursion is known to cause headaches.
Make it the caller’s problem
ಠ‿ಠ
Curry
(Int => Int) => (Int => Int)
function, I want a Int => Int
function!Write a helper to do that.
(⊙_◎)
Very clever.
Locally, recursion itself can be an extraneous detail.
Factor it out, use a helper to put it back in.
(convert explicit recursion to continuation passing)
That helper can now do more than just recurse!
It can cache (memoize). It can do X.
And your code doesn’t know about any of it.
sealed trait BoolF[A]
object BoolF {
case class True[A]() extends BoolF[A]
case class False[A]() extends BoolF[A]
case class And[A](l: A, r: A) extends BoolF[A]
case class Or[A](l: A, r: A) extends BoolF[A]
case class Not[A](b: A) extends BoolF[A]
}
Recursion is not my data’s problem anymore!
with some helpers to fix type inference…
BoolF.not(
BoolF.and(
BoolF.`true`,
BoolF.`false`))
// res20: BoolF[BoolF[BoolF[Nothing]]] = Not(And(True(), False()))
Problem: BoolF[BoolF[BoolF[BoolF[...
Put the recursion in helper data
def eval2(b: BoolF[String]): String =
b match {
case BoolF.True() => "true"
case BoolF.False() => "false"
case BoolF.And(l, r) => s"($l && $r)"
case BoolF.Or(l, r) => s"($l || $r)"
case BoolF.Not(b) => s"!$b"
}
No recursion in our interpreter!
No recursion in our data.
No recursion in our interpreter(s).
Recursion Schemes encapsulate recursive traversals.
Do less in your functions: externalize dependencies.
(but not with scope, please!)
Provide them later:
via function parameters
en masse with the Reader Monad
Do less in your functions: receive the thing that does the recursion.
(recursion-as-a-dependency, recursion-as-a-service)
The Y-combinator is the basic technique.
You can then have this passed-in capability do more, without changing your core logic.
Simplify your modeling primitives (algebras).
Simplify your interpreters.
Recursion Schemes glue it all together and provide flexible processing.
When you have a local problem…
make it the caller’s problem.
Then hide that problem from the caller’s caller.
These are functional programming abstractions.
They come from refactoring.
Adam Rosien @arosien
Inner Product LLC inner-product.com
Hire us to teach your team! ☝︎