Рассмотрим следующий пример
# LANGUAGE DataKinds, GADTs #
data Phantom = A | B
data Foo (a :: Phantom) where
FooA :: Foo 'A
FooB :: Foo 'B
class PhantomConstraint (a :: Phantom)
instance PhantomConstraint 'A -- Note: No instance for 'B
someFunc :: PhantomConstraint a => Foo a -> ()
someFunc FooA = ()
Если я сделаю что-то вроде этого, GHC жалуется, что совпадения шаблонов не являются исчерпывающими для someFunc
, однако, если я попытаюсь включить случай FooB (который я не хочу делать для конкретных доменных причин), он жалуется, что он не может вывести экземпляр PhantomConstraint
для Foo 'B
Можно ли каким-либо образом сопоставить соответствие шаблону GADT с ограничениями типа текста, чтобы исключить требуемые комбинации соответствия шаблону?
EDIT: более подробно о том, что я хочу сделать. У меня есть ведро типов, которые все связаны друг с другом, но имеют разные свойства. В мире OO это то, что люди используют подтипирование и наследование. Однако в сообществе FP консенсус, похоже, заключается в том, что нет реального хорошего способа делать подтипирование, поэтому в этом случае мне нужно взломать его. Таким образом, у меня есть GADT, который объединяет все типы, но с разными параметрами этого типа. Затем я перехожу к написанию разных типов и экземпляров typeclass в параметрах типа (включенными datakinds, без представления термина). Я хочу быть в состоянии выразить, что некоторые из этих типов из datakinds обладают свойством, которого нет у других, но все они имеют общие общие свойства, поэтому я действительно не хочу разбивать тип. Единственный другой вариант, который я могу предвидеть, - создать таксономию в части типа, но тогда типы DataKinds будут испорчены.
Всего 1 ответ
Я не могу воспроизвести эту проблему. Это загружает без предупреждений или ошибок в GHCi 8.4.3.
# LANGUAGE GADTs, DataKinds, KindSignatures #
# OPTIONS -Wall #
module GADTwarning2 where
data Phantom = A | B
data Foo (a :: Phantom) where
FooA :: Foo 'A
FooB :: Foo 'B
class PhantomConstraint (a :: Phantom)
instance PhantomConstraint 'A -- Note: No instance for 'B
someFunc :: PhantomConstraint a => Foo a -> ()
someFunc FooA = ()
someFunc FooB = ()
Как пояснил luqui в комментарии, мы не можем избежать случая FooB
, так как классы классов открыты, а другой экземпляр может быть добавлен позже другим модулем, что делает шаблон не исчерпывающим.
Если вы абсолютно уверены, что вам не нужны никакие другие экземпляры, кроме одного для A
, вы можете попытаться использовать
class a ~ 'A => PhantomConstraint (a :: Phantom)
Или, если индекс a
может быть 'A
или 'B
, но никогда не является третьим конструктором 'C
, мы можем попытаться подтвердить этот факт:
class PhantomConstraint (a :: Phantom) where
aIsAOrB :: Either (a :~: 'A) (a :~: 'B)
а затем использовать этот член позже.