2017-04-17 200 views
3

我通过在斯卡拉的书,并在幺章函数式编程读数的高Kinded类型,他们谈论一个Monoid接口,看起来像这样:在斯卡拉

trait Monoid[A] { 
def op(a1: A, a2: A): A 
def zero: A 
} 

后来,他们定义特定的含半幺群通过扩展这个接口实例。例如,

val intMonoid = new Monoid[Int] { 
    ... 
} 

val listMonoid = new Monoid[List[Int]] { 
    ... 
} 

一对夫妇,我通过本章的10中读出更多的网页,我碰到过“高kinded类型”根据本书是任何类型的,它自身是可以采取其他类型的类型。

trait Foldable[F[_]] { 
... 
... 
} 

所以特质Foldable是根据书中较高的亲属类型。我的问题是,对我来说,Monoid [A]也适合'更高接口类型'的定义,因为它可以采用List [A]。我的理解是否正确?如果不是什么让Scala更高级的KINED类型成为更高的KINED类型?

编辑:所以一元型构造函数采用参数,并产生一个类型。那么这个案例呢?

def listMonoid[A] = new Monoid[List[A]] { 
    ... 
    ... 
} 

那么我的listMonoid函数是HKT吗?

+0

[这个问题](http://stackoverflow.com/questions/6246719/what-is-a-higher-kinded-type-in​​-scala)解释了更高的kinded类型在简单的术语中,准确地回答你的问题问。 –

回答

4

一些术语:

  • 合适类型(例如强度)
  • 一阶类型(例如列表[_]);我们也可以说,第一阶样
  • 更高kinded类型(例如单子[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) 
} 

你参数化Monoid特征与一些A型,它可以(如你注意到)是一个简单的类型,也知道作为适当的类型(例如Int)或参数化类型(例如List[Int],甚至List[Set[Map[Int, Int]])。这使得Monoid成为一阶类型。我们也可以说它是一元类型构造函数 - 它需要一种类型来产生最终类型。

与Monoid不同,某些抽象(例如Monad)需要由的类型构造函数参数化为。 Int不再工作。它需要是“某种类型而不是另一种类型”。由类型构造函数参数化的抽象(即,由“一阶类型”参数化)是较高类型的类型。下面是一个例子:

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本身更高kinded型参数化。

请注意Monad如何在类级别上不关心“内部类型”本身,因为它将通过方法opzero定义。你也可以说trait Monad[M[A]]并且在类别ListMonad(例如,修复它为Int),但是然后你失去了灵活性(你的ListMonad将只能够构建和平面地图List[Int],你需要一个不同的类,例如List[String])。

这不是一个更高级的Monoid类型;它不需要一个类型构造函数来产生一个类型。如果它需要它,那么你永远不会有,例如Monoid[Int],因为Int不是一个类型构造函数。

这有点一方向的关系 - 如果你有一个简单的A型,它代表任何类型的任何命令,所以在这种情况下,List[Int]可以作为一个非常好的A(这样可以List[Set[Option[Try[Int]]]]但是,如果你有。一阶类型M [A],你不能把它变成一个简单的具体类型,如Int,它需要是一个类型构造函数,Monoid可以使用Ints,但Monad不能; monad需要有一个“容器“如Option[_],Try[_],List[_], Set[_]等。这就像子类型 - 香蕉可以作为一种水果,但水果不能作为香蕉。适当的类型可以是任何东西;一阶类型需要一种类型来产生类型;较高主体类型需要一阶类型来生成一个类型,依此类推。

同样重要的是要注意它是一个一元类型的构造函数,这意味着它只需要一种类型(例如,需要两个地图)。

  • 一元一阶类型构造函数* -> *(它采用单一类型,并产生最终的类型,例如,设置)
  • 二进制一阶类型构造是* -> * -> *:类型构造往往与星号和箭头表示(二进制类型构造函数,需要两种类型以产生最终的类型,例如,地图)
  • 一元-kinded更高类型是(* -> *) -> *(采用单个一元类型构造以产生最终的类型,例如,单子)

因此,一阶类型采用简单/具体/适当的类型并产生最终类型,而较高类型的类型则是上一级;它需要一个一阶的类型来产生最终的类型。

编辑:

回答在“编辑”的部分你的问题:OK,我想我知道发生了什么迷惑你。 listMonoid不是一种类型,因此它不能是更高级的类型。这是一种方法。 Monad[List[Int]]是完全解析的类型。 Monad[F[A]]也完全解决了。但是,Monad本身是更高阶的类型。

让我拉动功能的平行。如果您有功能foo(x: Int),则函数调用foo(42)foo(someIntegerValue)会生成具体值。这些类似于Monad[List[Int]]Monad[F[A]]。但foo本身就是一个函数,就像Monad本身就是一个类型构造函数一样。

如果foo取一个简单的值(不是函数),它是一个一阶函数;如果它需要或返回一个函数,那么它是一个更高阶的函数。与类型构造函数相同。如果它需要一个简单的类型,它是一个一阶类型的构造函数。例如:List。如果它需要另一个类型构造函数,它是一个高阶类型构造函数(也称为更高类型的构造函数)。例如:Monad

不要将解析类型与类型构造函数混合使用。认为函数foo是否为高阶是有意义的;这取决于它的参数和返回类型。但认为foo(42)是否为高阶是没有意义的;这不是一个函数,而是一个函数应用程序,它会产生价值。 Monad[List[Int]]不是类型构造函数,而是类型构造函数List对类型构造函数Monad(它是更高阶的)的应用程序。同样,Monoid[List[Int]]不是类型构造器,而是类型构造函数Monoid(它是一阶)的List[Int]类型的应用程序。 更高阶的型号构造函数被称为HKT。谈论HKT并指出一个具体的解析类型(这是由于应用某种类型的构造函数而创建的)是没有意义的。

+0

我仍然无法注意到区分高级亲属类型和我有的Monoid例子的微妙之处! – sparkr

+0

Monad无法使用M,它需要M [_]或M [B],但后者会失去灵活性。前者允许你说“某种类型的构造函数”,将具体类型的实际决定留给其他人(在我们的例子中是方法op和零)。这种类型的构造函数被称为HKT。另一方面,Monoid只需要M;如果你试图强迫Monoid使用HKT,你永远不会有'Monoid [Int]'。 – slouc

+0

@sparkr有区别。你可以有一个'Monoid [List [Int]]'而不是'Monoid [List]'。你可以有一个'Monad [List]'而不是'Monad [List [Int]]'。 “Monoid [List [Int]]”仅适用于整数列表。无论内部是什么,Monad [List]都适用于任何列表。 –