Why functional programming and category theory strongly matters
The document discusses the relevance of functional programming (FP) and category theory, emphasizing algebraic data types (ADTs) and their implementation in Scala. It explores key concepts like functors, monads, and the differences between FP and object-oriented programming (OOP) paradigms. Additionally, it provides detailed examples and comparisons related to data types, algebra, and modeling approaches in programming.
Introduction to Functional Programming (FP) including Algebraic Data Types (ADT) and foundational concepts such as first-class functions, immutability, and purity.
Exploration of advanced FP concepts including sum types, product types, monoids, and the algebra of types. Discusses their significance in theoretical computer science.
Comparison of Algebraic Data Types (ADTs) in functional programming versus Object-Oriented Programming (OOP), illustrating advantages in code clarity and immutability.
Detailed discussion on lists in FP, use of fold for various operations, and demonstrating powerful abstractions in functional programming.
An introduction to Category Theory focusing on definitions, categories, morphisms, and their application in software design and functional programming.
Introduction to Monads as a means to handle computations in functional programming, such as handling state and exceptions with various examples.
Description of Applicative Functors and Traversable structures, including definitions and examples of how they expand upon Functor capabilities.
Introduction to Comonads, their definitions, and practical applications, with examples including streams and recursive structures.
Connections between Category Theory and programming concepts, including discussions on various abstractions and realizations in functional programming.
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 DataTypes (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 ● functionsas 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 typedFP (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
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
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 (inventedin Haskell to confuse Java devs) final abstract class Void private () 17
18.
void type (inventedin Haskell to confuse Java devs) final abstract class Void private () val a: Void = ??? // can we implement this ? 18
19.
void type (inventedin 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 = AbstractData 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
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 aSemigroup ? // (A * B) * C = (A * B) * C val isAssociative: Boolean = (("a", "b"), "c") == ("a", ("b", "c")) 24
25.
product is Semigroupup 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) isMonoid 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) isMonoid 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) isCommutative 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!!! aboutAlgebra 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
ADT = AbstractData Types ● classes with only public getters - immutable ● consist only from product and sum types 33
34.
OOP model version1 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 version2 (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 methodsallow 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 1975in 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 vsFP - 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 ScalaLists 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 - folduniversal 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 - folduniversal 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 - folduniversal 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 allthe things ● define filter using fold ● define map using fold ● define reverse using fold 52
fold .. canwe 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 .. wecan 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> :kList 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 traitFoldable[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 - foldingusing 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
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
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
Category of Scalatypes 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 andfunctions 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 statesof 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 rowsin 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-categoryof 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 - simpleOOP 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 - simpleOOP 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 - simpleOOP 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
84.
Problem - OOPsolution - depend on abstraction trait PetRepository { def updateOwner(o: Owner): () def getOwner(id: Long): Owner } 84
85.
Problem - simpleOOP 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 - OOPsolution - 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” ● nicesyntax 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 ● donot 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 prosand 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
Functor in CategoryTheory 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
103.
List Functor -examples no typeclass sugar import cats.Functor import cats.implicits._ Functor[List].map(List(2, 3, 4))(isOdd) == List(false, true, false) Functor[Option].map(Option(42))(isOdd) == Option(false) val myId: Id[Int] = 42 Functor[Id].map(myId)(isOdd) == false 103
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
…. so ListFunctor 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
Functor - identitylaw 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 - compositionlaw 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 Scalavs 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 Functorin 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
Challenge - LazyFunctor 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 - WhateverFunctor 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 - Workingback 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
So you wantto 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
Monads - Variant1 - 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 - Variant1 - 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
133.
Monads - Variant1 - Exceptions val answer = Div( Div(Con(1972), Con(2)), Con(23) ) println(eval(answer)) // Rerurn(42) val error = Div(Con(1), Con(0)) println( Try(eval(error)) ) // Raise("divide by zero") 133
134.
Monads - Variant2 - State type State = Int // Initial state => (coputed value, new state) type M[A] = State => (A,State) 134
135.
Monads - Variant2 - 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 - Variant2 - State val answer = Div( Div(Con(1972), Con(2)), Con(23) ) println(eval(answer)) // (42, 2) 136
137.
Monads - Variant3 - Trace of execution type Output = String type M[A] = (Output, A) 137
138.
Monads - Variant3 - 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
Monads - fancyway 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 - fancyway 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 - fancyway 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 - Variant2 - 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 - Variant2 - 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 - Variant2 - 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 - Variant2 - 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 - sowe 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 - Variant2 - 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 - Variant2 - 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 - Variant2 - 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
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 … howto 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
Monad for option- simple problem val optionMonad: Monad[Option] = new Monad[Option] { def flatMap[A, B](ma: Option[A])(f: A => Option[B]): Option[B] = { ma match { case Some(a) => f(a) case None => None } } 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 } } 157
158.
Challenge flatMap ● defineflatMap using other methods: trait Monad[M[_]] extends Applicative[M] { def flatten[A](mma: M[M[A]]): M[A] = def flatMap[A, B](ma: M[A])(f: A => M[B]): M[B] = ??? } 158
159.
Monads in CategoryTheory 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
Challenge - FreeMonads 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 notcompose ● 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 ● Deathof 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
Applicative Functors ● inCategory 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
Applicative - instancefor 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 - examplesOption 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 - examplesOption - 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
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 - parallelMonad - 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
Challenge Applicative ● writeApplicative 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 - Monadsare 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
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
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 - classicpaper ● 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] { defrunWp[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 thatallow 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 wecan 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
Comonad - definition traitComonad[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
Comonads - NonEmptyListexamples val nel = Nel(1, Some(Nel(2, Some(Nel(3, None))))) nelComonad.extract(nel) == 1 nelComonad.map(nel)(_ * 10) == Nel(10, Some(Nel(20, Some(Nel(30, None))))) val n2 = Nel(2, Some(Nel(3, None))) val n3 = Nel(3, None) nelComonad.duplicate(nel) == Nel(nel, Some(Nel(n2, Some(Nel(n3, None))))) 215
216.
Comonads - modelco-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 - CalculatePI ● Write comonad that generate square root of 2 with increasing accuracy ● … generate pi digits :) 218
219.
Challenge - Comonadgame ● 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 truefalse 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, readeverything by Runar Functional Programming in Scala Paul Chiusano, Rúnar Bjarnason https://www.manning.com/books/functional-programming-in-scala 226
227.
Bartosz Milewski: beautifuland 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
● PR’s toScalaz 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 youwork, 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'smore …. 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