2017-04-17 160 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]都適用於任何列表。 –