Могу ли я параметризировать пустой тип ограничения?
У меня есть класс для очередей, который позволяет экземпляру определять ограничения, которые он накладывает на элементы. Например, приоритетная очередь требует, чтобы ее элементы были упорядочены:
{-# 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
который не накладывает никаких ограничений на свой аргумент?
1 ответ
Это должно работать:
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
, Однако я не рекомендую этого, поскольку считаю, что он менее читабелен, чем использование пользовательского класса.
(Это требует включения некоторых расширений, как Бенджамин Ходжсон указывает ниже в комментарии.)