A big picture of category theory in Scala - starting from regular functors with additional structure (Apply, Applicative, Monad) to Comonads. Usually, we think about structures like Monoids in a monoidal category with particular tensor. In here I analyze just signatures of different abstractions.
Exploration of Contravariant functors as a way to model computation "backward" or abstract over input with the ability to prepend operation. Examples for predicates, sorting, show and function input (or any other function parameter except the last one).
Profunctors as abstraction unifying Functors and Contravariant functors to model both input and output. Example for Profunctor - function with one argument.
Relation to Bifunctors, Kan extensions, Adjunctions, and Free constructions.
Scale your database traffic with Read & Write split using MySQL Router
Big picture of category theory in scala with deep dive into contravariant and profunctors
1. Big picture of Category Theory in Scala
with deep dive into:
- Contravariant
- Profunctors
Piotr Paradziński
ScalaC27.03.2019
1
2. ● PR’s to Scalaz 7
○ github.com/scalaz/scalaz/pull/2020 Day convolution (0,5 year fight translate 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 SclaC Hackaton
○ 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
18. Contravariant - like a Functor but flip!
trait Functor[F[_]] {
def map[A, B] (fa: F[A]) (f: A => B): F[B]
}
trait Contravariant[F[_]] {
def contramap[A, B] (fa: F[A]) (f: B => A): F[B]
}
18
19. Contravariant Functor - input + ability to prepend
Functor is “full of” A’s (container A, function producing A)
F[A] we can map A => B and we get F[B]
F[A] we can contramap B => A and we get F[B]
Contravariant “needs” A (index of container, function
consuming A)
19
20. case class Predicate[A](fun: A => Boolean)
Example in GIthub:
https://github.com/lemastero/scala_typeclassopedia/blob/master/src/test/scala/contravariant/Contravaria
ntSpec.scala
Contravariant - example Predicate (1)
20
21. case class Predicate[A](fun: A => Boolean)
val predicateContravariantFunctor = new Contravariant[Predicate] {
def contramap[A, B](pred: Predicate[A])(fba: B => A): Predicate[B] =
Predicate[B](fba andThen pred.fun)
}
Contravariant - example Predicate (2)
21
22. val pl = Predicate[String](_.length > 5)
pl.fun("Contravariant") // true
val pr1 = Predicate[Int](_ > 5)
pr1.fun(42) // true
Contravariant - example Predicate (3)
22
23. val pl = Predicate[String](_.length > 5)
pl.fun("Contravariant") // true
val pr1 = Predicate[Int](_ > 5)
pr1.fun(42) // true
val len: String => Int = _.length
val pr = Contravariant[Predicate].contramap(pr1)(len)
pr.fun("Contravariant") // true
Contravariant - example Predicate (4)
23
24. Contravariant - example Show
trait Show[F] {
def show(f: F): String
}
implicit val showContravariant = new Contravariant[Show] {
def contramap[A, B](r: Show[A])(f: B => A): Show[B] = new Show[B] {
def show(b: B): String = r.show(f(b))
}
}
Scalaz github.com/scalaz/scalaz/blob/series/7.3.x/core/src/main/scala/scalaz/Show.scala#L51-L53
24
25. Contravariant - example Op (Haskell like)
case class Op[R,A](getOp: A => R)
def opContravariant [R] = new Contravariant[Op[ R, ?]] {
def contramap[A, B](fa: Op[R, A])(f: B => A): Op[R, B] =
Op(f andThen fa.getOp)
}
25
26. Contravariant - example Function1
def function1Contravariant[R]: Contravariant[? => R] =
new Contravariant[? => R] {
def contramap[A, B](r: A => R)(f: B => A) = r compose f
}
26
27. Contravariant - FunctionN parameters all but last
trait Function2[-T1, -T2, +R]{
def apply(v1: T1, v2: T2): R
}
trait Function3[-T1, -T2, -T3, +R]{
def apply(v1: T1, v2: T2, v3: T3): R
}
//...
All parameters are in negative position, co we could define Contravariant
instance for it.
(Or even BiContravariant, TriContravariant, ... if they existed :)
27
29. Contravariant - example Reader - Functor
case class Reader[C, V](run: C => V)
def readerFunctor[C] = new Functor[Reader[C,?]] {
def map[A, B](x: Reader[C, A])(f: A => B): Reader[C, B] =
Reader(x.run andThen f)
}
29
30. Contravariant - example Reader - Monad
case class Reader[C, V](run: C => V)
def readerMonad[C] = new Monad[Reader[C, ?]] {
def map[A, B](x: Reader[C, A])(f: A => B): Reader[C, B] =
Reader(x.run andThen f)
def pure[A](a: A): Reader[C, A] =
new Reader(_ => a)
def flatMap[A, B](ma: Reader[C, A])(f: A => Reader[C, B]) = ???
}
30
31. Contravariant - example Reader - Contravariant
case class Reader[C, V](run: C => V)
def readerContra[V] = new Contravariant[Reader[?, V]] {
def contramap[A, B](fa: Reader[A, V])(f: B => A):
Reader[B, V] = Reader(f andThen fa.run)
}
31
32. Contravariant - example Encoder
Encoder in scodec:
github.com/scodec/scodec/blob/series/1.11.x/shared/src/main/scala/scodec/Encoder.scala#L4
0-L47
has Contravariant instance
github.com/scodec/scodec-cats/blob/master/shared/src/main/scala/scodec/interop/cats/CatsI
nstances.scala#L121-L123
32
33. Contravariant - Resources Haskell
George Wilson: Contravariant Functors: The Other Side of the Coin :
https://www.youtube.com/watch?v=IJ_bVVsQhvc
Michael noyman - Covariance and Contravariance
fpcomplete.com/blog/2016/11/covariance-contravariance
Tom Ellis - 24 Days of Hackage: contravariant
https://ocharles.org.uk/blog/guest-posts/2013-12-21-24-days-of-hackage-contravariant.html
(nice example with actors producing Behaviour a)
https://packdeps.haskellers.com/reverse/contravariant (100+ Haskell libs using
Contravariant package)
33
34. Contravariant - discrimination sort
youtube.com/watch?v=cB8DapKQz-I
Sorting in linear time
Edward Kmett in Haskell
In Scala?
34
35. Functor laws
fmap id = id
fmap f . fmap g = fmap (f . g)
Contravariant laws
contramap id = id
contramap f . contramap g = contramap (g . f)
Contravariant - laws in Haskell
35
39. case class Serializer[A](run: A => Array[Byte])
val strSerial = Serializer[String](_.getBytes)
val intSerial = Serializer[Int](_.toString.getBytes)
Github:
https://github.com/lemastero/scala_typeclassopedia/blob/master/src/test/scala/contravaria
nt/DivideSpec.scala
Divide (1) - Serialization
39
40. val fragmentSerial = Serializer[Fragment] { frag =>
val a1 = strSerial.run(frag.name)
val a2 = intSerial.run(frag.size)
a1 ++ a2
}
val serialized = fragmentSerial.run(Fragment("Area", 52))
new String(serialized ) mustBe "Area52"
Divide (2) - How to combine serializers?
40
46. Define full laws for Divide as in Haskell
hackage.haskell.org/package/contravariant/docs/Data-Functor-Contravariant-Divisible.html#g:4
Not simplified as in Scalaz and Cats!
Contravariant - Divide - Exercise
46
47. Contravariant Functors (1)
def contramap(fa: F[A],f: B => A): F[B] Contravariant
def divide(f: A => (B,C), fb: F[B],fc: F[C]): F[A] Divide
def conquer: F[A] Divisible
There is no Contravariant Monad
There is Contravariant Traversable, Alternative:
https://www.youtube.com/watch?v=cB8DapKQz-I Edward Kmett, Discrimination is
Wrong, 2015
47
51. trait Profunctor[P[_, _]] {
def dimap[X,Y,Z,W](ab: X => Y, cd: Z => W): P[Y, Z] => P[X, W]
}
Profunctor
51
52. trait Profunctor[P[_, _]] {
def dimap[X,Y,Z,W](ab: X => Y, cd: Z => W): P[Y, Z] => P[X, W]
}
● contramap on first type ( P[Y, _] is Contravariant )
● map on second type ( P[_, Z] is Functor )
Profunctor = Contravariant + Functor
52
53. trait Profunctor[P[_, _]] {
def dimap[X,Y,Z,W](ab: X => Y, cd: Z => W): P[Y, Z] => P[X, W]
// derived methods
def lmap[A,B,C](f: A => B): P[B,C] => P[A,C] = dimap[A,B,C,C](f,identity[C])
def rmap[A,B,C](f: B => C): P[A,B] => P[A,C] = dimap[A,A,B,C](identity[A],
f)
}
Profunctor - derived methods
53
54. Haskell
● dimap id id == id
● dimap (f . g) (h . i) == dimap g h . dimap f i
Scala a bit more verbose
Profunctor - Laws
54
55. val function1: Profunctor[Function1] = new Profunctor[Function1] {
def dimap[X, Y, Z, W](f: X => Y, g: Z => W): (Y => Z) => (X => W) =
h => f andThen (g compose h)
def lmap[A,B,C](f: A => B): (B => C) => (A => C) = f andThen
def rmap[A,B,C](f: B => C): (A => B) => (A => C) = f compose
}
Profunctor - Example Function1
55
56. val f: String => Int = _.length
case class Person(name: String, age: Int)
val preF: Person => String = _.name
val postF: Int => Boolean = _ > 5
Profunctor - Example Function1 - setup
56
57. val f: String => Int = _.length
case class Person(name: String, age: Int)
val preF: Person => String = _.name
val postF: Int => Boolean = _ > 5
Profunctor[Function1].dimap(f)(preF)(postF)(
Person("Foo", 100)
)
Profunctor - Example Function1 - true ?
57
58. val f: String => Int = _.length
case class Person(name: String, age: Int)
val preF: Person => String = _.name
val postF: Int => Boolean = _ > 5
Profunctor[Function1].dimap(f)(preF)(postF)(
Person("Foo", 100)
)
// false
Profunctor - Example Function1
58
71. Nice papers
- Different encodings of CT like Sjoerd Visscher data-category:
hackage.haskell.org/package/data-category-0.7/docs/Data-Category.html
- Bifunctors: Joker, Clown, Biff, … - nice papers
- Arrows … - nice papers
- Distributive (Comonad Applicative) - nice papers
- Adjunctions between Monads, Applicative, Arrows - nice papers
- Monad, Applicative, Arrow as Monoid in Monoidal Category with given tensor
(Functor Composition, Sum, Product, Day convolution) - nice papers
- Recursion schemas (Fold, Unfolds, Fix point) - people talk about 3, exists 20+
71
72. Alternative encoding
- Different encodings of CT like Sjoerd Visscher data-category:
hackage.haskell.org/package/data-category-0.7/docs/Data-Category.html
- Proofs of Category Theory in Coq
- Proofs of Category Theory using Univalent Foundations (related to HoTT) in
Coq (UniMath/UniMath) https://github.com/UniMath/UniMath/tree/master/UniMath/CategoryTheory
72
73. How to learn Category Theory
1) Translate to Scala, Kata?
wiki.haskell.org/Typeclassopedia, Edward Kmett libs
2) Describe github.com/lemastero/scala_typeclassopedia
3) In Scala 3
4) Talk using new vocabulary!
73
74. Good general theory does not search for the maximum
generality, but for the right generality.
Saunders Mac Lane
Choose the right level of abstraction, to see the
problem more clearly
Eugenia Cheng, Category in Life
https://www.youtube.com/watch?v=ho7oagHeqNc
Thank you :)
74