我有一个队列类,它允许实例定义它对元素的约束。例如,优先级队列要求其元素可订购:
{-# LANGUAGE MultiParamTypeClasses, ConstraintKinds, FunctionalDependencies #-}
class Queue q c | q -> c where
empty :: q a
qpop :: c a => q a -> Maybe (a, q a)
qpush :: c a => a -> q a -> q a
data PriorityQueue a = ...
instance Queue PriorityQueue Ord where
...
这是一个魅力:在PriorityQueue
的实例声明中,我可以使用Ord
的成员(例如(>)
)对队列元素进行操作。
我一直试图定义一个对其元素没有要求的队列:
newtype LIFO a = LIFO [a]
instance Queue LIFO () where
empty = LIFO []
qpop (LIFO []) = Nothing
qpop (LIFO (x:xs)) = Just (x, LIFO xs)
qpush x (LIFO xs) = LIFO $ x:xs
此操作失败,并显示GHC的以下错误消息:
The second argument of `Queue' should have kind `* -> Constraint',
but `()' has kind `*'
In the instance declaration for `Queue LIFO ()'
此错误消息对我有意义。 Eq
接受一个类型参数(我们通常会写Eq a => ...
),而()
没有参数 - 它是一个普通的旧类型不匹配。
我在写一个忽略第二个参数的类型函数时遇到了麻烦,这个函数允许我写instance Queue LIFO (Const ())
:
{-# LANGUAGE TypeFamilies, KindSignatures, PolyKinds #-}
type family Const a b :: k -> k2 -> k
type instance Const a b = a
我发现类型族和类型多态的这种相互作用非常漂亮,所以当它没有工作时我感到非常失望(我真的认为它会!):
Expecting two more arguments to `a'
The first argument of `Const' should have kind `*',
but `a' has kind `k0 -> k1 -> k0'
In the type `a'
In the type instance declaration for `Const'
我有一种感觉,这最后一个例子就像语法错误一样愚蠢(我是新来的类型系列)。我如何写一个Constraint
对其论证没有任何限制?
答案 0 :(得分:8)
这应该有效:
class NoConstraint a where
instance NoConstraint a where
instance Queue LIFO NoConstraint where
...
以上定义了所有类型都满足的约束。因此,c a
的义务始终可以解除c = NoConstraint
。
此外,由于该类中没有成员,因此它应该具有零(或几乎为零)的运行时成本。
"约束"您尝试使用的()
不被视为GHC设置的空约束,而是单元类型() :: *
。这会导致Const () :: k2 -> *
,从而触发类型错误。
如果您不想使用自定义类,可以尝试例如Const (Eq ())
或Const (Num Int)
k2 -> Constraint
,{{1}}。不过,我不建议这样做,因为我觉得它比使用自定义类的可读性差。
(这需要启用一些扩展,正如Benjamin Hodgson在评论中指出的那样。)