10

Grokking Monad in Scala - Free

 3 years ago
source link: https://blog.oyanglul.us/grokking-monad/scala/en/part3
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Table of Contents

This is the literal programming file that generates 4-1-kind.scala and 4-2-free.scala

you can actually run sbt "testOnly *Kind" and sbt "testOnly *Free" for exercises

Kinds are types for types. Like a function for parameters of type and return a type. e.g. List[_] is a Kind, it has a hole represented as _ in it, which is like parameter in function. If you fill the hole with a type String, then you will get a type List[String].

Recall our Printable[_] kind defined in 3.1.Functor, we'vecreatede implement of typeclass Contravariant over kind Printable, which means no matter what type you fill into the hole of Printable[_], it should fulfill constrain of typeclass Contravariant.

You can also define a function over Kind F[_] -> G[_], which is called FunctionK , just like function over type.

FunctionK

to define a FuntionK in cats, we can simply use fish arrow ~>

e.g. a FuntionK from List to Option

import cats.~>
val first = new (List ~> Option) {
  def apply[A](l:List[A]): Option[A] = l.headOption
}

Kind Projector

you'll notice that we've include a compiler plugin kind-projector https://github.com/non/kind-projector in build.sbt

it provides us pretty syntactic suger for such case

val first = Lambda[List ~> Option](_.headOption)

which will be expanded to exactly the same code as what we defined before.

Rank N Type

But why this is useful than function, the fnk in spherePrintable can be easily replace with a simple function and it should behave still the same.

let's create another function that use Sphere ~> Box, to make tuple of sphere (Sphere[String], Sphere[Int]) printable

you will get some compile error as such

[error] /Users/jichao.ouyang/Develop/scala-dojo/src/main/scala/Free.scala:21:35: type mismatch;
[error]  found   : free.Sphere[A]
[error]  required: free.Sphere[C]
[error]       val box1 = fn(tupleOfSphere._1)
[error]                                   ^
[error] /Users/jichao.ouyang/Develop/scala-dojo/src/main/scala/Free.scala:22:35: type mismatch;
[error]  found   : free.Sphere[B]
[error]  required: free.Sphere[C]
[error]       val box2 = fn(tupleOfSphere._2)
[error]                                   ^

Apparently when your fn is sticked to a C type, it's not convertible to neither A nor B type, even if you stick it to A type, then the tupleOfSphere._2 can't convert to A either.

This is Rank 1 Type, because all types A, B, C are fixed in the same rank of tuplePrintable's polymorphism.

To make such code compile, you'll need to make fn as Rank 2 Type, which means fn will not be fixed in the first rank of tuplePrintable polymorphism, it's type polymorphism will be in another rank totally independent from the tuplePrintable

Natural Transformation

If your kinds are happened to be a Functor, then this functionK becomes Natural Transformation

There's nothing different except that Natural Transformation will provide you a property:

applying FunctionK before or after a Functor map makes no difference

hence fnk(fa.map(f)) is exactly the same as fnk(fa).map(f)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK