Scala 中的高级类型
Higher Kinded Types in Scala
我正在阅读《Scala 函数式编程》一书,在 Monoids 一章中,他们讨论了一个如下所示的 Monoid 接口:
trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
后来,他们通过扩展这个接口来定义特定的Monoid实例。例如,
val intMonoid = new Monoid[Int] {
...
}
val listMonoid = new Monoid[List[Int]] {
...
}
我读完第 10 章后又读了几页,我发现 'higher kinded types' 根据书中的说法,它是任何类型,它本身是一种可以采用其他类型的类型。
trait Foldable[F[_]] {
...
...
}
所以根据这本书,可折叠性状是一种更高级的类型。我的问题是,Monoid[A] 对我来说也符合 'higher kinded type' 定义,因为它可以采用 List[A]。我的理解正确吗?如果不是,是什么让更高种类的类型成为 Scala 中的更高种类类型?
编辑:所以一元类型构造函数接受一个参数并生成一个类型。那么这里的这个案例呢?
def listMonoid[A] = new Monoid[List[A]] {
...
...
}
那么我的 listMonoid 函数是 HKT 吗?
一些术语:
- 正确的类型(例如 Int)
- 一阶类型(例如List[_]);我们也可以说一阶种类
- 更高种类的类型(例如 Monad[M[_])
当你说
trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
val listMonoid = new Monoid[List[Int]] {
def op(l: List[Int], l2: List[Int]) = List(1,2)
def zero = List(1,2)
}
你正在用某种类型 A 参数化 Monoid
特征,它可以(正如你注意到的那样)是一个简单类型,也称为正确类型(例如 Int
)或参数化类型(例如 List[Int]
,甚至 List[Set[Map[Int, Int]]
)。这使得 Monoid
成为一阶类型。我们也可以说它是一个一元类型构造函数——它需要一个类型来产生最终类型。
与 Monoid 不同,某些抽象(例如 Monad)需要由类型构造函数参数化。 Int 不再起作用了。它需要是 "some type than can produce another type"。由类型构造函数参数化的抽象(即由 "first-order type" 参数化)是 higher-kinded 类型。这是一个例子:
trait Monad[M[_]] {
def op[A, B](m: M[A], f: A => M[B]): M[B]
def zero[A](a: A): M[A]
}
object ListMonad extends Monad[List] {
def op[A, B](m: List[A], f: A => List[B]) = m.flatMap(f)
def zero[A](a: A) = List[A](a)
}
val listMonad = ListMonad.zero(42)
val result = ListMonad.op(listMonad, (i: Int) => List(i - 1, i, i + 1))
// result = List(41, 42, 43)
所以Monad
被一阶类型(一元类型构造函数)参数化,这使得Monad
本身是高等类型.
请注意 Monad
如何不真正关心 class 级别上的 "inner type" 本身,因为它将由方法 op
和 [=21] 定义=].您也可以在 class ListMonad
的定义点说 trait Monad[M[A]]
和 "fix" 类型 A(例如,将其固定为 Int),但是您正在失去灵活性(您的然后 ListMonad
将只能构造和 flatMap a List[Int]
而你需要一个不同的 class 来表示 List[String]
).
这与 Monoid 不同,Monoid 不是更高级的类型;它不需要类型构造函数来生成类型。如果它需要它,那么你永远不会有一个,比如说,Monoid[Int]
,因为 Int 不是类型构造函数。
还要注意我是怎么说 Monad 需要一个 unary 类型构造函数的,这意味着它只需要一种类型(不像 Map 需要两种类型)。类型构造函数通常用星号和箭头表示:
- 一元一阶类型构造函数是
* -> *
(它采用单一类型并生成最终类型,例如 Set) - binary first-order type constructor is
* -> * -> *
(a binary type constructor, takes two types to produce the final type, e.g. Map) - 一元高级类型是
(* -> *) -> *
(采用单个一元类型构造函数来生成最终类型,例如 Monad)
等等
因此,一阶类型采用 simple/concrete/proper 类型并生成最终类型,而更高种类的类型则高出一个级别;它需要一个一阶类型来产生最终类型。
编辑:
在 "edit" 部分回答您的问题:好的,我想我知道是什么让您感到困惑。 listMonoid
不是类型,所以它不可能是更高级的类型。这是一种方法。 Monad[List[Int]]
是完全解析的类型。 Monad[F[A]]
也完全解决了。但是,Monad
本身就是高阶类型。
让我把并行函数拉出来。如果您有一个函数 foo(x: Int)
,那么 foo(42)
或 foo(someIntegerValue)
等函数调用会产生具体值。这些类似于 Monad[List[Int]]
和 Monad[F[A]]
。然而,foo
本身是一个函数,就像 Monad
本身是一个类型构造函数一样。
如果foo
取简单值(不是函数),则为一阶函数;如果它接受或 returns 一个函数,那么它就是一个高阶函数。与类型构造函数相同。如果它采用简单类型,则它是一阶类型构造函数。示例:List
。如果它采用另一个类型构造函数,则它是高阶类型构造函数(也称为高种类类型)。示例:Monad
.
不要将已解析的类型与类型构造函数混合使用。思考函数 foo
是否高阶是有意义的;这取决于它的参数和 return 类型。但是考虑 foo(42)
是否为高阶是没有意义的;这不是一个功能,而是一个功能应用程序,它会产生价值。 Monad[List[Int]]
不是类型构造函数,而是类型构造函数 List
对类型构造函数 Monad
(高阶)的应用。同样,Monoid[List[Int]]
不是类型构造器,而是类型 List[Int]
对类型构造器 Monoid
的应用(一阶)。 高阶类型构造函数 称为 HKT。谈论 HKT 并指出具体的已解析类型(由于应用某种类型构造函数而创建)是没有意义的。