У меня есть конструктор GADT, представляющий ту же идею, но мне нужны два из них, потому что тип гибок в зависимости от контекста. Посмотрите этот надуманный пример:
data A
data B
data C
data Thing a where
AFoo :: String -> Thing A
Bar :: Float -> Thing A
BFoo :: String -> Thing B
Baz :: Int -> Thing B
Bah :: Char -> Thing C
AFoo
и BFoo
представляют одну и ту же концепцию базовых данных: некоторые данные представлены строкой. Но в этом контексте это нечто большее. В идеале AFoo
и BFoo
должны быть объединены в Foo
, потому что они представляют собой одно и то же, но мне нужен тот, который печатает как Thing A
, и тот, который печатает как Thing B
. Как упоминалось ранее, некоторые контексты, в которых может использоваться Foo
, требуют Thing A
, а некоторые требуют Thing B
, поэтому для удовлетворения системы типов для каждого должен существовать один конструктор.
Конечно, я мог бы написать функцию, которая "приводит" к желаемому типу, переключая конструктор при необходимости:
fooAsThingA :: Thing a -> Maybe (Thing A)
fooAsThingA t@(AFoo _) = Just t
fooAsThingA (BFoo s) = Just $ AFoo s
fooAsThingA _ = Nothing
(и аналогично для fooAsThingB
)
Но это некрасиво, потому что это требует Maybe
, что я должен размножать, потому что не все Thing B
s могут стать Thing A
s (на самом деле, только BFoo
может).
В идеале я хотел бы написать:
data A
data B
data C
data Thing a where
Foo :: String -> Thing ??
Bar :: Float -> Thing A
Baz :: Int -> Thing B
Bah :: Char -> Thing C
Но мне неясно, что бы я поставил вместо ??
. Предположительно, это какой-то способ представления объединения A
и B
(и, возможно, для этого нужно изменить типы других конструкторов для этого GADT, но это нормально).
Просто для ясности, если бы вышеупомянутое было верным, я бы ожидал, учитывая следующие функции:
processThingA :: Thing A -> String
processThingB :: Thing B -> Int
processThingC :: Thing C -> Float
Тогда я смогу сделать все следующее:
processThingA $ Foo "Hello"
processThingB $ Foo "World"
processThingA $ Bar 3.14
processThingB $ Baz 42
processThingC $ Bah '\n'
tl; dr В первом фрагменте можно ли объединить AFoo
и BFoo
в Foo
, который печатает как Thing A
и Thing B
?
edit: Примечание: есть другие EmptyDataDecls
, чем A
и B
, которые я использую для Thing a
. Я добавил C
в качестве примера.