Rank N Types 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
There is no Rank N Types in Scala 2, but you can use FunctionK from Cats to mimic Rank N Types.
This is a Rank 1:
// forall a b c. (b, c) -> (a -> a) -> (b, c)
def rank[A,B,C](a: (B, C), doSomething: A => A): (B, C) = (doSomething(a._1), doSomething(a._2))
It won't compile because `doSomething` is rank 1 in rank 2 place.
When compiler tried to figure our what type doSomething(a._1)
really is, since =a._1: B
fix the type doSomething: B => B
so compiler knows that A = B
, but latter on there is a doSomething(a._2)
now compiler confused, A cannot be both B and C while there
are no proof of B = C
.
Simplest way to make Scala 2 is to use Cats FunctionK:
def rank[B,C](a: (B, C), doSomething: Id ~> Id): (B, C) = (doSomething(a._1), doSomething(a._2))
Note that the trick of FunctionK, which basically remove A from type parameter rank[A, B, C]
, which means compiler then has nothing
to confuse about.
While it is not totally free to use cats, there are some boilerplate you have to do when defining doSomething
:
def doSomething: Id ~> Id = new (Id ~> Id) {
def apply[A](a: Id[A]): Id[A] = a
}
Type A
is hidden in the apply
method.
Scala 3
Now in Scala 3, you can simply do the same thing natively with Polymorphic function types.
// rank 2 type (forall a. a -> a)
val id = [A] => (a: A) => a
// forall b c. (b, c) -> (forall a. a -> a) -> (b, c)
def rank2[B,C](a: (B, C), doSomething: [A] => A => A): (B, C) = (doSomething(a._1), doSomething(a._2))
def main(args: Array[String]): Unit = {
println(
rank2((1, "2"), id)
)
}
The trick is now A
is defined [A] => A => A
, no in method type parameters.
It is quite similar to how we define rank n types in Haskell
forall b c. (b, c) -> (forall a. a -> a) -> (b, c)
So basically [A] =>
is the same thing as forall a.
Play around with the above examples in scatie: https://scastie.scala-lang.org/jcouyang/3hNle3faQ7SpS4mCcoMSGA/29
Or clone the repo and play locally: https://github.com/jcouyang/meow