簡體   English   中英

Haskell中的那種“*”究竟是什么?

[英]What exactly is the kind “*” in Haskell?

在Haskell中,(值級)表達式被分類為類型 ,可以用:: like來表示: 3 :: Int"Hello" :: String(+ 1) :: Num a => a -> a 類似地,類型被分類為種類 在GHCi中,您可以使用以下命令檢查類型表達式的類型:kind:k

> :k Int
Int :: *
> :k Maybe
Maybe :: * -> *
> :k Either
Either :: * -> * -> *
> :k Num
Num :: * -> Constraint
> :k Monad
Monad :: (* -> *) -> Constraint

有一些定義, *是“具體類型”或“值”或“運行時值”。 例如,參見Learn You A Haskell 那是真的嗎? 我們有幾個 問題, 關於種 ,在經過處理的話題,但它會是不錯的規范和准確的解釋*

究竟是什么*是什么意思? 它與其他更復雜的類型有什么關系?

此外, DataKindsPolyKinds擴展是否會改變答案?

首先, *不是通配符! 它通常也被稱為“明星”。

流血邊注 :截至2015年2月,提出了簡化GHC的子系統(7.12或更高版本)的建議 該頁面對GHC 7.8 / 7.10故事進行了很好的討論。 展望未來,GHC可能會放棄類型和種類之間的區別,使用* :: * 參見Weirich,Hsu和Eisenberg,System FC with Explicit Kind Equality

標准:類型表達式的描述。

Haskell 98報告在此上下文中定義*

符號*表示所有nullary類型構造函數的種類。

在這種情況下,“nullary”只是意味着構造函數不帶參數。 Either是二元的; 它可以應用於兩個參數: Either ab Maybe是一元的; 它可以應用於一個參數: Maybe a Int是無效的; 它可以應用於沒有參數。

這個定義本身有點不完整。 包含完全應用的一元,二元等類型構造函數的表達式也具有kind * ,例如Maybe Int :: *

在GHC:包含價值觀的東西?

如果我們圍繞GHC文檔,我們會得到更接近“可以包含運行時值”的定義。 GHC評論頁面“Kinds”表示“' * '是盒裝值。像IntMaybe Float這樣的東西有點* 。” 另一方面, 版本7.4.1GHC用戶指南聲明*是“提升類型”的類型。 (當修改PolyKinds部分時,該段落未被​​保留。)

盒裝價值和提升類型有點不同。 根據GHC評論頁面“TypeType”

如果類型的表示不是指針,則取消裝箱。 未裝箱的類型也未被取消。

如果它具有底部作為元素,則提升類型。 閉包始終具有提升類型:即,Core中的任何let-bound標識符必須具有提升類型。 在操作上,抬起的物體是可以進入的物體。 只有提升類型可以與類型變量統一。

因此, ByteArray# (內存的原始塊類型)被裝箱,因為它被表示為指針,但由於底部不是元素而未被提升

> undefined :: ByteArray#
Error: Kind incompatibility when matching types:
   a0 :: *
   ByteArray# :: #

因此,舊的用戶指南定義似乎比GHC評論更准確: *提升類型的類型。 (反過來說, #是那種未提升的類型。)

請注意,如果類型*總是被提升,對於任何類型的t :: *您可以使用undefined :: t或其他一些機制構建一個“值”來創建底部。 因此,即使像Void這樣的“邏輯上無人居住”的類型也可以有一個值,即底部。

所以看來,是的, *表示可以包含運行時值的類型,如果undefined是您對運行時值的想法。 (這不是一個完全瘋狂的想法,我不認為。)

GHC擴展?

有幾種擴展使這種類型的系統更加生動。 其中一些是平凡的: KindSignatures允許我們編寫類型注釋 ,如類型注釋。

ConstraintKinds添加了類型Constraint ,大致是=>左側的那種。

DataKinds允許我們引入除*#之外的新類型,就像我們可以引入具有datanewtypetype新類型一樣。

使用DataKinds每個data聲明(可能適用的條款和條件)都會生成一個提升的類型聲明。 所以

 data Bool = True | False

介紹了常用的值構造函數和類型名稱; 此外,它產生了一種新Bool和兩種類型: True :: BoolFalse :: Bool

PolyKinds引入了類變量 這只是一種說“對任何類型k ”的方式,就像我們在類型級別上說“for any type t ”一樣。 至於我們的朋友*以及它是否仍然意味着“具有值的類型”,我想你可以說一個類型t :: k其中k是一個種類變量可以包含值,如果k ~ *k ~ #

在種類語言的最基本形式中,只有種類*和種類構造函數-> ,那么*就是那種可以與價值觀建立關系的東西; 沒有什么不同的類型可以是一種價值觀。

存在用於對值進行分類的類型。 為了進行類型檢查,具有相同類型的所有值都是可互換的,因此類型檢查器只需要關注類型,而不是特定值。 因此,我們擁有所有實際價值所在的“價值水平”,以及其類型所在的“類型水平”。 “type-of”關系形成兩個級別之間的鏈接,單個類型是(通常)多個值的類型。 Haskell使這兩個層次非常明確; 這就是為什么你可以有data Foo = Foo Int Chat Bool聲明,你已經聲明了一個類型級別的東西Foo (一個帶有kind *的類型)和一個值級別的東西Foo (一個類型為Int -> Char -> Bool -> Foo的構造函數Int -> Char -> Bool -> Foo )。 涉及的兩個Foo只是簡單地引用不同層次上的不同實體,Haskell將它們完全分開,以至於它總能告訴你所指的是什么級別,因此可以允許(有時令人困惑的)不同級別的事物具有相同的名稱。

但是一旦我們引入了自己具有結構的類型(比如Maybe Int ,它是一個類型構造函數, Maybe應用於Int類型),那么我們在類型級別存在的東西實際上並不存在於一種類型的關系中任何價值觀。 沒有類型只是Maybe值,只有類型Maybe Int (和Maybe BoolMaybe () ,甚至Maybe Void等)的值。 因此,我們需要對類型級別的事物進行分類,原因與我們需要對值進行分類的原因相同; 只有某些類型表達式實際上代表了可以作為值類型的東西,但是它們中的許多可以互換地用於“種類檢查”(對於它被聲明為類型的值級別事物而言它是否是正確的類型是一個不同級別的問題)。 1

所以* (通常被稱為“類型”)是基本類型; 它是所有類型級別的東西,可以說是值的類型。 Int ; 因此它的類型是* Maybe沒有值,但它需要一個參數並生成一個具有值的類型; 這讓我們有點像___ -> * 我們可以通過觀察Maybe的參數被用作Just a出現的值的類型來填補空白,因此它的參數也必須是一種值(帶有種類* ),所以Maybe必須有種類* -> * 等等。

當你處理只涉及星和箭的種類時,只有種類*的類型表達式是值的類型。 任何其他類型(例如* -> (* -> * -> *) -> (* -> *) )僅包含其他“類型級實體”,這些實體不是包含值的實際類型。

PolyKinds我理解, PolyKinds並沒有真正改變這種情況。 它只允許您在類級別進行多態聲明,這意味着它會將變量添加到我們的類型語言中(除了星號和箭頭)。 所以現在我可以考慮類型級別的事物k -> * ; 這可以實例化為* -> *(* -> *) -> *(* -> (* -> *)) -> * 我們獲得了與(a -> b) -> [a] -> [b]類型級別獲得的完全相同的權力; 我們可以用一個包含變量的類型編寫一個map函數,而不必分別編寫每個可能的map函數。 但是仍然只有一種類型包含類型值的類型: *

DataKinds還為這種語言引入了新的東西。 實際上它的作用是讓我們聲明包含新類型級實體的任意新類型(正如普通data聲明允許我們聲明任意新類型,其中包含新的值級實體)。 但它並沒有讓我們通過所有3個層面的實體對應來聲明事物; 如果我有data Nat :: Z | S Nat data Nat :: Z | S Nat並使用DataKinds將其提升到類型級別,然后我們在類型級別上存在兩個名為Nat不同的東西(作為值級別ZSZS (SZ)等類型),並且在種類級別(作為類型級別 ZSZS (SZ)的種類)。 類型級 Z不是任何值的類型; Z駐留在類型級別 Nat (其又是類型* ),而不是類型級別Z 因此, DataKinds將新的用戶定義的東西添加到種類語言中,這種類型語言可以是類型級別的新用戶定義的東西,但仍然存在這樣的情況:可以作為值類型的唯一類型級別的東西是種類的*

我所知道的那種真正改變了這種語言的唯一補充就是@ChristianConkle的回答中提到的種類,比如# (我相信現在還有更多的東西?我不是非常了解“低級“類型,如ByteArray# )。 這些類型具有GHC需要知道的以不同方式處理的值(例如,不假設它們可以被盒裝和延遲評估),即使涉及多態函數,因此我們不能僅僅附加他們需要的知識對這些值的類型進行不同的處理,或者在調用它們的多態函數時會丟失它們。


1因此,“類型”一詞可能有點令人困惑。 有時,它用於指代實際上與價值層面上的事物建立關系的事物(這是當人們說“ Maybe不是類型,它是類型構造函數”時使用的解釋)。 有時它用來指代存在於類型級別的任何東西(在這種解釋下, Maybe實際上是一種類型)。 在這篇文章中,我試圖非常明確地引用“類型級別的東西”,而不是使用“類型”作為簡寫。

對於試圖了解類型的初學者(您可以將它們視為類型的類型),我推薦了“ 了解您的Haskell”一書的這一章。

我個人認為這種方式:

您有具體的類型,例如IntBoolString[Int]Maybe IntEither Int String

所有這些都有* 為什么? 因為他們不能再將任何類型作為參數; Int ,是Int ; 一個Maybe Int是一個Maybe Int 什么Maybe[]Either有關系嗎?

當你說Maybe ,你沒有具體的類型,因為你沒有指定它的參數。 Maybe IntMaybe String是不同的但兩者都有*種類,但是Maybe等待類型*返回種類* 為了澄清,讓我們來看看GHCI的:kind命令可以告訴我們:

Prelude> :kind Maybe Int
Maybe Int :: *
Prelude> :kind Maybe
Maybe :: * -> *

使用列表它是相同的:

Prelude> :k [String]
[String] :: *
Prelude> :k []
[] :: * -> *

怎么樣Either

Prelude> :k Either Int String
Either Int String :: *
Prelude> :k Either Int
Either Int :: * -> *

你可以想到直觀地將Either視為一個帶參數的函數,但參數是類型

Prelude> :k Either Int
Either Int :: * -> *

表示Either Int正在等待類型參數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM