请使用以下代码:
{-# LANGUAGE KindSignatures, DataKinds #-}
data Nat = O | S Nat
class NatToInt (n :: Nat) where
natToInt :: n -> Int
instance NatToInt O where
natToInt _ = 0
instance (NatToInt n) => NatToInt (S n) where
natToInt _ = 1 + natToInt (undefined :: n)
GHC通知我们,它期望OpenKind
的类型规范中的natToInt
而不是Nat
,因此拒绝编译。这可以通过某种铸造来解决:
data NatToOpen :: Nat -> *
然后将n
替换为NatToOpen n
- s中的natToInt
。
问题1:有没有办法在不使用类型级包装器的情况下在任何函数中指定*
以外的类型?
问题2:在我看来,非类函数将很乐意使用任何类型的类型,例如:
foo :: (a :: *) -> (a :: *)
foo = id
bar = foo (S O)
但是在内部类中,编译器会抱怨类型不匹配,如上所述。这是为什么?似乎非类函数在各种类型中都是正确的多态,因为上面我实际上指定了 *
,它仍然适用于Nat
- s,就好像种类被简单地忽略了。
答案 0 :(得分:6)
值总是有类型*
(可能有一些涉及拆箱的奇怪异常?),所以没有什么可以将函数应用于或作为其他类型的参数。
在最后一个示例中,您将foo
应用于Nat
的未提升版本:值为S
和O
,其类型< / em>是Nat
,其类型为*
。在类定义中,您使用签名提供Nat
种类,这意味着提升版本,其中S
和O
是类型。
NatToOpen
类型只是以通常的方式使用幻像类型,但使用非*
类型的幻像类型参数。
这种区别对DataKinds
也不是新的。例如,没有类型为Maybe :: * -> *
的值,与类型forall a. Maybe a :: *
不同,后者是Nothing
的类型。