

file:grokking-monad/scala/en/part3.org
source link: https://blog.oyanglul.us/grokking-monad/scala/en/part3.html
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"
andsbt "testOnly *Free"
for exercises
package free
import monad._
import cats._
import org.scalatest._
class `4-1-Kind` extends AsyncFlatSpec with Matchers {
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.
behavior of "FunctionK"
it should "create a FunctionK from Box[_] to Sphere[_]" in {
Printable.format(Sphere("hill")) shouldBe "\"hill\""
}
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.
implicit def spherePrintable[A](implicit p: Printable[Box[A]],
fn: Sphere[A] => Box[A]): Printable[Sphere[A]] = ???
let's create another function that use Sphere ~> Box
, to make tuple of sphere (Sphere[String], Sphere[Int])
printable
implicit def tuplePrintable[A, B, C](
implicit p: Printable[Box[String]],
fn: Sphere[C] => Box[C])): Printable[(Sphere[A], Sphere[B])] = {
val tupleOfSphereToBox = (tupleOfSphere: (Sphere[A], Sphere[B])) => {
val box1 = fn(tupleOfSphere._1)
val box2 = fn(tupleOfSphere._2)
Box(s"(${box1.value}, ${box2.value})")
}
Contravariant[Printable].contramap(p)(tupleOfSphereToBox)
}
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
behavior of "Rank N Type"
it should "able to print a tuple of Sphere" in {
Printable.format((Sphere("hill"), Sphere(1))) shouldBe "\"(hill, 1)\""
}
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)
"Natural Transformation" should "satisfy law" in {
implicit val functorBox: Functor[Box] = new Functor[Box] {
def map[A, B](fa: Box[A])(f: A => B) =
Box(f(fa.value))
}
import cats.syntax.functor._
Sphere.sphereToBox(Sphere(100).map(_ + 1)) shouldBe Sphere.sphereToBox(Sphere(100)).map(_ + 1)
}
}
Recommend
-
7
Type classes Type classes is somewhat like an FP design pattern, with type classes you can extend new functionality without touch any of your existing code or 3rd party library. Type classes vs OO classes Add new meth...
-
16
Table of Contents This is the literal programming file that generates 4-1-kind.scala and
-
21
第三部分:搞基猫呢Advanced Monads 第二部分介绍了一些实用的monad instances,这些 monad 都通过同样的抽象方式,解决了分离计算与副作用的工作。 通过它们可以解决大多数的基本问题,但是正对于复杂业务逻辑,我们可能还需要一些更...
-
13
Applied Category Theory in Scala Applied Category Theory in Scala Choice Table of Contents
-
5
第二部分:食用猫呢 Practical Monads
-
7
第三部分:搞基猫呢Advanced Monads 第二部分介绍了一些实用的monad instances,这些 monad 都通过同样的抽象方式,解决了分离计算与副作用的工作。 通过它们可以解决大多数的...
-
6
Type classes Type classes is somewhat like an FP design pattern, with type classes you can extend new functionality without touch any of your existing code or 3rd party library. Type classes vs...
-
16
Type Classes in Scala 3 Type Classes in Scala 3 Table of Contents I'm recently migrating some libs and projects to Scala 3, I gues...
-
15
Get Started with Http4s follow the structure of Getting Started with Rails This guide covers getting up and...
-
7
第一部分:猫论Catergory Theory ...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK