我们可以为 kind 参数编写单一的、综合的实例*
:
class MyClass d where
f :: d -> Int
instance MyClass (Maybe d) where
f _ = 3
test1 :: Maybe d -> Int
test1 x = f x
这编译得很好,请注意我们不需要对 test1 的 (MyClass (Maybe d)) 约束,因为编译器会找到任何匹配的实例Maybe d
。
我们甚至可以为类型构造函数的类参数编写一个包罗万象的实例:
class MyClass2 (a :: * -> *) where
g :: Tagged (a Int) Int
instance MyClass2 a where
g = Tagged 3
test2 :: Int
test2 = untag (g :: Tagged (Maybe Int) Int)
这也编译得很好,并且 test2 不需要 (MyClass2 Maybe) 约束,因为编译器会找到匹配的实例。
考虑以下玩具代码来计算(提升)类型列表的长度:
{-# LANGUAGE TypeOperators,
DataKinds,
KindSignatures,
GADTs,
FlexibleInstances,
FlexibleContexts,
ScopedTypeVariables #-}
import Data.Tagged
import Data.Proxy
data HList :: [*] -> * where
HNil :: HList '[]
HCons :: a -> HList as -> HList (a ': as)
class ListLen a where
len :: Tagged a Int
instance ListLen (HList '[]) where
len = Tagged 0
instance (ListLen (HList as)) => ListLen (HList (a ': as)) where
len = Tagged (1+(untag (len :: Tagged (HList as) Int)))
test3 :: Int
test3 = untag (len :: Tagged (HList '[Int, Double, Integer]) Int)
test4 :: (Proxy (HList qs)) -> Int
test4 (_ :: Proxy (HList qs)) = untag (len :: Tagged (HList qs) Int) -- error occurs here
这会导致错误:
No instance for (ListLen (HList qs))
arising from a use of `len'
Possible fix: add an instance declaration for (ListLen (HList qs))
...
如果我们注释掉 的签名test4
,GHCi 将类型推断为
test4 :: (ListLen (HList qs)) => (Proxy (HList qs)) -> Int
但这不应该是必要的。显然,我已经为它编写了ListLen
匹配任何可能的类型列表的实例:一个“空列表”案例和一个“缺点”案例。如果我将ListLen
参数的种类更改为有种类[*]
(即删除HList
包装器ListLen
及其实例),问题仍然存在。
如果我们注释掉test4
,test3
编译并运行良好:因为我(本质上)为我如何构造类型列表提供了明确的语法('[Int,Double,Integer]
),编译器能够找到匹配的实例。
我正在尝试编写为我构建类型列表的代码,因此我不必写出显式类型列表语法。然而,使用显式语法似乎是 GHC 能够匹配这些综合实例的唯一方法。也许我错过了一个案例?我没有使用的语法?
我能做些什么来让 GHC 意识到我有一个适用于所有类型的实例[*]
?
我正在使用 GHC 7.4.2。这可能与之前关于解构非提升类型的帖子有关。关于类型提升的论文可以在这里找到。