Abstractions of Category Theory to define abstractions (Functor, Applicative, Monad, Comonad, Coyoneda) commonly used in functional programming (FP). Using definitions from Category Theory to reason about modular and composable design. Examples based on Haskell papers: Functional pearls translated to Scala.
Time Series Foundation Models - current state and future directions
Why functional programming and category theory strongly matters
1. Why Functional Programming &
Category Theory now strongly matters
Piotr Paradziński, ScalaC
@pparadzinski
https://github.com/lemastero
Lublin 2019.03.26
2. 1. Algebraic Data Types (ADT) and basic FP
2. Abstract algebra, ADT, domain modeling
3. Functional pearl: Tutorial of expressivity and universality of fold, G. Hutton
4. Category Theory #1: Category & composition in software design
5. Functor in Category Theory and as FP abstractions: Functor
6. Functional pearl: Monads for functional programming, P. Wadler
7. FP abstractions Apply, Applicative, Flatmap, Monad
8. Effects
9. FP abstractions: Coyoneda trick, Comonads
10. FP abstractions vs OOP patterns
11. Curry-Howard-Lambek Isomorphism & hint about HoTT and Cubical TT
2
3. ● ignore ChallangeX - your mind mind
could collapse to black hole
(unless u have X+ years XP in Scala FP)
● last man who know all
Johann Goethe died 1832
feel free to ask (we have 2019)
3
4. FP (Clojure, Scheme)
● functions as first class citizens
○ pass functions as parameters to other functions
○ return functions from other functions
○ store functions in variables
● prefere immutability
4
5. Pure FP
● functions as first class citizens
○ pass functions as parameters to other functions
○ return functions from other functions
○ store functions in variables
● strict immutability (don’t change/update stuff, return copy with modifications)
5
6. Pure strongly typed FP (Haskell, Scala)
● functions as first class citizens
○ pass functions as parameters to other functions
○ return functions from other functions
○ store functions in variables
● immutability (don’t change/update stuff, return copy with modifications)
● express in type effects (non repeatable operations)
○ exceptions
○ read/write from disc, network, DB, etc
○ generate random numbers
○ get current time
6
8. product type (tuple, pair) - cartesian product
case class Tuple[A, B](_1: A, _2: B)
8
9. product type (tuple, pair) - cartesian product
case class Tuple[A, B](_1: A, _2: B)
val foo = Tuple(42, "foo")
val foo2 = (42, "foo")
9
10. product type (tuple, pair) - cartesian product
case class Tuple[A, B](_1: A, _2: B)
val foo = Tuple(42, "foo")
val a42: Int = foo._1
val afoo: String = foo._2
10
11. sum type (either, xor)
sealed trait Either[A,B]
case class Left[A,B](a: A) extends Either[A,B]
case class Right[A,B](b: B) extends Either[A,B]
11
12. sum type (either, xor)
sealed trait Either[A,B]
case class Left[A,B](a: A) extends Either[A,B]
case class Right[A,B](b: B) extends Either[A,B]
val error = Left("oh boy connection lost")
val value = Right(42)
12
13. sum type (either, xor)
sealed trait Either[A,B]
case class Left[A,B](a: A) extends Either[A,B]
case class Right[A,B](b: B) extends Either[A,B]
val error = Left("oh boy connection lost")
val value = Right(42)
Left("truth") != Right("truth") // discriminated union
13
15. unit type (one, most boring type ever)
case object Unit
val boring = Unit
15
16. unit type (one, most boring type ever)
case object Unit
val boring = Unit
def foo: Unit = ??? // how many implementations?
16
17. void type (invented in Haskell to confuse Java devs)
final abstract class Void private ()
17
18. void type (invented in Haskell to confuse Java devs)
final abstract class Void private ()
val a: Void = ??? // can we implement this ?
18
19. void type (invented in Haskell to confuse Java devs)
final abstract class Void private ()
// Nothing is close enough replacement :)
// discussion about different implementations: https://github.com/scalaz/scalaz/issues/1491
19
20. ADT = Abstract Data Types
● classes with only public getters - immutable
● consist only from product and sum types
sealed trait Graph[A]
case object Empty extends Graph[Nothing]
case class Vertex[A](a: A) extends Graph[A]
case class Overlay[A](lhs: Graph[A], rhs: Graph[A]) extends Graph[A]
case class Connect[A](lhs: Graph[A], rhs: Graph[A]) extends Graph[A]
Algebraic Graphs with Class - Andrey Mokhov 2017 eprint.ncl.ac.uk/file_store/production/239461/EF82F5FE-66E3-4F64-A1AC-A366D1961738.pdf
20
22. Abstract Algebra - Monoid
trait Monoid[M] extends Semigroup[M] {
def empty: M
def combine(lhs: M, rhs: M): M
}
● associativity law: combine(a, combine(b, c)) == combine(combine(a, b), c)
● identity law: combine(a, empty) == combine(empty, a) == a
22
23. Abstract Algebra - Commutative Monoid
trait Monoid[M] extends Semigroup[M] {
def empty: M
def combine(lhs: M, rhs: M): M
}
● associativity law: combine(a, combine(b, c)) == combine(combine(a, b), c)
● identity law: combine(a, empty) == combine(empty, a) == a
● commutativity law: combine(a, b) == combine(b, a)
23
24. is product a Semigroup ?
// (A * B) * C = (A * B) * C
val isAssociative: Boolean = (("a", "b"), "c") == ("a", ("b", "c"))
24
25. product is Semigroup up to isomorphism
// (A * B) * C = (A * B) * C
def assoc1[A,B,C](abc: ((A,B), C)): (A, (B,C)) = { case((a,b), c) => (a, (b,c)) }
def assoc2[A,B,C](abc: (A, (B,C))): ((A,B), C) = { case(a, (b,c)) => ((a,b), c) }
val isAssociativeUpToIsomorphism: Boolean = (("a", "b"), "c") == assoc2(("a", ("b", "c")))
25
26. (product, Unit) is Monoid up to isomorphism
// (A * 1) = (1 * A) = A
def leftId[A](x: (A, Unit)): A = x._1
def leftId_inv[A](a: A): (A, Unit) = (a, ())
def rightId[A](x: (Unit, A)): A = x._2
def rightId_inv[A](a: A): (Unit, A) = ((), a)
26
27. (product, Unit) is Monoid up to isomorphism
// commutative
// A * B = B * A
def swap[A,B](abc: (A,B)): (B,A) = {
case(a,b) => (b, a)
}
27
28. (Unit, product) is Commutative Semigroup
associativity
(A * B) * C = (A * B) * C using def assoc((a,b),c) = (a, (b,c))
identity
A * 1 = 1 * A = A (a, ()) ((), a) ~ a
commutative
A * B = B * A (a,b) is (b,a) using swap(a,b) = (b, a)
so Abelian Semigroup ! (up to isomorphism)
28
29. Algebra of Types (Void, sum) is Abelian Semigroup
associativity
(A + B) + C = (A + B) + C Either((a,b),c) ~ Either(a, (b,c))
identity
A + 1 = 1 + A = A Either(a, Void) ~ Either(a, Void) ~ a
commutative
A + B = B + A Either(a,b) is Either(b,a)
so Abelian Semigroup ! (up to isomorphism)
29
30. Algebra of Types - distribution law and multiply 0
distribution law
A * (B + C) = (A * B) + (A * C) (a, Either(b,c)) ~ Either((a, c), (b,c))
multiply by 0
A * 0 = 0 * A = 0 (a, Void) ~ (a, Void) ~ Void
30
31. Moar !11!!! about Algebra of ADT
● School of Haskell - Sum Types - Gabriel Gonzales
○ https://www.schoolofhaskell.com/school/to-infinity-and-beyond/pick-of-the-week/sum-types
● Category Theory 5.2 Algebraic data types - Bartosz Milewski (lists)
○ https://www.youtube.com/watch?v=w1WMykh7AxA
● London Haskell - The Algebra of Algebraic Data Types - Christ Taylor (lists, trees, zipper)
○ https://www.youtube.com/watch?v=YScIPA8RbVE
● The Derivative of a Regular Type is its Type of One-Hole Contexts
○ http://strictlypositive.org/diff.pdf
31
33. ADT = Abstract Data Types
● classes with only public getters - immutable
● consist only from product and sum types
33
34. OOP model version 1
class Bar {}
class Foo {
def doMagic(bar: Bar): Int = ???
}
34
35. instance method ~ function with extra param
// OOP
class Bar {}
class Foo {
def doMagic(bar: Bar): Int = ???
}
// FP
case class Bar()
case class Foo()
def doMagic(foo: Foo, bar: Bar): Int = ???
35
36. OOP model version 2 (assigning responsibilities)
class Bar {}
class Foo {
def doMagic(bar: Bar): Int = ???
}
// no doMagic should belong to Bar not Foo
class Bar {
def doMagic2(bar: Foo): Int = ???
}
class Foo {}
36
37. OOP model 2 => FP model
// OOP
class Bar {
def doMagic2(bar: Foo): Int = ???
}
class Foo {}
// FP
case class Bar()
case class Foo()
def doMagic2(bar: Bar, foo: Foo): Int = ???
37
38. FP model 1, model 2
case class Bar()
case class Foo()
// model 1
def doMagic(foo: Foo, bar: Bar): Int = ???
// model 2
def doMagic2(bar: Bar, foo: Foo): Int = ???
38
39. state without methods allow you to change your
mind about responsibility, without touching model
case class Bar()
case class Foo()
// model 1
def doMagic(foo: Foo, bar: Bar): Int = ???
// model 2
def doMagic2(bar: Bar, foo: Foo): Int = doMagic(foo, bar)
39
40. discovered in 1975 in Scheme
● in context of actor model (and message passing is ~ calling object method) and running functions (~ lambda
calculus): https://en.wikisource.org/wiki/Scheme:_An_Interpreter_for_Extended_Lambda_Calculus/Whole_text 40
41. OOP modeling vs FP - expression problem
● in OOP you cannot solve expression problem, otherwise than using Visitor
pattern
● in FP one can use type class to solve it
● Tony Morris - The Expression Problem and Lenses ( next time :) )
○ https://www.youtube.com/watch?v=UXLZAynA0sY
● Hacker News - The Expression Problem and its solutions
○ https://news.ycombinator.com/item?id=11683379
41
42. FP in Scala Lists and superpowers of fold
http://www.cs.nott.ac.uk/~pszgmh/fold.pdf 42
43. Graham Hutton - A tutorial on the universality and
expressiveness of fold
fold - gives us superpowers
● writing proofs for functions on List without using induction
● define how to refactor list functions using fold
fold has luxury houses:
● universal property
● fusion property
fold is in general badass (great expressive power)
43
44. FP lists - definition
sealed trait List[A]
case object Nil extends List[Nothing]
case class Cons[A](h: A, t: List[A]) extends List[A]
// Haskell
data List t = Nil | Cons :: List t
44
45. FP lists - sum
def sum(xs: List[Int]): Int = {
xs match {
case Nil => 0
case Cons(head, tail) => head + sum(tail)
}
}
45
46. FP lists - product
def product(xs: List[Int]): Int = {
xs match {
case Nil => 1
case Cons(head, tail) => head * product(tail)
}
}
46
47. FP lists - refactor common parts into - fold
def fold[A,B](f: (A,B) => B, init: B, xs: List[A]): B = {
xs match {
case Nil => init
case Cons(head, tail) => f(head, fold(f, init, tail))
}
}
47
48. FP lists - fold
def fold[A,B](f: (A,B) => B, init: B, xs: List[A]): B = {
xs match {
case Nil => init
case Cons(head, tail) => f(head, fold(f, init, tail))
}
}
def sum(xs: List[Int]): Int = fold[Int, Int](_ + _, 0, xs)
def product(xs: List[Int]): Int = fold[Int, Int](_ * _, 1, xs)
48
49. FP - fold universal property - those are equivalent
def g(xs: List[B]): A = xs match {
case Nil => v
case Cons(h,t) => f(h,g(t))
}
g(xs) == fold[B,A](f, v, xs)
49
50. FP - fold universal property - those are equivalent
def g(xs: List[B]): A = xs match {
case Nil => v
case Cons(h,t) => f(h,g(t))
}
g(bs) == fold[B,A](f, v, bs)
… and definition g is unique
50
51. FP - fold universal property - those are equivalent
def g(xs: List[B]): A = xs match {
case Nil => v
case Cons(h,t) => f(h,g(t))
}
g(xs) == fold[B,A](f, v, xs)
def product(xs: List[Int]): Int = xs match {
case Nil => 1
case Cons(head, tail) => head * product(tail)
}
def product(xs: List[Int]): Int = fold[Int, Int](_ * _, 1, xs)
51
52. Challenge fold all the things
● define filter using fold
● define map using fold
● define reverse using fold
52
54. fold .. can we make it more general?
def fold[A,B](f: (A,B) => B, init: B, xs: List[A]): B = {
xs match {
case Nil => init
case Cons(head, tail) => f(head, fold(f, init, tail))
}
}
54
55. fold .. we can make and abstraction from it
trait Foldable[F[_]] {
def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B
}
// F[_] is a type constructor
55
56. type constructor
ascala> :k List
List's kind is F[+A]
scala> :k Int
Int's kind is A
scala> :k List[Int]
List[Int]'s kind is A 56
57. Foldable for List
trait Foldable[F[_]] {
def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B
}
val listFoldable: Foldable[List] = new Foldable[List] {
def foldLeft[A, B](fa: List[A], b: B)(f: (B, A) => B): B = {
fa.foldLeft(b)(f)
}
} 57
58. Foldable - folding using Monoid
def fold[A](fa: F[A])(implicit A: Monoid[A]): A =
foldLeft(fa, A.empty) { (acc, a) =>
A.combine(acc, a)
}
https://github.com/lemastero/scala_typeclassopedia#foldable Let’s take a look (wiki, Cats, Scalaz)
58
62. United monoids - efficient graph operations
https://blogs.ncl.ac.uk/andreymokhov/an-algebra-of-graphs/
Algebra of Parametrised Graphs (Functional Pearl)
Andrey Mokhov
2017 Haskell Symposium
62
63. Inverse semigroup - Checking parents match
https://www.youtube.com/watch?v=d7JPz3Vq9YI
https://www.youtube.com/watch?v=HGi5AxmQUwU
Edward Kmett
Lambda World 2018
63
66. Category
● objects: A, B, C, ...
● morphisms (arrows) f: A-> B
● for every object there is identity morphism
○ id : A -> A
● ...
66
67. Category
● objects: A, B, C, ...
● morphisms (arrows) f: A-> B
● for every object there is identity morphism
○ id : A -> A
● composition for every morphisms f: A -> B, g: B -> C exists
○ (g . f) : A -> C
67
68. Category
● objects: A, B, C, ...
● morphisms (arrows) f: A-> B
● for every object there is identity morphism
○ id : A -> A
● composition for every morphisms f: A -> B, g: B -> C exists
○ (g . f) : A -> C
● associativity, for every morphisms f: A -> B, g: B -> C, h: C -> D
○ h . (g . f) = (g . h) . f
68
69. Category
● objects: A, B, C, ...
● morphisms (arrows) f: A-> B
● for every object there is identity morphism
○ id : A -> A
● composition for every morphisms f: A -> B, g: B -> C exists
○ (g . f) : A -> C
● associativity, for every morphisms f: A -> B, g: B -> C, h: C -> D
○ h . (g . f) = (g . h) . f
69
70. Category
● objects: A, B, C, ...
● morphisms (arrows) f: A-> B
● for every object there is identity morphism
○ id : A -> A
● composition for every morphisms f: A -> B, g: B -> C exists
○ (g . f) : A -> C
● associativity, for every morphisms f: A -> B, g: B -> C, h: C -> D
○ h . (g . f) = (g . h) . f
70
72. Category of Scala types and functions
objects - types
morphisms - functions
identity for A - identity[A]
composition g f - f compose g
PS did I miss any arrow or two?
72
73. When types and functions does not form Category?
● throwing exceptions
def get[A](index: Int, xs: List[A]): A = xs(index)
● input output (network communication, disc read/write, DB calls, console)
def readline(): String = scala.io.StdIn.readLine()
● changing/depending on external state that can be changed
def now() = java.time.LocalDateTime.now()
73
74. Equation reasoning = superpowers from Category
● parallelism does not require PhD (you can write few parallel algorithms every
day)
○ Umut Acar 2018 OSSPL https://www.cs.uoregon.edu/research/summerschool/summer18/topics.php
● writing unit tests is trivial (no mocks, no xUnit Patterns)
○ pass argument, check output
● understand == read types (no need for analyzing state somewhere,
implementations of sub function)
● reusability is trivial
74
75. Category of RDBMS
objects - tables
morphisms - connect tables using FK
identity for A - PK
composition g f - combine FK conditions
https://www.youtube.com/watch?v=fTporauBJEs Categorical Databases David I. Spivak
75
76. Category of states of UI and user actions
objects - states of user interface of web application
morphisms - user actions
id morphism - user stare at screen :)
composition - performing particular set of actions
Is this a category? What if it is a CMS (Liferay, Joomla) with multiple users?
76
77. Category of rows in table with PK and <=
● entities in RDBMS with natural number as PK with <=
● entities in RDBMS with created_timestamp as PK <=
● entities in RDBMS with UUID as PK with <=
Hint: those are Posets (Partially Ordered Sets)
77
78. OOP Challenge - Category of components and ?
objects - software components
morphisms - ???
Hints:
- does Adapter pattern help?
- does Facade pattern help?
- what is reusable in reusable components?
- is there a Category of microservices and ...
78
79. Challenge X 3-category of Scala types
Given Category of Scala types (as objects) and functions (as morphisms). Define
3-category based on this.
● give example of 2-morphisms and composition of them
● give example of 3-morphisms and composition of them
Hint
● Tom Leinster “An Introduction to n-Categories” https://www.youtube.com/watch?v=6bnU7_6CNa0
79
80. Composition in FP == composing functions
● composing in FP == composing functions
● but exceptions, mutable state, IO are handy:
○ showing stuff to user
○ talking to HTTP
○ reading configs from disc
○ reading/writing to NoSQL or DB….
● what’s practical in Category definition?
80
81. Problem - simple OOP program - model
case class Pet(name: String)
case class Owner(id: Long, var pets: ListBuffer[Pet])
case class InvalidOwnerId(msg: String) extends RuntimeException(msg)
81
82. Problem - simple OOP program - storage
case class PetRepository() {
def updateOwner(o: Owner): () = ???
def getOwner(id: Long): Owner = {
if (id < 0) throw InvalidOwnerId("Invalid id")
// query DB/NoSQL/microservice
???
}
} 82
83. Problem - simple OOP program - service
def addPet(ownerId: Long, pet: Pet): Unit = {
val repo = PetRepository() // probably from DI framework
val owner = repo.getOwner(ownerId)
owner.pets = owner.pets += pet
repo.updateOwner(owner)
}
83
85. Problem - simple OOP program - concrete impl
case class PetRepositoryJDBC()
extends PetRepository {
def updateOwner(o: Owner): () = ???
def getOwner(id: Long): Owner = {
if (id < 0) throw InvalidOwnerId("Invalid id")
???
}
}
85
86. Problem - OOP solution - depend on abstraction
(testable)
def addPet(ownerId: Long, pet: Pet, repo: PetRepository): () = {
val owner = repo.getOwner(ownerId)
owner.pets = owner.pets += pet
repo.updateOwner(owner)
}
val repo: PetRepository = ??? // get by DI
addPet(42, Pet("fluffy"), ???)
86
87. “Better Java”
● nice syntax for POJO/JavaBeans
● nice syntax for lambdas
● you can add methods to existing class, without touching them 87
88. FP solution 1 - immutability 1/2
case class Owner(id: Long, pets: List[Pet])
def addPet(ownerId: Long, pet: Pet, repo: PetRepository): () = {
val owner = repo.getOwner(ownerId)
val newPets = pet :: owner.pets
val newOwner = owner.copy(pets = newPets)
repo.updateOwner(newOwner)
}
88
89. FP solution 1 - immutability 2/2
val repo: PetRepository = ??? // summon using Spring
val uo = addPet(42, Pet("fluffy"), repo)
89
90. FP solution 2 - immutability + describe effect (1/2)
case class UpdateOwner(newOwner: Owner, repo: PetRepository)
def addPet(ownerId: Long, pet: Pet, repo: PetRepository): UpdateOwner = {
val owner = repo.getOwner(ownerId)
val newPets = pet :: owner.pets
val newOwner = owner.copy(pets = newPets)
UpdateOwner(newOwner, repo)
}
90
91. FP solution 2 - immutability + describe effects 2/2
// our program don’t do anything it just descriptions stuff!
val uo = addPet(42, Pet("fluffy"), repo)
// at the end of the world (... in main)
val repo: PetRepository = ??? // read config, create JDBC repo
uo.repo.updateOwner(uo.newOwner) // do some changes
91
92. FP solution 2 - higher order functions (HOF)
def addPet(ownerId: Long, pet: Pet, getOwner: Long => Owner,
updateOwner: Owner => Unit): () = {
val owner = getOwner(ownerId)
val newPets = pet :: owner.pets
val newOwner = owner.copy(pets = newPets)
updateOwner(newOwner)
}
92
93. FP solution 2 - higher order functions (HOF)
def addPet(ownerId: Long, pet: Pet, getOwner: Long => Owner, updateOwner: Owner => Unit): () = {
val owner = getOwner(ownerId)
val newPets = pet :: owner.pets
val newOwner = owner.copy(pets = newPets)
updateOwner(newOwner)
}
● could we return description?
● do we depend in here on Repository interface?
93
94. higher order functions (HOF)
HOF == loose coupling
for free !!!
4 life !!!
for all !!!
94
95. FP tricks
● do not run - create description + at the end of the world - interpret it
(Interpreter pattern?)
● do not modify state in place, use immutable objects/collections with update on
copy
● use expressions (return interesting stuff) not statements (return boring stuff)
● use higher order functions (lambda expressions in Java)
● do not throw exceptions (wait I did not show that !)
95
96. FP tricks pros and cons
● Higher Order Functions
○ testability
○ loose coupling (and free beer if your name is Homer)
○ can be hard to understand without good naming
○ harder to find them (not attached to class instance, no IDE support)
● create description
○ testability
○ multiple interpreters (create documentation, tweak performance)
○ slower than running
○ can be harder to understand (why did not run)
● immutability
○ no concurrency bugs (live locks, dead locks, no heisenbus)
○ friendly for parallel computing
○ can be faster than mutable in place
○ can be slower than mutable in place 96
98. Functor in Category Theory
Functor F: C -> D from Category C to Category
D is a mapping of:
● objects: C -> D
● morphisms C -> D
that preserve structure, meaning:
● F preserve identity F(id(x)) = id(F(a))
● F preserve composition F(g . f) = F(g) . F(f)
98
99. Functor in Scala - interface
trait Functor[F[_]] {
def map[A, B](fa: F[A])(fun: A => B): F[B]
}
99
100. Functor in Scala - Implementation for Identity
case class Id[A](value: A)
val idFunctor: Functor[Id] = new Functor[Id] {
def map[A, B](wrapper: Id[A])(fab: A => B): Id[B] = {
val a: A = wrapper.value
val b: B = fab( a )
Id( b )
}
}
100
101. List as Functor - custom implementation
val listFunctorCust: Functor[List] = new Functor[List]() {
def map[A, B](fa: List[A])(ab: A => B): List[B] = fa match {
case Nil => Nil
case head :: tail => ab(head) :: map(tail)(ab)
}
}
101
102. Option as Functor - custom implementation
val optionFunctorCust: Functor[Option] = new Functor[Option]() {
def map[A, B](opt: Option[A])(ab: A => B): Option[B] = opt match {
case None => None
case Some(v) => Some( ab(v) )
}
}
102
104. Option as Functor - Option already have map
val optionFunctor: Functor[Option] = new Functor[Option]() {
def map[A, B](fa: Option[A])(f: A => B): Option[B] =
fa map f
}
104
105. List as Functor - List already have map
val listFunctor: Functor[List] = new Functor[List]() {
def map[A, B](fa: List[A])(f: A => B): List[B] =
fa map f
}
105
106. List already has map - examples no typeclass sugar
import cats.Functor
import cats.implicits._
List(2, 3, 4).map(isOdd) == List(false, true, false)
Option(42).map(isOdd) == Option(false)
val myId: Id[Int] = 42
Functor[Id].map(myId)(isOdd) == false
106
107. …. so List Functor gives us more complicated way to
invoke map function - doh!
List(2, 3, 4).map(isOdd) == List(false, true, false)
Option(42).map(isOdd) == Option(false)
val myId: Id[Int] = 42 // and use useless types !!!
Functor[Id].map(myId)(isOdd) == false
107
113. Functor - identity law
def identityLaw[A](fa: F[A]): Boolean = {
// identity
// F[A] =================> F[A]
val l: F[A] = map(fa)(identity)
val r: F[A] = fa
l == r
}
113
114. Functor - composition law
def covariantComposition[A, B, C](fa: F[A], f: A => B, g: B => C): Boolean = {
// f
// F[A] ========> F[B]
val l1: F[B] = map(fa)(f)
// g
// F[B] =========> F[C]
val l2: F[C] = map(l1)(g)
// f andThen g
// F[A] ==============> F[C]
val r: F[C] = map(fa)(f andThen g)
l2 == r
}
114
115. Functor in Scala vs Functor in Ct
● type constructor map objects (types): A -> F[A]
F[_]
● map function maps morphisms (functions) f: A -> B ----> map(f): F[A] -> F[B]
def map[A, B](fa: F[A])(fun: A => B): F[B]
115
116. Option is Functor in cat. of Scala types and funs
identity[String] ->
map(identity[String]) is
identity for Option[String]
?
map(apply . length) is the
same as
map(length) . map(apply)
?
116
118. Challenge - Lazy Functor
type Thunk[A] = () => A
val thunkFunctor: Functor[Thunk] = new Functor[Thunk] {
def map[A, B](fa: Thunk[A])(f: A => B):
Thunk[B] =
???
}
118
119. Challenge - Whatever Functor
case class Const[A,B](a: A)
def constFunctor[X]: Functor[Const[X, ?]] = new Functor[Const[X,?]] {
def map[A, B](fa: Const[X, A])(f: A => B): Const[X, B] =
???
}
119
120. Challenge - Working back
val predicateContravariant: Contravariant[Predicate] = new Contravariant[Predicate] {
def contramap[A, B](pred: Predicate[A])(fba: B => A): Predicate[B] =
Predicate[B](fba andThen pred.fun)
}
case class Predicate[A](fun: A => Boolean)
● define trait Contravariant
● define laws for Contravariant
● define Contravariant[? => X]
● why there is no Functor[? => X]
120
121. Challenge X - ekmett/discrimination in Scala
● Contravariant functors are used by Edward Kmett to implement discrimination
sorting in linear time
● Github repo (Haskell) https://github.com/ekmett/discrimination/
● video (dense Category Theory) https://www.youtube.com/watch?v=cB8DapKQz-I
121
122. Challenge X - Opposite twins
trait Profunctor[P[_, _]] {
def dimap[A,B,C,D](f: A => B, g: C => D): P[B, C] => P[A, D]
def lmap[A,B,C](f: ???): ??? = dimap(f,identity[C])
def rmap[A,B,C](g: ???): ??? = dimap[A,A,B,C](identity[A], g)
}
trait Bifunctor[P[_,_]] {
def bimap[A,B,C,D](f: A => B, g: C => D): P[A, C] => P[B, D]
def first[A,B,C](f: ???): ??? = bimap(f, identity)
def second[A,B,C](g: ???): ??? = bimap(identity, g)
}
● fill missing types in
definitions of Bifunctor and
Profunctor (???)
● define laws for Bifunctor
● define laws for Profunctor
● can you extract common
part from Profunctor and
Bifunctor
● define instance for
○ Profunctor[? => ?]
○ Bifunctor[(?, ?)]
○ Bifunctor[Either(?,?)]
122
123. Challenge X - Ran Kan Fun
trait Ran[G[_], H[_], A] {
def runRan[B](f: A => G[B]): H[B]
}
def ranFunctor[G[_], H[_]]: Functor[Ran[G, H, ?]] = {
new Functor[Ran[G, H, ?]] {
def map[A, B](fa: Ran[G, H, A])(f: A => B): Ran[G, H, B] = ???
}
}
● (warmup) replace all H occurences by G and implement ranFunctor
● implement ranFunctor (Functor for Right Kan Extension)
123
126. So you want to make another talk about Monads
“It is doubtful that Monads have been discovered without the insight afforded by
category theory.”
Philip Wadler
● Monad tutorials timeline
○ https://wiki.haskell.org/Monad_tutorials_timeline
● why writing Monad tutorial is not such a bad idea
○ https://byorgey.wordpress.com/2009/01/12/abstraction-intuition-and-the-monad-tutorial-fallacy/
126
128. Monads - ADT for simple expressions
sealed trait Term
case class Con(value: Int) extends Term
case class Div(lhs: Term, rhs: Term) extends Term
128
129. Monads - evaluation for simple expressions
def eval(term: Term): Int = term match {
case Con(a) => a
case Div(lhs, rhs) => eval(lhs) / eval(rhs)
}
129
130. Monads - examples for evaluation
val answer = Div(
Div(Con(1972), Con(2)),
Con(23)
) // 42
println(eval(answer))
val error = Div(Con(1), Con(0))
println( Try(eval(error)) ) // error
130
131. Monads - Variant 1 - Exceptions
type Exception = String
sealed trait M[A]
case class Raise[A](exception: Exception) extends M[A]
case class Return[A](a: A) extends M[A]
131
132. Monads - Variant 1 - Exceptions
def eval(term: Term): M[Int] = term match {
case Con(a) => Return(a)
case Div(lhs, rhs) => eval(lhs) match {
case Raise(e) => Raise(e)
case Return(a) => eval(rhs) match {
case Raise(e) => Raise(e)
case Return(b) =>
if(b == 0) Raise("divide by zero")
else Return(a / b)
}
}
}
132
134. Monads - Variant 2 - State
type State = Int
// Initial state => (coputed value, new state)
type M[A] = State => (A,State)
134
135. Monads - Variant 2 - State
def eval(term: Term): M[Int] = term match {
case Con(a) => x => (a, x)
case Div(lhs, rhs) => x =>
val (a,y) = eval(lhs)(x)
val (b,z) = eval(rhs)(y)
(a / b, z + 1)
}
135
136. Monads - Variant 2 - State
val answer = Div(
Div(Con(1972), Con(2)),
Con(23)
)
println(eval(answer)) // (42, 2)
136
137. Monads - Variant 3 - Trace of execution
type Output = String
type M[A] = (Output, A)
137
138. Monads - Variant 3 - Trace of execution
def line(t: Term, i: Int): String = s"eval ($t) <= $in"
def eval(term: Term): M[Int] = term match {
case Con(a) => (line(Con(a), a), a)
case Div(lhs, rhs) =>
val (x,a) = eval(lhs)
val (y,b) = eval(rhs)
(x ++ y ++ line(Div(lhs, rhs), a/b), a / b)
}
138
140. Monads - Here comes the Monad!
trait Monad[F[_]] {
def unit[A](a: A): F[A]
def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B]
}
140
141. Monads - fancy way to do what we already can!
type Id[A] = A
val idMonad: Monad[Id] = new Monad[Id] {
def unit[A](a: A): Id[A] = a
def flatMap[A, B](ma: Id[A])(f: A => Id[B]): Id[B] = f(ma)
}
141
142. Monads - fancy way to do what we already can!
type Id[A] = A
val idMonad: Monad[Id] = new Monad[Id] {
def unit[A](a: A): Id[A] = a
def flatMap[A, B](ma: Id[A])(f: A => Id[B]): Id[B] = f(ma)
}
def eval[M[_]](term: Term)(implicit MM: Monad[M]): M[Int] = term match {
case Con(a) => MM.unit(a)
case Div(lhs, rhs) =>
MM.flatMap( eval(lhs) )(a => MM.flatMap( eval(rhs) )(b => MM.unit(a / b))
)}
142
143. Monads - fancy way to do what we already can!
type Id[A] = A
val idMonad: Monad[Id] = new Monad[Id] {
def unit[A](a: A): Id[A] = a
def flatMap[A, B](ma: Id[A])(f: A => Id[B]): Id[B] = f(ma)
}
def eval[M[_]](term: Term)(implicit MM: Monad[M]): M[Int] = term match {
case Con(a) => MM.unit(a)
case Div(lhs, rhs) =>
MM.flatMap( eval(lhs) )(a => MM.flatMap( eval(rhs) )(b => MM.unit(a / b))
)}
println( eval(answer)(idMonad) )
143
144. Monads - Variant 2 - Exceptions with MonadError
type Exception = String
sealed trait Validated[+A]
case class Raise[A](exception: Exception) extends Validated[A]
case class Return[A](a: A) extends Validated[A]
144
145. Monads - Variant 2 - Exceptions with MonadRaise
type Exception = String
sealed trait Validated[+A]
case class Raise[A](exception: Exception) extends Validated[A]
case class Return[A](a: A) extends Validated[A]
trait MonadErr[F[_]] extends Monad[F] {
def raise[A](e: Exception): F[A]
}
145
146. Monads - Variant 2 - Exceptions with MonadRaise
trait MonadErr[F[_]] extends Monad[F] {
def raise[A](e: Exception): F[A]
}
val exceptionMonad: MonadErr[Validated] = new MonadErr[Validated] {
def unit[A](a: A): Validated[A] = Return(a)
def flatMap[A, B](ma: Validated[A])(f: A => Validated[B]): Validated[B] = ma match {
case Raise(e) => Raise(e)
case Return(a) => f(a)
}
def raise[A](e: Exception): Validated[A] = Raise(e)
} 146
147. Monads - Variant 2 - Exceptions with MonadRaise
def eval[M[_]](term: Term)(implicit MM: MonadErr[M]): M[Int] = term match {
case Con(a) => MM.unit(a)
case Div(lhs, rhs) =>
MM.flatMap( eval(lhs) )(a =>
MM.flatMap( eval(rhs) )(b =>
if(b == 0) MM.raise[Int]("divide by zero")
else MM.unit(a / b)
))}
println( eval(answer)(exceptionMonad) )
val error = Div(Con(1), Con(0))
147
148. MonadRaise - so we have new abstraction
trait MonadRaise[F[_]] extends Monad[F] {
def raise[A](e: Exception): F[A]
}
trait FunctorRaise[F[_]] extends Monad[F] {
def raise[A](e: Exception): F[A]
}
● this is advanced abstraction from MTL: https://typelevel.org/cats-mtl/mtl-classes/functorraise.html
148
149. Monads - Variant 2 - Output with MonadTell
type Output = String
type Writer[A] = (Output, A)
trait MonadTell[F[_]] extends Monad[F] {
def tell(x: Output): F[Unit]
}
149
150. Monads - Variant 2 - Output with MonadTell
val writerMonadOut: MonadTell[Writer] = new MonadTell[Writer] {
def unit[A](a: A): (Output, A) = ("", a)
def flatMap[A, B](m: (Output, A))(k: A => (Output, B)): (Output, B) = {
val (x,a) = m
val (y,b) = k(a)
(x ++ y, b)
}
def tell(x: Output): Writer[Unit] = (x, ())
}
150
151. Monads - Variant 2 - Output with MonadTell
def eval[M[_]](term: Term)(implicit MM: MonadTell[M]): M[Int] = term match {
case Con(a) => {
val out1 = line(Con(a),a)
val out2 = MM.tell(out1)
MM.flatMap(out2)(_ => MM.unit(a))
}
case Div(lhs, rhs) =>
MM.flatMap( eval(lhs) )(a =>
MM.flatMap( eval(rhs) )(b => {
val out1 = line(Div(lhs, rhs), a/b)
val out2 = MM.tell(out1)
MM.flatMap(out2)(_ => MM.unit(a / b))
} ) ) }
151
153. New abstraction - FunctorTell
trait MonadTell[F[_]] extends Monad[F] {
def tell(x: Output): F[Unit]
}
trait FunctorTell[F[_]] extends Monad[F] {
def tell(x: Output): F[Unit]
}
● this is advanced abstraction from MTL: https://typelevel.org/cats-mtl/mtl-classes/functortell.htmlw
153
154. Challenge X - what about State case?
● Hint section 2.8, Philip Wadler, Monads for functional programming:
http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf
154
155. Papers … how to read them ???
● next Philip Wadler goes into arrays updates and recursive descent parsers :)
● slow down:
○ read abstract, conclusions, maybe browse literature
○ browse for pictures,
○ find page with most complicated symbols - post on twitter with “interesting” :)
○ stare at it when you can’t sleep
155
159. Monads in Category Theory
Monad is given by:
● an (endo)functor T : C -> C and
● mappings between functor T (natural transformations):
η: I => T “eta”
μ: TxT => T “mu”
such that following
diagrams works
(diagrams commute)
159
160. Monads laws - associativity
// fa.flatMap(f).flatMap(g) == fa.flatMap(a => f(a).flatMap(b => g(b))
def flatMapAssociativity[A,B,C](fa: M[A], f: A => M[B], g: B => M[C]): Boolean = {
// flatMap(f)
// M[A] =============> M[B]
val l1: M[B] = flatMap(fa)(f)
// flatMap(g)
// M[B] =============> M[C]
val l2: M[C] = flatMap(l1)(g)
val r1: A => M[C] = a => flatMap(f(a))(b => g(b))
// flatMap(f flatMap g)
// M[A] ======================> M[C]
val r2: M[C] = flatMap(fa)(r1)
l2 == r2
}
160
161. Monads laws - left identity
// pure(a).flatMap(f) == f(a)
def leftIdentity[A,B,C](a: A, f: A => M[B], g: B => M[C]): Boolean = {
// pure
// A =======> M[A]
val l1: M[A] = pure(a)
// flatMap
// M[A] ==========> M[B]
val l2: M[B] = flatMap(l1)(f)
// A =======> M[B]
val r: M[B] = f(a)
l2 == r
} 161
162. Monads laws - right identity
// fa.flatMap(pure) == fa
def rightIdentity[A,B,C](a: A, fa: M[A], g: B => M[C]): Boolean = {
// flatMap
// M[A] ==========> M[A]
val l: M[A] = flatMap(fa)(pure)
val r: M[A] = fa
l == r
}
162
163. Challenge X - how Monad laws in FP relate to CT
163
164. Challenge X - Mambonad Kings Ran
trait Ran[G[_], H[_], A] {
def runRan[B](f: A => G[B]): H[B]
}
def ranMonad[G[_], H[_]]: Monad[Ran[G, H, ?]]= ???
● this monad is called Codensity:
https://github.com/scalaz/scalaz/blob/series/7.3.x/core/src/main/scala/scalaz/Codensity.scala
164
165. Challenge - Monad all the things
● write monad instance for all things
○ Option
○ List
○ Either
○ Thunk
○ Tuple(A, ?), Tuple(?, A), Tuple(A,A)
165
166. Challenge - Monad all the things - Continuation
● write monad instance for Continuation
trait Cont[M[_],A] {
def run[Z](f: A => M[Z]): M[Z]
}
166
168. Challenge - Free Monads
sealed trait Free[F[_], A]
case class Return[F[_], A](a: A) extends Free[F,A]
case class Bind[F[_],I,A](i: F[I], k: I => Free[F,A]) extends Free[F,A]
● write a Monad instance for Free
● Daniel Spiewak, Free as in Monads: https://www.youtube.com/watch?v=aKUQUIHRGec
● Cats docs about Free Monads https://typelevel.org/cats/datatypes/freemonad.html
● ChallangeX: http://blog.higher-order.com/blog/2013/11/01/free-and-yoneda/
168
169. Monads do not compose
● Functor, Apply, Applicatvie, Arrows -> compose
● Monads model sequential computation and do not compose
● Solutions
○ Monad transformers (transform inner monad, slow)
○ Free Monads
○ tagless final
○ ...
169
170. Final Tagless
● Death of Final Tagless, John A. DeGoes
○ https://skillsmatter.com/skillscasts/13247-scala-matters
● FP to the Max Fun(c) 2018.7, John A. DeGoes
○ https://www.youtube.com/watch?v=sxudIMiOo68
170
173. Applicative Functors
● in Category Theory lax monoidal functor
● Monad is a Monoid in (Monoidal) Category of endofunctors with Functor
composition as tensor
● Applicative is a Monoid in (Monoidal) Category of endofunctors with Day
convolution as tensor
173
176. Applicative - instance for Option
val optionApplicative: Applicative[Option] = new Applicative[Option] {
def pure[A](a: A): Option[A] = Some(a)
def ap[A, B](ff: Option[A => B])(fa: Option[A]): Option[B] = (ff,fa) match {
case (Some(fab), Some(faa)) => Some(fab(faa))
case (_,_) => None
}
}
176
177. Applicative - examples Option
import cats.Applicative
import cats.implicits.catsStdInstancesForOption
val add3: Int => Int = a => a + 3
Applicative[Option].pure(42) == Some(42)
Applicative[Option].ap(Some(add3))(Some(39)) == Some(42)
177
178. Applicative - examples Option - where is culprit
import cats.Applicative
import cats.implicits.catsStdInstancesForList
val list = List(1, 2)
val listFuns: List[Int => Int] = List(a => a + 3, a => a * a)
Applicative[List].ap(listFuns)(list) mustBe List(4,5,1,4)
Applicative[List].pure("Minsc") mustBe List("Boo")
178
181. Functor - Apply - Applicative - Monad hierarchy (3/3)
Functor def map[A, B] (fa: F[A]) (f: A => B): F[B]
Apply def ap[A, B] (fa: F[A]) (ff: F[A => B]) : F[B]
Applicative def pure[B] (b: B): F[A]
Monad def flatMap[A, B](fa: F[A]) (f: A => F[B]): F[B]
● more abstractions in this way on Tony Morris blog:
http://blog.tmorris.net/posts/scala-type-class-hierarchy/index.html
181
182. Applicative - parallel Monad - sequential
● Monads have sequential semantics - you cannot do parallel processing when
you use flatMap
● Applicatives do not have such limitations, if you want parallel semantics you
should prefere Applicative instance than Monad (even that Monad one have
more methods). Matters in: cats-effect https://typelevel.org/cats-effect/)
● Monads knows more about F inside, you can do more with them, but you
must pay price: composition, parallel semantics
182
184. Challenge Applicative
● write Applicative instance for ValidatedNel:
case class Nel[A](head: A, tail: Nel[A])
sealed trait ValidatedNel[A]
case class SuccessVN[A](value: A) extends ValidatedNel[A]
case class Errors[A](errors: Nel[Throwable]) extends ValidatedNel[A]
● (harder) add type parameter instead of Throwable
184
185. Challenge - Monads are Applicative
● write Applicative instance for every thing you know is a Monad:
○ Option[A]
○ Tuple[A,?], Tuple[?,A]
○ Either[A,?], Either[?,A]
○ Thunk () => A
○ State S => (A,S)
○ Reader Ctx => A
○ Writer A => (A, L)
185
187. Traversable - Foldable + Functor
trait Traverse[F[_]]
extends Functor[F]
with Foldable[F] {
def traverse[G[_] : Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]]
}
● map flatMap like function and change the way how effects are stacked
187
190. Traversable - example - sequence
import cats.implicits._
val xs1: List[Option[Int]] = List(Some(1), Some(2), None)
xs1.sequence == None
val xs2: List[Option[Int]] = List(Some(1), Some(2), Some(42))
xs2.sequence == Some(List(1,2,42))
190
191. Challenge Traversable - map
● implement map for Traverse
trait Traverse[F[_]] extends Functor[F] with Foldable[F] {
def traverse[G[_] : Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]]
override def map[A, B](fa: F[A])(f: A => B): F[B] = ???
}
191
192. Traversable - classic paper
● more in: The Essence of the Iterator Pattern, J. Gibbons, B. Oliveira
○ https://www.cs.ox.ac.uk/jeremy.gibbons/publications/iterator.pdf
192
193. MOAR !11!!!!
Andrey Morkhov, 2019,
submitted to ICFP 2019 - choose
only one of two effects
trait Selective[F[_]] extends Applicative[F] {
def handle[A, B](fab: F[Either[A, B]], ff: F[A => B]): F[B]
def select[A, B, C](fab: F[Either[A, B]], fac: F[A => C], fbc: F[B => C]): F[C]
}
193
194. trait Dijkstra[S,A] {
def runWp[R](f: (S,A) => R): S => R
}
implicit def dijakstraMonad[S]: Monad[Dijkstra[S, ?]] = new Monad[Dijkstra[S, ?]] {
def pure[A](a: A): Dijkstra[S,A] = new Dijkstra[S,A] {
def runWp[R](f: (S, A) => R): S => R = s => f(s,a)
}
def flatMap[A,B](ma: Dijkstra[S,A])(f: A => Dijkstra[S,B]): Dijkstra[S,B] =
new Dijkstra[S,B] {
def runWp[R](g: (S, B) => R): S => R =
ma.runWp{ case (s,a) => f(a).runWp(g)(s) }
}
}
194
195. Computational Context - other intuition for Monads
● Option - may not have value
● Either, ValidatedNel, Try, Future - may fail
● Future, Cats Effect IO, Twitter Future - take time to compute
● List, Vector - may have multiple values
● Tuple - value with some context
● Const - always have the same value
● Reader, Function[Config, ?] - require some context to be computed
● Map - is indexed by
● State - computing changes some state
● Writer - produce some data when computed
● Id - no computational context
195
196. Algebraic effects, handlers
● Theory using Category Theory to deal with effects
● Lectures (math limited to algebra) Andrej Bauer, Algebraic Effects and
Handlers https://www.cs.uoregon.edu/research/summerschool/summer18/
● wiki with publications: https://github.com/yallop/effects-bibliography
● impl in Scala looks scary :)
196
197. Coyoneda Trick
Trick that allow pretend that any type constructor is a functor, as long as at the
end we can move it to something that is a Functor.
197
198. Coyoneda Trick - type with constructor
trait Coyoneda[F[_], A] {
type Z
def fb: F[Z]
def f: Z => A
}
198
199. Coyoneda Trick - liftCoyoneda create Coyo for F
object Coyoneda {
def apply[F[_],AA,BB](ff: BB => AA, ffb: F[BB]): Coyoneda[F,AA] = new Coyoneda[F,AA] {
type Z = BB
def fb: F[Z] = ffb
def f: Z => AA = ff
}
def liftCoyoneda[F[_], A](fa: F[A]): Coyoneda[F, A] = Coyoneda[F, A, A](identity[A], fa)
} 199
200. Coyoneda Trick - we can map
implicit def coyoFunctor[F[_]]: Functor[Coyoneda[F, ?]] = new Functor[Coyoneda[F, ?]] {
def map[A, AA](fa: Coyoneda[F, A])(ff: A => AA): Coyoneda[F, AA] = new Coyoneda[F, AA] {
type Z = fa.Z
def f: Z => AA = fa.f andThen ff
def fb: F[Z] = fa.fb
}
}
200
201. Coyoneda Trick - we can change F
def hoistCoyoneda[F[_], G[_], A, C](fab : F~>G])(coyo: Coyoneda[F, A]): Coyoneda[G, A] =
new Coyoneda[G, A] {
type Z = coyo.Z
def f: Z => A = coyo.f
def fb: G[Z] = fab(coyo.fb)
}
201
202. Coyoneda Trick - we can change F
def hoistCoyoneda[F[_], G[_], A, C](fab : F~>G])(coyo: Coyoneda[F, A]): Coyoneda[G, A] =
new Coyoneda[G, A] {
type Z = coyo.Z
def f: Z => A = coyo.f
def fb: G[Z] = fab(coyo.fb)
}
202
203. Coyoneda Trick - using Functor[F] get back F
trait Coyoneda[F[_], A] {
type Z
def fb: F[Z]
def f: Z => A
// ..
def lowerCoyoneda(implicit fun: Functor[F]): F[A] = fun.map(fb)(f)
}
203
204. Coyoneda Trick
Now we can pretend every type constructor is a Functor, if we can transform it to
proper Functor at some point in the future :)
204
205. Challenge X Yo - Reverse engineering machines
Translate to Scala:
Dan Piponi, Reverse Engineering Machines with the Yoneda Lemma:
http://blog.sigfpe.com/2006/11/yoneda-lemma.html
205
207. Comonad - definition
trait Comonad[F[_]] extends Functor[F] {
def extract[A](w: F[A]): A
def duplicate[A](wa: F[A]): F[F[A]]
// derived methods
def extend[A, B](w: F[A])(f: F[A] => B): F[B] =
map(duplicate(w))(f)
}
● get value (in Monads pure -> put value inside)
● add one more layer (in Monads flatten -> remove one layer)
207
208. Category Theory - duality
● every concept has dual
● we create Opposite Category where
○ objects the same
○ morphisms have reversed arrows
● dual concepts
○ monad <-> comonad
○ initial object <-> terminal object
○ product <-> coproduct
○ limit <-> colimit
208
209. Category Theory - duality
initial object
one, unique morphism to every other
terminal object
one, unique morphism from every other
Bartosz Milewski, Products and Coproducts bartoszmilewski.com/2015/01/07/products-and-coproducts/
209
210. void type - absurd
final abstract class Void private () {
def absurd[A]: A
}
210
216. Comonads - model co-inductive process
● Streams
● web servers (routing)
● Generators
● calculating irrational number with increased accurrency
216
217. Comonads - Co-Freedom
● Write comonad instance for following class
case class Cofree[A, F[_]](extract: A, sub: F[Cofree[A, F]])(implicit functor: Functor[F])
217
218. Challenge - Calculate PI
● Write comonad that generate square root of 2 with increasing accuracy
● … generate pi digits :)
218
219. Challenge - Comonad game
● Write comonad that generate will be base for main loop of console program
● Write some game to guess Category Theory abstractions using above
comonad
219
220. Challenge X - Co-game of life
● Implement Game of Life using Comonads
Hint: google it! ( e.g. https://chrispenner.ca/posts/conways-game-of-life.html)
People use much more than Comonads (Zipper, Representable functors, etc) and
that is even more fun!
220
221. Comonads more !1!!!
● John De Goes: Streams for (Co)Free!
https://www.youtube.com/watch?v=wEVfJSi2J5o&t=955s
(link targets specific video with so called John’ Laws of Clean Functional Code, whole talk is pure gold)
● Runar blog posts about Comonads
○ http://blog.higher-order.com/blog/2015/06/23/a-scala-comonad-tutorial/
○ http://blog.higher-order.com/blog/2015/10/04/scala-comonad-tutorial-part-2/
● Phil Freeman - The Future is Comonadic! (PureScript, use Day convolution to compose) all Phil
Freeman is awesome but in Haskell or Purescript
○ https://www.youtube.com/watch?v=EoJ9xnzG76M
● Comonadic notions of computations
○ http://math.ut.ee/luebeck2006/TLW2006.pdf (slides - easier to understand)
○ https://www.sciencedirect.com/science/article/pii/S1571066108003435
221
222. Category Theory “patterns” vs OOP Design Patterns
● abstractions from math are not patterns, they are libraries if language has expressive enough type
system - higher kinded types - Haskell, Scala, Idris, PureScript
○ Scala: Cat, Scalaz
○ Haskell: Profunctor, Contravariant, Monad, Free
Some notable attempts:
● Relation between Category Theory abstractions and OOP Design Patterns by
Mark Seemann: https://blog.ploeh.dk/2017/10/04/from-design-patterns-to-category-theory/
● Implemenation of Design Patterns in Scala by Li Haoyi:
http://www.lihaoyi.com/post/OldDesignPatternsinScala.html
222
223. Curry-Howard-Lambek iso ~ compu. trinitarianism
● Abstract algebra of ADT is just the beginning
● Logic (Proof theory), Programming (type theory), Category Theory are talking
about the same concepts using different names and tools :)
● names
○ propositions as types
○ proofs as programs
○ Curry-Howard-Lambek isomorphism
○ computational trinitarianism
● Bartosz Milewski, CT 8.2 Type algebra, Curry-Howard-Lambek isomorphism
○ https://www.youtube.com/watch?v=iXZR1v3YN-8
● Philip Wadler, Propositions as Types
○ video: https://www.youtube.com/watch?v=IOiZatlZtGU
○ publication: https://homepages.inf.ed.ac.uk/wadler/papers/propositions-as-types/propositions-as-types.pdf 223
224. Curry-Howard-Lambek isomorphism
logic true false and or implication
types () Void (a,b) Either(a,b) A => B
CT terminal initial product coproduct exponential
● nLab (wiki about Category Theory used by mathematicians)
○ very very long list of those similar concepts:
○ https://ncatlab.org/nlab/show/computational+trinitarianism
224
225. GIMME MOAR !!1!11111
● Bartosz Milewski
○ video playlists (Haskell + Category Theory) Functor, Kleisli, CCC, Comonads, Adjuctions, Representable, Yoneda, Limits, Ends,
F-Alebras, Lambek Theorem, Profunctors
○ youtube.com/user/DrBartosz/playlists Category Theory I, Category Theory II, Category Theory III
○ https://bartoszmilewski.com/ blog, even more
● mpliquist Functional Structures in Scala
○ https://www.youtube.com/playlist?list=PLFrwDVdSrYE6dy14XCmUtRAJuhCxuzJp0
○ Functor, Applicative, Monad, Traverse, Semigroup, Foldable, OptionT, Reader, ReaderT
● Red Book
○ https://www.manning.com/books/functional-programming-in-scala
○ how to design using FP, best explanation ever
● Haskell FP Course https://github.com/data61/fp-course
● https://github.com/lemastero/scala_typeclassopedia (wiki of everything Category Theory related, pictures for laws, advanced concepts) 225
226. RedBook: watch, read everything by Runar
Functional Programming in Scala
Paul Chiusano, Rúnar Bjarnason
https://www.manning.com/books/functional-programming-in-scala
226
227. Bartosz Milewski: beautiful and easy explanations!
Blog: https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/
Book: (in pdf and dead tree format, thanks to Igal Tabachnik): https://github.com/hmemcpy/milewski-ctfp-pdf
Video lectures: https://www.youtube.com/playlist?list=PLbgaMIhjbmEnaH_LTkxLI7FMa2HsnawM_
(there are 2 more series :) of videos)
227
229. ● PR’s to Scalaz 7
○ github.com/scalaz/scalaz/pull/2020 Day convolution (0,5 year fight transalte from Haskell)
○ github.com/scalaz/scalaz/pull/2028 Strong Profunctor laws - they were none
○ github.com/scalaz/scalaz/pull/2029 Density comonad (abstraction - no practical applications yet)
● PR’s to Cats
○ github.com/typelevel/cats/pull/2640 Strong Profunctor laws (based on CT replace ad hoc ones)
● PR’s to Haskell Profunctors
○ github.com/ekmett/profunctors/pull/65 broken link (this is how you start!)
○ github.com/ekmett/profunctors/pull/66 small fix in docs for Strong Profunctor laws
● type_classopedia - wiki about Category Theory abstraction in Scala
○ github.com/lemastero/scala_typeclassopedia
● other OSS work: resources about effects, programming language theory, streaming benchmarks
○ Sclaz ZIO github.com/scalaz/scalaz-zio/pulls?utf8=%E2%9C%93&q=+author%3Alemastero Hackaton @ ScalaC
○ yallop/effects-bibliography github.com/yallop/effects-bibliography/pulls?utf8=✓&q=+author%3Alemastero add
Idris Effects, Scala Eff Monad) wiki about effects by Jeremy Yallop (University of Cambridge)
○ steshaw.org/plt/ (github.com/steshaw/plt/pulls?utf8=✓&q=+author%3Alemastero) add 7Sketches, TAC Journal
○ monix/streaming-benchmarks github.com/monix/streaming-benchmarks/pull/1 updated benchmarks
○ cohomolo-gy/haskell-resources github.com/cohomolo-gy/haskell-resources/pull/3 add Haskell papers
○ lauris/awesome-scala github.com/lauris/awesome-scala/pull/425 add ZIO, update Monix, RxScala
○ passy/awesome-recurion-schemas github.com/passy/awesome-recursion-schemes/pull/22 add droste 229
230. The harder you work, the luckier you get.
Forget the sky, you are the limit.
Work hard. Dream fiercely.
random quotes from internet memes, transpiled by lemastero
Thank you :)
230
231. but wait there's more …. HoTT, UF, CTT
● Vladimir Voevodsky 2006/2009 propose Univalent foundations - new approach
to foundations of mathematics (used to be Set theory - but paradoxes)
● Homotopy Type Theory (HoTT = Homotopy T. + Type T.) is branch of Type
Theory, used by proof assistants, mix Higher Category Theory
● Category Theory was encoded in Coq (proof assistant, Coq itself is very
advanced FP language, more that Scala, Haskell)
● Category Theory encodend in FP using Coq https://arxiv.org/abs/1505.06430
● … different approach using UF: https://github.com/UniMath/UniMath/tree/master/UniMath/CategoryTheory
● Cubical Type Theory - new development in type theory
and some say Type Theory is more applicable than Category Theory!
231