name: inverse layout: true class: underscore --- class: center, middle, hero .title[ # Why do functional programmers always talk about algebra(s)? ## Adam Rosien, [@arosien](http://twitter.com/arosien) ## [Scala.IO 2016](http://scala.io/) [](http://underscore.io/) ] ??? Hi, and welcome to my talk. My name is Adam Rosien and I'm a consultant at Underscore, a Scala specialist consultancy. Toggle speaker view by pressing P Pressing C clones the slideshow view, which is the view to put on the projector if you're using speaker view. Press ? to toggle keyboard commands help. --- class: center wide # .spring[.remark-code[a + 2b + 12]] ??? Algebras... what are they? Here's a kind of algebraic expression familiar from school. -- # .lemon[.remark-code[(p & q) | r]] ??? Here's another that we use all the time in programming: the boolean algebra. -- # .maraschino[█ + █ = ██] ??? What about... Lego(tm)? We put a block together with another block, and what do we get? Another Lego(tm). -- # .sky[.smaller-code[.remark-code[path((0 to 3) map (i => ◜ ↻ (i*90)) = ○]]] ??? How about figure drawing? We can rotate an arc 4 times by 90°, connect them as a path, and we can draw a circle. -- # .smaller-code[.remark-code[case class Address(street: Street, state: State)]] ??? How about types? We can certainly combine types together in certain ways to form other types. -- .lavender[ .smaller-code[.remark-code[GET /repos/:owner/:repo/git/commits/:sha]]
.smaller-code[.remark-code[POST /repos/:owner/:repo/git/commits]]] ??? How about an API? Can we consider an API an algebra? Or make it one? We'll find out! --- class: center middle # 1 Structure # 2 Algebraic Data Types # 3 Exploitation # 4 Domain Algebras --- class: center middle section # 1 Structure ??? First, what are algebras, do they have some structure? --- class: center # .spring[.remark-code[a + 2b + 12]] ??? Here's our arithmetical expression again. -- # a, b, ... 0, 1, 2, ... ??? We see it consists of some variables, some constants. -- # a ⋅ b a + b ??? You can add or multiply them together. -- a ⋅ b = b ⋅ a a ⋅ 1 = a a ⋅ 0 = 0
a + b = b + a a + 0 = a
a ⋅ (b + c) = (a ⋅ b) + (a ⋅ c) ??? You might remember these rules: the order of operations doesn't matter, there's a special number 0, that when added to anything, gives you back the input, etc. --- class: center # .spring[
Algebra
] # a, b, ... 0, 1, 2, ... # a ⋅ b a + b a ⋅ b = b ⋅ a a ⋅ 1 = a a ⋅ 0 = 0
a + b = b + a a + 0 = a
a ⋅ (b + c) = (a ⋅ b) + (a ⋅ c) ??? So this gives us enough information to describe algebraic structure. An algebra... --- class: center # .spring[
Algebra
] # Symbols # a ⋅ b a + b a ⋅ b = b ⋅ a a ⋅ 1 = a a ⋅ 0 = 0
a + b = b + a a + 0 = a
a ⋅ (b + c) = (a ⋅ b) + (a ⋅ c) ??? has symbols --- class: center # .spring[
Algebra
] # Symbols # Operations a ⋅ b = b ⋅ a a ⋅ 1 = a a ⋅ 0 = 0
a + b = b + a a + 0 = a
a ⋅ (b + c) = (a ⋅ b) + (a ⋅ c) ??? operations --- class: center # .spring[
Algebra
] # Symbols # Operations # Laws ??? and laws. --- class: center # .spring[
Algebra
] # .tin[Symbols:] constants, variables # .tin[Operations:] a ⋅ b a + b # .tin[Laws: ] identity, symmetry, distributivity, ... ??? Every algebra has 3 parts: its symbols, the operations on those symbols, and laws that constrain what those operations can do. For example, in arithmetic, the symbols are our variables. We operate on those variables using addition and multiplication, and we have to maintain certain invariants like adding 0 to anything gives you back your input. There can be higher order constraints, like the distributive law, where the product of a sum is equal to the sum of the products (with the multiplication factor *distributed* to the sums). --- class: center middle section # 2 Algebraic Data Types --- class: center middle #
Algebraic Data Types
# Symbols # Operations # Laws ??? Algebraic data types... they must be some kind of algebra. We know what makes up an algebra now: symbols, operations, and laws. --- class: center middle #
Algebraic Data Types
# .tin[Symbols: ] A, B, ... # Operations # Laws ??? We know what the symbols of this algebra are, they are the names of our types. --- class: center middle #
Algebraic Data Types
# .tin[Symbols: ] A, B, ... # .tin[Operations: ] "make new types" # .tin[Laws: ] compiler does it? ??? But what about the operations? We know we want to "make new types". You may already have ideas about what operations we can perform. And the laws? Maybe the compiler has them? Or, what are they? --- class: center # a ⋅ b ??? It turns out that the algebra of types is equivalent to the algebra of arithmetic. So we'll build our algebra of types starting with the same operations, and laws, as we did before. Here we have a product: a times b. -- ``` (A, B) ``` ??? In types, that's just a tuple. -- ``` (A, B) ≅ (B, A) (A, (B, C)) ≅ ((A, B), C) ``` ??? (symmetry law, associativity law) Note that I'm not using = here, I'm using ≅ to mean "equivalent": both order of components in the tuples contain equivalent information. Same for nesting of tuples. --- class: center # a ⋅ 1 = a ??? What about multiplication by 1? We know this is true for arithmetic. -- ``` (A, Unit) ≅ A ``` ??? It's the same for products for types. 1 is the type Unit. identity law (products) --- class: center # a ⋅ 0 = 0 ??? What about multiplication by 0? -- ``` (A, Nothing) ≅ Nothing ``` ??? 0 in types is Nothing, the type with *no* values. So anything paired with a value that can't exist, that can't exist either! --- class: center # a + b -- ``` Either[A, B] ``` -- ``` Either[A, B] ≅ Either[B, A] Either[A, Either[B, C]] ≅ Either[Either[A, B], C] ``` ??? symmetry law associativity law --- class: center # a + 0 = a -- ``` Either[A, Nothing] ≅ A ``` ??? identity law (coproducts) --- class: center # a ⋅ (b + c) = (a ⋅ b) + (a ⋅ c) -- ```scala (A, Either[B, C]) ≅ Either[(A, B), (A, C)] ``` ??? distributive law --- class: center # 1 + 1 = 2 -- ``` Either[Unit, Unit] ≅ Boolean ``` --- class: center # 1 + a -- ``` Either[Unit, A] ≅ Option[A] ``` --- class: center # a + a = 2 ⋅ a -- ``` Either[A, A] ≅ (Boolean, A) ``` --- class: center middle #
Algebraic Data Types
# Symbols # Operations # Laws --- class: center middle #
Algebraic Data Types
# .tin[Symbols:] Type symbols # Operations # Laws --- class: center middle #
Algebraic Data Types
# .tin[Symbols:] Type symbols # .tin[Operations:] product, sum # Laws ??? TODO: product ~ case class TODO: sum ~ sealed trait hierarchy --- class: center middle #
Algebraic Data Types
# .tin[Symbols:] Type symbols # .tin[Operations:] product, sum # .tin[Laws:] "it's just a semiring" --- class: center middle section # 3 Exploitation ??? "exploiting algebraic structure" a.k.a "what's the point?" --- class: center middle # Symbols # Operations # Laws --- class: center middle # .tin[Symbols] # .tin[Operations] # Laws ??? Exploitation of our algebraic structure comes from the laws. --- class: center middle wide smaller-code remark-code |(M, ⊕, 0
M
)
(N, ⊗, 0
N
)|h|h(0
M
) = 0
N
h(a ⊕ b) = h(a) ⊗ h(b)| |---|---|---| ||||| ??? We're going to exploit the distributive law. There's some math here. DON'T PANIC. You got this. --- class: center middle wide smaller-code remark-code |(M, ⊕, 0
M
)
(N, ⊗, 0
N
)|h|h(0
M
) = 0
N
h(a ⊕ b) = h(a) ⊗ h(b)| |---|---|---| |(ℬ, ∧, true)
(ℬ, ∨, false)| | | --- class: center middle wide smaller-code remark-code |(M, ⊕, 0
M
)
(N, ⊗, 0
N
)|h|h(0
M
) = 0
N
h(a ⊕ b) = h(a) ⊗ h(b)| |---|---|---| |(ℬ, ∧, true)
(ℬ, ∨, false)|!| | --- class: center middle wide smaller-code remark-code |(M, ⊕, 0
M
)
(N, ⊗, 0
N
)|h|h(0
M
) = 0
N
h(a ⊕ b) = h(a) ⊗ h(b)| |---|---|---| |(ℬ, ∧, true)
(ℬ, ∨, false)|!|!true = false
!(a ∧ b) = !a ∨ !b| --- class: center middle wide smaller-code remark-code |(M, ⊕, 0
M
)
(N, ⊗, 0
N
)|h|h(0
M
) = 0
N
h(a ⊕ b) = h(a) ⊗ h(b)| |---|---|---| |(ℬ, ∧, true)
(ℬ, ∨, false)|!|!true = false
!(a ∧ b) = !a ∨ !b| |(ℬ, ∨, false)
(ℬ, ∧, true)|!|!false = true
!(a ∨ b) = !a ∧ !b| ??? But the reverse case is also true! These are DeMorgan's laws. --- class: center middle wide smaller-code remark-code |(M, ⊕, 0
M
)
(N, ⊗, 0
N
)|h|h(0
M
) = 0
N
h(a ⊕ b) = h(a) ⊗ h(b)| |---|---|---| |(ℝ, +, 0)
(ℝ, ⋅, 1)| | | --- class: center middle wide smaller-code remark-code |(M, ⊕, 0
M
)
(N, ⊗, 0
N
)|h|h(0
M
) = 0
N
h(a ⊕ b) = h(a) ⊗ h(b)| |---|---|---| |(ℝ, +, 0)
(ℝ, ⋅, 1)|e
x
| | --- class: center middle wide smaller-code remark-code |(M, ⊕, 0
M
)
(N, ⊗, 0
N
)|h|h(0
M
) = 0
N
h(a ⊕ b) = h(a) ⊗ h(b)| |---|---|---| |(ℝ, +, 0)
(ℝ, ⋅, 1)|e
x
|e
0
= 1
e
a + b
= e
a
⋅ e
b
| --- class: center middle wide smaller-code remark-code |(M, ⊕, 0
M
)
(N, ⊗, 0
N
)|h|h(0
M
) = 0
N
h(a ⊕ b) = h(a) ⊗ h(b)| |---|---|---| |(ℝ, +, 0)
(ℝ, ⋅, 1)|e
x
|e
0
= 1
e
a + b
= e
a
⋅ e
b
e
2a
= e
a
⋅ e
a
| --- class: center middle wide smaller-code remark-code |(M, ⊕, 0
M
)
(N, ⊗, 0
N
)|h|h(0
M
) = 0
N
h(a ⊕ b) = h(a) ⊗ h(b)| |---|---|---| |(≤, ∘
≤
, ε)
(→, ∘, id)|sort
≤
|sort
ε
= id
sort
a ∘
≤
b
= sort
a
∘ sort
b
| .tin[ ≤ = comparators (`A => Boolean`) ∘
≤
= composition of comparators ε = always true comparator ] --- class: center middle wide smaller-code remark-code |(M, ⊕, 0
M
)
(N, ⊗, 0
N
)|h|h(0
M
) = 0
N
h(a ⊕ b) = h(a) ⊗ h(b)| |---|---|---| |(L, ++, Nil)
(L
s
, ⊙, Nil)|sort|sort(Nil) = Nil
sort(xs ++ ys) =
sort(xs) ⊙ sort(ys)| .tin[ L = lists L
s
= sorted lists ++ = list concatenation ⊙ = "merge" ] --- class: smaller-code ``` trait Functor[F[_]] { def map[A](fa: F[A])(f: A => B): F[B] } ``` -- ``` trait FunctorLaw[F[_]] { def F: Functor[F] def composeLaw[A, B, C]( fa: F[A], f: A => B, g: B => C) = F.map(F.map(fa)(f), g) == F.map(fa)(f andThen g) } ``` --- class: smaller-code ``` trait RDD[A] { def compute(): Iterator[A] // Functor! def map[B](f: A => B): RDD[B] = ??? } ``` --- class: smaller-code ``` trait RDD[A] { def compute(): Iterator[A] // Functor! def map[B](f: A => B): RDD[B] = MappedRdd(this, f) } ``` -- ``` case class MappedRDD[A, B]( parent: RDD[A], f: A => B) extends RDD[B] { def compute(): Iterator[B] = parent.compute map f override def map[C](g: B => C): RDD[C] = copy(f = f andThen g) } ``` --- class: center # Other exploitations -- map-reduce -- database query optimization: predicate push-down, etc. -- conflict-free replicated data types (CRDTs) -- fold-fusion ("Banana-split law") -- graph and dataflow algorithms via closed semirings --- class: center #
(Exploiting) Algebra
-- # Symbols # Operations # Laws --- class: center # .tin[
(Exploiting) Algebra
] # .tin[Symbols] # .tin[Operations] # Laws -- Transform code to the "better" side of an identity Enable code transformation via reification (code → data) --- class: center middle section # 4 Domain Algebras ??? So we've talked about very *mathy* things so far: high-school algebra, laws like associativity and distributivity, etc. But we can apply these ideas, through the structure of an algebra, to our *domains*. Domains are the "business" stuff of our applications, quite often fuzzy, ill-defined, or inconsistent in known (or unknown!) ways. (Much of this section was derived from Debasish Ghosh's talks and book "Functional and Reactive Domain Modeling", which I highly recommend.) --- class: center middle what's a domain --- class: center middle # .lavender[
Domain Algebra
] # Symbols # Operations # Laws --- class: center middle # .lavender[
Domain Algebra
] # .tin[Symbols:] entities # Operations # Laws --- class: center middle # .lavender[
Domain Algebra
] # .tin[Symbols:] entities # .tin[Operations:] behaviors # Laws --- class: center middle # .lavender[
Domain Algebra
] # .tin[Symbols:] entities # .tin[Operations:] behaviors # .tin[Laws:] business rules --- class: center middle .lavender[ .smaller-code[.remark-code[GET /repos/:owner/:repo/git/commits/:sha
POST /repos/:owner/:repo/git/commits]]]
# .tin[Symbols: entities] # .tin[Operations: behaviors] # .tin[Laws: business rules] --- class: center middle .lavender[ .smaller-code[.remark-code[GET /repos/.snow[:owner]/.snow[:repo]/git/commits/.snow[:sha]
POST /repos/.snow[:owner]/.snow[:repo]/git/.snow[commits]]]]
# .tin[Symbols:] .snow[entities] # .tin[Operations: behaviors] # .tin[Laws: business rules] --- class: center middle .lavender[ .smaller-code[.remark-code[.snow[GET] /repos/:owner/:repo/git/.snow[commits]/:sha
.snow[POST] /repos/:owner/:repo/git/.snow[commits]]]]
# .tin[Symbols: entities] # .tin[Operations:] .snow[behaviors] # .tin[Laws: business rules] --- class: center middle .lavender[ .smaller-code[.remark-code[GET /repos/:owner/:repo/git/commits/:sha
POST /repos/:owner/:repo/git/commits]]]
# .tin[Symbols: entities] # .tin[Operations: behaviors] # .tin[Laws:] .snow[business rules ???] --- class: middle smaller-code .center[.lavender[ .smaller-code[.remark-code[GET /repos/:owner/:repo/git/commits/:sha
POST /repos/:owner/:repo/git/commits]]]]
``` trait Github[Owner, Repo, Commit, SHA] { def get: Owner => Repo => SHA => Commit def create: Owner => Repo => Commit => SHA } ``` --- class: middle smaller-code ``` trait Github[Owner, Repo, Commit, SHA] { type Response[A] = Either[Fail, A] sealed trait Fail case class CommitExists( owner: Owner, repo: Repo, sha: SHA) extends Fail def get: Owner => Repo => SHA => Option[Commit] def create: Owner => Repo => Commit => Response[SHA] } ``` --- class: middle smaller-code ``` trait Github[Owner, Repo, Commit, SHA] { type Response[A] = Either[Fail, A] sealed trait Fail case class CommitExists( owner: Owner, repo: Repo, sha: SHA) extends Fail def get(o: Owner, r: Repo, sha: SHA): Option[Commit] def create(o: Owner, r: Repo, c: Commit): Response[SHA] } ``` --- class: middle smaller-code wide ``` trait GithubLaws[Owner, Repo, Commit, SHA] { def api: Github[Owner, Repo, Commit, SHA] def createLaw(o: Owner, r: Repo, c: Commit) = api.create(o, r, c) match { case Right(sha) => sha == c.sha && api.get(o, r, sha).isDefined case Left(CommitExists(fo, fr, sha)) => fo == o && fr == r && api.get(o, r, sha).isDefined } } } ``` --- class: center middle # .lavender[
Domain Algebra
] # Symbols # Operations # Laws --- class: center middle # .lavender[
Domain Algebra
] # .tin[Symbols: ] abstract types # Operations # Laws --- class: center middle wide # .lavender[
Domain Algebra
] # .tin[Symbols: ] abstract types # .tin[Operations: ] (effectful) functions # Laws --- class: center middle wide # .lavender[
Domain Algebra
] # .tin[Symbols: ] abstract types # .tin[Operations: ] (effectful) functions # .tin[Laws: ] property-based tests --- class: center middle section # ⅀ Summary --- class: center #
Algebraic structure
## Symbols, Operations, Laws -- #
Don't repeat yourself / Be lazy
## so much structure is already well-understood and applicable ## build new algebras from other algebras --- class: center wide #
Exploitation
## use the identities from the laws ## use (code ↔ data) transformations to apply identities -- #
Domain algebras
## "messy" business stuff can be algebraic ## abstract with verifiable laws --- class: center, middle, hero .title[ # Thank You! ## Adam Rosien, [@arosien](http://twitter.com/arosien) ### http://underscore.io [](http://underscore.io/) ] --- # References - [Origin of the word 'algebra'](http://www.und.edu/instruct/lgeller/algebra.html) - [The Algebra of Algebraic Data Types](http://chris-taylor.github.io/blog/2013/02/10/the-algebra-of-algebraic-data-types/) by Chris Taylor - [Functional Patterns: Monoid](http://philipnilsson.github.io/Badness10k/posts/2016-07-21-functional-patterns-monoid.html) by Philop Nilsson - [An Algebraic Approach to Functional Domain Modeling](http://www.slideshare.net/debasishg/an-algebraic-approach-to-functional-domain-modeling) by Debasish Ghosh # See also, i.e., explode your brain: - F-algebras - Object algebras - "Finally tagless" encoding - [Fun with Semirings](https://www.cl.cam.ac.uk/~sd601/papers/semirings.pdf) --- class: middle center section # ⊕ Bonus slides --- class: center # b
a -- ``` A => B ``` --- class: center # 1
a
= 1 -- ``` A => Unit ≅ Unit ``` --- class: center # a
1
= a -- ``` Unit => A ≅ A ``` --- class: center # (b ⋅ c)
a
= b
a
⋅ c
a
-- ``` A => (B, C) ≅ (A => B, A => C) ``` --- class: center # c
ab
= (c
a
)
b
-- ```scala (A, B) => C ≅ A => B => C ``` --- class: center # L(a) = 1 + a ⋅ L(a) -- = 1 + a ⋅ (1 + a ⋅ L(a)) -- = 1 + a + a
2
⋅ L(a) -- = 1 + a + a
2
⋅ (1 + a ⋅ L(a)) -- = 1 + a + a
2
+ a
3
⋅ L(a) -- ... = 1 + a + a
2
+ a
3
+ a
4
+ ... --- class: wide smaller-code center # L(a) = 1 + a ⋅ L(a) -- ``` type L[A] = Either[Unit, (A, L[A])] ``` -- ``` sealed trait List[+A] case object Nil extends List[Nothing] case class Cons[A](head: A, tail: List[A]) extends List[A] ```