This document discusses referential transparency and how embracing functional programming principles can improve code quality. Referential transparency means that replacing an expression with one that evaluates to the same value does not change the program. This allows for optimizations like memoization and parallelization. The document advocates using monads to model effects as first-class values in a pure language in order to separate pure and effectful code in a type-safe way.
11. - Testability! Pure functions are extremely easy to test
- Know when and where our effects occur.
- Both you and the compiler now have guarantees that your code can be
reasoned with, this makes refactoring a walk in the park.
Why is this useful?
12. - Memoization; If we can guarantee a function to always return the same
thing given the same input, the compiler can easily memoize the result.
- Parallelization; Without sharing state, we can easily run multiple functions in
parallel without having to worry about deadlocks or data races.
- Common Subexpression Elimination; The compiler can know exactly when
multiple expressions evaluate to the same value and can optimize this away
Potential optimizations
17. async impure def launchNukes(): Unit = {
... //call impure code here
}
async impure def main(): Unit = {
sum(List(1,2,3))
await launchNukes()
}
Async annotations!
18. We’d need to add language feature
for every different kind of effect
19. Effects shouldn’t be “to the side”, they
should be first class values that we can
supply to our runtime.
Realization:
20. type Effect = () => Unit
type Effects = List[SideEffect]
def main(): Effects = List(
() => println(“Launching Nukes”),
() => launchNukes()
)
How should we model our effects?
21. type Effects = List[Any => Any]
def performSideEffects(effs: Effects): Unit = {
effs.foldLeft(())((acc, cur) => cur(acc))
}
def main(): Effects = List(
_ => localStorage.getItem("foo"),
foo => println(foo),
_ => new Date().getFullYear(),
year => println(year) //no access to foo here
)
Hmmmm...