FunctionK in Scala 3
I'm recently migrating some libs and projects to Scala 3, I guess it would be very helpful to me or anyone interested to learn some new functional programming features that Scala 3 is bringing to us.
- Rank N Types
- FunctionK
- GADT
- Phantom Types
- Dependent Types
- "First Class" Types
- Type Classes
- Generic Type Class Derivation
Source code 👉 https://github.com/jcouyang/meow
Scala 2
In the last section we saw the usage of FunctionK and Id in Cats when we were trying to implement rank N types in Scala 2.
But the actual use of FunctionK is like:
import cats.~>
def tupledOptionToList[B,C](a: (Option[B], Option[C]), fnk: Option ~> List): (List[B], List[C]) =
(fnk(a._1), fnk(a._2))
Same reason as rank n types, normal function Option[A] => List[A]
won't work since A
should not be in the same rank with B
and C
.
So Option ~> List~
When F
and G
are Functor
, FunctionK[F, G]
is the natural transformation from F
to G
, noted F ~> G
.
hide the A
type, means the function only map Kind to other Kind, and leave what ever type A
not changed,
which is also why is called FunctionK(K for Kind).
Scala 3
While in Scala 3, since we have Rank N Types, a.k.a Polymorphic function types, it is very easy to implement ~>
.
Basically you don't even need to implement, just a type alias will do:
// kind: * -> *
// FunctionK: (* -> *) -> (* -> *)
type ~>[F[?],G[?]] = [A] => F[A] => G[A]
Use the ~>
in infix position, then you get exactly what Cats FunctionK does:
object Main {
// rank 2 type (forall a. Option a -> List a)
val optionToList: Option ~> List = [A] => (a: Option[A]) => a.toList
// forall b c. (Option b, Option c) -> (forall a. Option a -> List a) -> (List b, List c)
def tupledOptionToList[B,C](a: (Option[B], Option[C]), fnk: Option ~> List): (List[B], List[C]) =
(fnk(a._1), fnk(a._2))
def main(args: Array[String]): Unit = {
println(
tupledOptionToList((Some(1), Some("2")), optionToList)
)
}
}
No Cats needed, even better, it's also much easier to define a functionK:
// Cats
val optionToList: Option ~> List = new (Option ~> List) {
def apply(a: Option[A]): List[A] = a.toList
}
// Scala 3
val optionToList: Option ~> List = [A] => (a: Option[A]) => a.toList
Try the above examples online: https://scastie.scala-lang.org/jcouyang/W5jIXajVTU64g8KZe8V7Kw/9
Or clone and run it locally: https://github.com/jcouyang/meow
Footnotes:
When F
and G
are Functor
, FunctionK[F, G]
is the natural transformation from F
to G
, noted F ~> G
.