4

FunctionK in Scala 3

 3 years ago
source link: https://blog.oyanglul.us/scala/dotty/en/functionk
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.
FunctionK in Scala 3

FunctionK in Scala 3

Table of Contents

中文 | English


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.

Source code 👉 https://github.com/jcouyang/meow


Scala 2

Last section we've seen the usable of FunctionK and Id in Cats when we were trying to implement rank N types in Scala 2.

But the actual usable 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, you will get exactly same thing 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 much easier to define a functionK too:

// 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:

1

When F and G are Functor, FunctionK[F, G] is the natural transformation from F to G, noted F ~> G.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK