Maybe
,我理解为type constructor
,有* -> *
种。据我所知,它需要一种类型才能生成一种类型。
ghci> :k Maybe
Maybe :: * -> *
ghci> :k Maybe Int
Maybe Int :: *
但是,以下输出意味着什么?
ghci> :k Random
Random :: * -> Constraint
答案 0 :(得分:7)
Haskell中有几种。具体类型具有种类*
,这意味着它已完全应用,因此Int
,[String]
,IO (Maybe Int)
等类型。然后有些类型采用* -> k
形式,其中k
是一种类型变量。这些就像Haskell中的函数一样,它带有一个或多个参数,除了*
具有是一个具体的类型,它不是变量。例子是
> :kind Maybe
Maybe :: * -> *
> :kind Either
Either :: * -> * -> *
> data Foo a b c d e = Foo
> :kind Foo
Foo :: * -> * -> * -> * -> * -> *
然后有一种Constraint
,它由一个或多个 1 构成:
> :kind Num
Num :: * -> Constraint
> :kind Show
Show :: * -> Constraint
> import Control.Monad.State
> :kind MonadState
MonadState :: * -> (* -> *) -> Constriant
最后一个有点复杂,但是把它想象成一个更高阶的函数,其中(* -> *)
意味着第二个参数必须是一个类型构造函数,其中有一个类型参数。
Random
只是一个简单的类型类,因此需要一个完全应用的类型并生成Constraint
。
还有原始类型#
,类型的值是原始的并且被烘焙到GHC中,它们甚至需要语言扩展来解析它们的名称:
> import GHC.Prim
> :set -XMagicHash
> :i Int#
data Int#
> :k Int#
Int# :: #
大多数Haskell程序员都不需要使用它们,但如果您需要为GHC API进行低级编程,它们就可用。
通过几个扩展程序,您甚至可以自己制作
> :set -XDataKinds
> :set -XKindSignatures
> :set -XGADTs
> -- Multiline input in GHCi
> :set +m
> data FlagType = Flag1 | Flag2 deriving (Show)
> data Foo :: FlagType -> * -> *
| F1 :: Int -> Foo 'Flag1 Int
| F2 :: String -> Foo 'Flag2 String
|
> :t F1 1
F1 1 :: Foo 'Flag1 Int
> :t F2 "foo"
F2 "foo" :: Foo 'Flag2 String
Kinds给我们提供了一种类型级编程的形式,有很多技巧可以用来构建漂亮的API。