HOME | EDIT | RSS | INDEX | ABOUT | GITHUB

Scala 3 原生 Rank N Types


可以跑的源码在这里 👉 https://github.com/jcouyang/meow


Scala 2

没有 Rank N Types, 所以我们需要使用 Cats 的 FunctionK 来做一些事情.

比如:

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

是编译不了的, 因为这只有 rank 1, 数 rank 几 很容易, 有几个forall, 这里只有一个, 也就是, A, B, C 是一个 rank.

同一个 rank 的毛病是, 编译器在看到 doSomething(a._1) 时, 由于 =a._1: B 时就决定了这时 doSomething: B => B 就 决定了 A = B, 再看后面的 doSomething(a._2) 编译就挂了, 因为我决定好了是 B 了你给我个 C 当然不行.

Scala 2 解决的办法可以通过 Cats 的 FunctionK:

def rank[B,C](a: (B, C), doSomething: Id ~> Id): (B, C) = (doSomething(a._1), doSomething(a._2))

可以看见 FunctionK 的小诡计把 A 从 rank[A, B, C 的类型参数上拿掉了, 也就是编译器完全不需要在编译这个函数的时候确定下来 doSomething 的 A 类型是啥.

使用 cats 也不是完全免费, 你在定义 doSomething 的时候要多些一堆代码

def rankNId: Id ~> Id = new (Id ~> Id) {
  def apply[A](a: Id[A]): Id[A] = a
}

定义个 id 这么累, 你可以通过这个过程看到, 哦, 原来 A 类型被藏到这里了.

Scala 3

但是现在 Dotty 中, 已经实现了 Rank N Types, 叫 Polymorphic function types

// rank 2 type (forall a. a -> a)
val id = [T] => (t: T) => t

// forall b c. (b, c) -> (forall a. a -> a) -> (b, c)
def rank2[B,C](a: (B, C), doSomething: [T] => T => T): (B, C) = (doSomething(a._1), doSomething(a._2))

def main(args: Array[String]): Unit = {
  println(
    rank2((1, "2"), id)
  )
}

是可以完美编译运行的.

不信? 可以自己在 scastie 上试试: https://scastie.scala-lang.org/jcouyang/3hNle3faQ7SpS4mCcoMSGA/29