Contravariant Functors in Scala
prepending wagons to the train like a CT pro
Piotr Paradziński
ScalaC03.03.2020
1
● PR’s to Scalaz 7 / Cats
○ Scalaz7 github.com/scalaz/scalaz/pull/2020 Day convolution (0,5 year fight translate from Haskell)
○ Scalaz7 github.com/scalaz/scalaz/pull/2028 add Strong Profunctor laws
○ Scalaz7 github.com/scalaz/scalaz/pull/2029 Density comonad
○ Cats github.com/typelevel/cats/pull/2640 replace Strong Profunctor laws (based on CT)
● type_classopedia - wiki about abstractions from CT github.com/lemastero/scala_typeclassopedia
●
● Small fix in (broken links this is how you start!, docs):
○ Haskell Profunctors github.com/ekmett/profunctors/pull/65 github.com/ekmett/profunctors/pull/66
○ 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
○ steshaw.org/plt/ (github.com/steshaw/plt/pulls?utf8=✓&q=+author%3Alemastero)
○ cohomolo-gy/haskell-resources github.com/cohomolo-gy/haskell-resources/pull/3
○ passy/awesome-recurion-schemas github.com/passy/awesome-recursion-schemes/pull/22
○ Agda https://github.com/agda/agda/pull/3751
○ awesome-provable https://github.com/awesomo4000/awesome-provable/pull/1
○ Andrej Baure HoTT lectures: https://github.com/andrejbauer/homotopy-type-theory-course/pull/3
○ Cubical TT course by groupoid https://github.com/groupoid/groupoid.space/pull/20
○ statebox/awesome-applied-CT https://github.com/statebox/awesome-applied-ct/issues?q=author%3Alemastero
INPUT => OUTPUT
3
INPUT => OUTPUT
monix.Task[A], Future[A]
Functor[F], Monad[F],
Comonad[F],
Reader
4
INPUT => OUTPUT
monix.Task[A], Future[A]
Functor[F], Monad[F],
Comonad[F],
Reader
Why we focus only on output?
5
INPUT => OUTPUT
Think
backwards!
6
Contravariant Functors
7
Contravariant Functors
8
Prepend wagos to end Append new Locomotive to begin
(at the end) INPUT (to beginning) OUTPUT
Predicate - checks property of type A
case class Predicate[A](fun: A => Boolean)
9
Predicate - complicated one ;)
case class Predicate[A](fun: A => Boolean)
val hasEnoughLength: Predicate[Int] = Predicate(a => a > 3)
10
Can we use length to create Predicate[String]?
case class Predicate[A](fun: A => Boolean)
val hasEnoughLength: Predicate[Int] = Predicate(a => a > 3)
def len(s: String): Int = s.length
val isValidCode: Predicate[String] = ???
11
Predicate - can we map (define Functor)?
case class Predicate[A](fun: A => Boolean)
import cats.Functor
val predicateFuncor: Functor[Predicate] = new Functor[Predicate] {
def map[A, B](fa: Predicate[A])(f: A => B): Predicate[B] = ???
}
12
Predicate - but we can prepend!
case class Predicate[A](fun: A => Boolean)
val hasEnoughLength: Predicate[Int] = Predicate(a => a > 3)
def len(s: String): Int = s.length
val isValidCode: Predicate[String] =
Predicate[String](len andThen hasEnoughLength.fun)
13
case class Predicate[A](fun: A => Boolean)
def contramap[A, B](pred: Predicate[A])(fba: B => A): Predicate[B] =
Predicate[B](fba andThen pred.fun)
Predicate - generalize prepend
14
Example2 - Show
trait Show[F] {
def show(f: F): String
}
Scalaz github.com/scalaz/scalaz/blob/series/7.3.x/core/src/main/scala/scalaz/Show.scala#L51-L53
15
Example2 - Can we reuse existing Show?
trait Show[A] {
def show(a: A): String
}
case class Processor(name: String)
case class Multicore(name: String, coreCount: Int)
val sp: Show[Processor] = new Show[Processor] {
override def show(a: Processor): String = s"Processor ${a.name}"
}
def asProcessor(mc: Multicore): Processor = Processor(s"${mc.coreCount} x ${mc.name}")
16
Example2 - Can we define Functor for Show?
trait Show[A] {
def show(a: A): String
}
val showFunctor: Functor[Show] = new Functor[Show] {
override def map[A, B](fa: Show[A])(f: A => B): Show[B] = ???
}
17
Example2 - no! but we can prepend
trait Show[A] {
def show(a: A): String
}
def contramap[A, B](fa: Show[A])(f: B => A): Show[B] = new Show[B] {
override def show(a: B): String = fa.show(f(a))
}
18
Common pattern - prepend to input 1/3
trait Show[A] {
def show(a: A): String
}
def prepend[A, B](fa: Show[A])(f: B => A): Show[B] = new Show[B] {
override def show(a: B): String = fa.show(f(a))
}
19
Common pattern - prepend to input 2/3
case class Predicate[A](fun: A => Boolean)
def prepend[A, B](pred: Predicate[A])(fba: B => A): Predicate[B] =
Predicate[B](fba andThen pred.fun)
20
Common pattern - prepend to input Contravariant
trait InputPrepender[F[_]] {
def prepend[A, B] (fa: F[A])(f: B => A): F[B]
}
21
Mathematicians and names :)
trait Contravariant[F[_]] {
def contramap[A, B] (fa: F[A])(f: B => A): F[B]
}
22
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]
}
23
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)
24
Contravariant Functor
25
Prepend Append
contramap map
to INPUT to OUTPUT
Exercises - Contravariant for scala.math.Equiv
trait Equiv[T] {
def equiv(x: T, y: T): Boolean
}
val EquivContra: Contravariant[Equiv] = new Contravariant[Equiv] {
override def contramap[A, B](fa: Equiv[A])(f: B => A): Equiv[B] = ???
}
Much more:
https://github.com/lemastero/contravariant_profunctor_exercises/blob/master/src/main/scala/contra_pro/Exercise1_Contravariant.
scala
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
}
27
Contravariant - example Function1
def function1Contravariant[R]: Contravariant[? => R] =
new Contravariant[? => R] {
def contramap[A, B](r: A => R)(f: B => A) = r compose f
}
Glorified compose of Function1 !!!
28
Contravariant - example Reader (1)
case class Reader[C, V](run: C => V)
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)
}
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]) = ???
}
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)
}
32
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
33
Contravariant - laws in Scala
34
Cats:
github.com/typelevel/cats/blob/master/laws/src/main/scala/cats/laws/ContravariantLaws.scala
Scalaz 7:
github.com/scalaz/scalaz/blob/series/7.3.x/core/src/main/scala/scalaz/Contravariant.scala#L59-L68
scala_typeclassopedia:
github.com/lemastero/scala_typeclassopedia#contravariant-contravariant-functor
Contravariant - laws in Scala
35
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 :)
36
Contravariant * Contravariant = Covariant
Compose contravariant functors:
https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/Contravariant.scala#L11-L15
Input of your input is not my input
But it’s input is ...
https://github.com/lemastero/scala_typeclassopedia/blob/master/src/main/scala/contravariant/HoFStructure.scala
37
Contravariant Functors everywhere
● Kafka producer
● HTTP endpoints
● DB Repository
● S3 client
● ...
38
Contravariant Functors everywhere but why?
We don’t want to map … and change HttpApi[Json] into HttpApi[Ints]
We might want to:
● provide part of configuration
● change config file path to parsed case class with configuration
● set execution context for all operations
● provide dependency like AvroKafkaDeserializer and schema registry config
(clients don’t have to worry about it)
● add defaults and require only Option[Config]
● ...
39
Contravariant - examples in open source libs
Encoder in Scodec:
github.com/scodec/scodec/blob/series/1.11.x/shared/src/main/scala/scodec/Encoder.scala#L40-L47
Schedule/ZSink/ZQueue in ZIO (contramap, provideSome):
https://github.com/zio/zio/search?q=contramap&unscoped_q=contramap
Logger
https://github.com/zio/zio-logging/issues/7
JSON Encoder in Circe
https://github.com/circe/circe/blob/master/modules/core/shared/src/main/scala/io/circe/Encoder.scala#L53-L55
40
https://gitlab.haskell.org/ghc/ghc/issues/14767
https://github.com/ghc/ghc/blob/master/libraries/base/changelog.md#41200-21-september-2018
Contravariant part of Haskell GHC 8.6.1 in 2018
41
Contravariant - discrimination sort
youtube.com/watch?v=cB8DapKQz-I
Sorting in linear time
Edward Kmett in Haskell
In Scala?
42
Divide
(Contravariant Semigroupal - in Cats)
43
case class Serializer[A](run: A => Array[Byte])
val strSerial = Serializer[String](_.getBytes)
val intSerial = Serializer[Int](_.toString.getBytes)
GOTO Github:
https://github.com/lemastero/contravariant_profunctor_exercises/blob/master/src/test/scala/contra_pro/DivideSpec.scala
Divide (1) - Serialization
44
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?
45
trait Divide[F[_]] extends Contravariant[F] {
def divide[A,B,C](f: A => (B,C), fb: F[B], fc: F[C]): F[A]
}
Divide (3) - abstraction
46
val fragmentDivide: Divide[Serializer] = new Divide[Serializer] {
def divide2[A1, A2, Z](s1: => Serializer[ A1], s2: => Serializer[ A2])(f: Z
=> (A1, A2)): Serializer[ Z] = Serializer{ frag =>
val (a1,a2) = f(frag)
s1.run(a1) ++ s2.run(a2)
}
}
Divide (4)
47
val fragAsTuple: Fragment => (String, Int) =
frag => (frag.name, frag.size)
val fragSerial: Serializer[Fragment] =
Divide[Serializer].divide(strSerial, intSerial)(fragAsTuple)
Divide (5)
48
val fragAsTuple: Fragment => (String, Int) =
frag => (frag.name, frag.size)
val fragSerial: Serializer[Fragment] =
Divide[Serializer].divide(strSerial, intSerial)(fragAsTuple)
val serialized = fragSerial.run(Fragment("Area", 52))
new String(serialized ) mustBe "Area52"
Divide - Run
49
https://github.com/lemastero/scala_typeclassopedia/blob/master/Contravariant.MD
#divide-contravariant-apply
Divide - Laws & derived methods
50
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 - Challenge
51
Big Picture
52
Functor - Signature
def map[A,B](fa: F[A])(f: A => B): F[B] Functor
53
Apply - Signature
def map[A,B](fa: F[A])(f: A => B): F[B] Functor
def ap[A,B](ff: F[A => B])(fa: F[A]): F[B] Apply
54
Applicative - Signature
def map[A,B](fa: F[A])(f: A => B): F[B] Functor
def ap[A,B](ff: F[A => B])(fa: F[A]): F[B] Apply
def pure[A](value: A): F[A] Applicative
55
Contravariant Functors - arrows reversed
//def map (F[A], A => B): F[B] Functor
def contramap (F[A], B => A): F[B] Contravariant (Contravariant Functor)
//def map2 ((A,B) => Z, F[A], F[B]): F[Z] Apply (not ap!)
def divide (Z => (A,B), F[A], F[B]): F[Z] Divide (Contravariant Apply)
//def pure: ( () => A ) => F[A] Applicative
def conquer: ( A => () ) => F[A] Divisible (Contravariant Applicative)
56
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 :)
57
Thank you :)
58

Contravariant functors in scala

  • 1.
    Contravariant Functors inScala prepending wagons to the train like a CT pro Piotr Paradziński ScalaC03.03.2020 1
  • 2.
    ● PR’s toScalaz 7 / Cats ○ Scalaz7 github.com/scalaz/scalaz/pull/2020 Day convolution (0,5 year fight translate from Haskell) ○ Scalaz7 github.com/scalaz/scalaz/pull/2028 add Strong Profunctor laws ○ Scalaz7 github.com/scalaz/scalaz/pull/2029 Density comonad ○ Cats github.com/typelevel/cats/pull/2640 replace Strong Profunctor laws (based on CT) ● type_classopedia - wiki about abstractions from CT github.com/lemastero/scala_typeclassopedia ● ● Small fix in (broken links this is how you start!, docs): ○ Haskell Profunctors github.com/ekmett/profunctors/pull/65 github.com/ekmett/profunctors/pull/66 ○ 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 ○ steshaw.org/plt/ (github.com/steshaw/plt/pulls?utf8=✓&q=+author%3Alemastero) ○ cohomolo-gy/haskell-resources github.com/cohomolo-gy/haskell-resources/pull/3 ○ passy/awesome-recurion-schemas github.com/passy/awesome-recursion-schemes/pull/22 ○ Agda https://github.com/agda/agda/pull/3751 ○ awesome-provable https://github.com/awesomo4000/awesome-provable/pull/1 ○ Andrej Baure HoTT lectures: https://github.com/andrejbauer/homotopy-type-theory-course/pull/3 ○ Cubical TT course by groupoid https://github.com/groupoid/groupoid.space/pull/20 ○ statebox/awesome-applied-CT https://github.com/statebox/awesome-applied-ct/issues?q=author%3Alemastero
  • 3.
  • 4.
    INPUT => OUTPUT monix.Task[A],Future[A] Functor[F], Monad[F], Comonad[F], Reader 4
  • 5.
    INPUT => OUTPUT monix.Task[A],Future[A] Functor[F], Monad[F], Comonad[F], Reader Why we focus only on output? 5
  • 6.
  • 7.
  • 8.
    Contravariant Functors 8 Prepend wagosto end Append new Locomotive to begin (at the end) INPUT (to beginning) OUTPUT
  • 9.
    Predicate - checksproperty of type A case class Predicate[A](fun: A => Boolean) 9
  • 10.
    Predicate - complicatedone ;) case class Predicate[A](fun: A => Boolean) val hasEnoughLength: Predicate[Int] = Predicate(a => a > 3) 10
  • 11.
    Can we uselength to create Predicate[String]? case class Predicate[A](fun: A => Boolean) val hasEnoughLength: Predicate[Int] = Predicate(a => a > 3) def len(s: String): Int = s.length val isValidCode: Predicate[String] = ??? 11
  • 12.
    Predicate - canwe map (define Functor)? case class Predicate[A](fun: A => Boolean) import cats.Functor val predicateFuncor: Functor[Predicate] = new Functor[Predicate] { def map[A, B](fa: Predicate[A])(f: A => B): Predicate[B] = ??? } 12
  • 13.
    Predicate - butwe can prepend! case class Predicate[A](fun: A => Boolean) val hasEnoughLength: Predicate[Int] = Predicate(a => a > 3) def len(s: String): Int = s.length val isValidCode: Predicate[String] = Predicate[String](len andThen hasEnoughLength.fun) 13
  • 14.
    case class Predicate[A](fun:A => Boolean) def contramap[A, B](pred: Predicate[A])(fba: B => A): Predicate[B] = Predicate[B](fba andThen pred.fun) Predicate - generalize prepend 14
  • 15.
    Example2 - Show traitShow[F] { def show(f: F): String } Scalaz github.com/scalaz/scalaz/blob/series/7.3.x/core/src/main/scala/scalaz/Show.scala#L51-L53 15
  • 16.
    Example2 - Canwe reuse existing Show? trait Show[A] { def show(a: A): String } case class Processor(name: String) case class Multicore(name: String, coreCount: Int) val sp: Show[Processor] = new Show[Processor] { override def show(a: Processor): String = s"Processor ${a.name}" } def asProcessor(mc: Multicore): Processor = Processor(s"${mc.coreCount} x ${mc.name}") 16
  • 17.
    Example2 - Canwe define Functor for Show? trait Show[A] { def show(a: A): String } val showFunctor: Functor[Show] = new Functor[Show] { override def map[A, B](fa: Show[A])(f: A => B): Show[B] = ??? } 17
  • 18.
    Example2 - no!but we can prepend trait Show[A] { def show(a: A): String } def contramap[A, B](fa: Show[A])(f: B => A): Show[B] = new Show[B] { override def show(a: B): String = fa.show(f(a)) } 18
  • 19.
    Common pattern -prepend to input 1/3 trait Show[A] { def show(a: A): String } def prepend[A, B](fa: Show[A])(f: B => A): Show[B] = new Show[B] { override def show(a: B): String = fa.show(f(a)) } 19
  • 20.
    Common pattern -prepend to input 2/3 case class Predicate[A](fun: A => Boolean) def prepend[A, B](pred: Predicate[A])(fba: B => A): Predicate[B] = Predicate[B](fba andThen pred.fun) 20
  • 21.
    Common pattern -prepend to input Contravariant trait InputPrepender[F[_]] { def prepend[A, B] (fa: F[A])(f: B => A): F[B] } 21
  • 22.
    Mathematicians and names:) trait Contravariant[F[_]] { def contramap[A, B] (fa: F[A])(f: B => A): F[B] } 22
  • 23.
    Contravariant - likea 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] } 23
  • 24.
    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) 24
  • 25.
  • 26.
    Exercises - Contravariantfor scala.math.Equiv trait Equiv[T] { def equiv(x: T, y: T): Boolean } val EquivContra: Contravariant[Equiv] = new Contravariant[Equiv] { override def contramap[A, B](fa: Equiv[A])(f: B => A): Equiv[B] = ??? } Much more: https://github.com/lemastero/contravariant_profunctor_exercises/blob/master/src/main/scala/contra_pro/Exercise1_Contravariant. scala 26
  • 27.
    Contravariant - exampleFunction1 def function1Contravariant[R]: Contravariant[? => R] = new Contravariant[? => R] { def contramap[A, B](r: A => R)(f: B => A) = r compose f } 27
  • 28.
    Contravariant - exampleFunction1 def function1Contravariant[R]: Contravariant[? => R] = new Contravariant[? => R] { def contramap[A, B](r: A => R)(f: B => A) = r compose f } Glorified compose of Function1 !!! 28
  • 29.
    Contravariant - exampleReader (1) case class Reader[C, V](run: C => V) 29
  • 30.
    Contravariant - exampleReader - 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) } 30
  • 31.
    Contravariant - exampleReader - 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]) = ??? } 31
  • 32.
    Contravariant - exampleReader - 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) } 32
  • 33.
    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 33
  • 34.
  • 35.
  • 36.
    Contravariant - FunctionNparameters 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 :) 36
  • 37.
    Contravariant * Contravariant= Covariant Compose contravariant functors: https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/Contravariant.scala#L11-L15 Input of your input is not my input But it’s input is ... https://github.com/lemastero/scala_typeclassopedia/blob/master/src/main/scala/contravariant/HoFStructure.scala 37
  • 38.
    Contravariant Functors everywhere ●Kafka producer ● HTTP endpoints ● DB Repository ● S3 client ● ... 38
  • 39.
    Contravariant Functors everywherebut why? We don’t want to map … and change HttpApi[Json] into HttpApi[Ints] We might want to: ● provide part of configuration ● change config file path to parsed case class with configuration ● set execution context for all operations ● provide dependency like AvroKafkaDeserializer and schema registry config (clients don’t have to worry about it) ● add defaults and require only Option[Config] ● ... 39
  • 40.
    Contravariant - examplesin open source libs Encoder in Scodec: github.com/scodec/scodec/blob/series/1.11.x/shared/src/main/scala/scodec/Encoder.scala#L40-L47 Schedule/ZSink/ZQueue in ZIO (contramap, provideSome): https://github.com/zio/zio/search?q=contramap&unscoped_q=contramap Logger https://github.com/zio/zio-logging/issues/7 JSON Encoder in Circe https://github.com/circe/circe/blob/master/modules/core/shared/src/main/scala/io/circe/Encoder.scala#L53-L55 40
  • 41.
  • 42.
    Contravariant - discriminationsort youtube.com/watch?v=cB8DapKQz-I Sorting in linear time Edward Kmett in Haskell In Scala? 42
  • 43.
  • 44.
    case class Serializer[A](run:A => Array[Byte]) val strSerial = Serializer[String](_.getBytes) val intSerial = Serializer[Int](_.toString.getBytes) GOTO Github: https://github.com/lemastero/contravariant_profunctor_exercises/blob/master/src/test/scala/contra_pro/DivideSpec.scala Divide (1) - Serialization 44
  • 45.
    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? 45
  • 46.
    trait Divide[F[_]] extendsContravariant[F] { def divide[A,B,C](f: A => (B,C), fb: F[B], fc: F[C]): F[A] } Divide (3) - abstraction 46
  • 47.
    val fragmentDivide: Divide[Serializer]= new Divide[Serializer] { def divide2[A1, A2, Z](s1: => Serializer[ A1], s2: => Serializer[ A2])(f: Z => (A1, A2)): Serializer[ Z] = Serializer{ frag => val (a1,a2) = f(frag) s1.run(a1) ++ s2.run(a2) } } Divide (4) 47
  • 48.
    val fragAsTuple: Fragment=> (String, Int) = frag => (frag.name, frag.size) val fragSerial: Serializer[Fragment] = Divide[Serializer].divide(strSerial, intSerial)(fragAsTuple) Divide (5) 48
  • 49.
    val fragAsTuple: Fragment=> (String, Int) = frag => (frag.name, frag.size) val fragSerial: Serializer[Fragment] = Divide[Serializer].divide(strSerial, intSerial)(fragAsTuple) val serialized = fragSerial.run(Fragment("Area", 52)) new String(serialized ) mustBe "Area52" Divide - Run 49
  • 50.
  • 51.
    Define full lawsfor 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 - Challenge 51
  • 52.
  • 53.
    Functor - Signature defmap[A,B](fa: F[A])(f: A => B): F[B] Functor 53
  • 54.
    Apply - Signature defmap[A,B](fa: F[A])(f: A => B): F[B] Functor def ap[A,B](ff: F[A => B])(fa: F[A]): F[B] Apply 54
  • 55.
    Applicative - Signature defmap[A,B](fa: F[A])(f: A => B): F[B] Functor def ap[A,B](ff: F[A => B])(fa: F[A]): F[B] Apply def pure[A](value: A): F[A] Applicative 55
  • 56.
    Contravariant Functors -arrows reversed //def map (F[A], A => B): F[B] Functor def contramap (F[A], B => A): F[B] Contravariant (Contravariant Functor) //def map2 ((A,B) => Z, F[A], F[B]): F[Z] Apply (not ap!) def divide (Z => (A,B), F[A], F[B]): F[Z] Divide (Contravariant Apply) //def pure: ( () => A ) => F[A] Applicative def conquer: ( A => () ) => F[A] Divisible (Contravariant Applicative) 56
  • 57.
    Good general theorydoes 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 :) 57
  • 58.