Ошибки типов с экзистенциальными типами в Haskell
Я борюсь с экзистенциальными типами в моей программе. Я думаю, что пытаюсь сделать что-то очень разумное, однако я не могу пройти мимо проверки типов:(
У меня есть тип данных, который имитирует монаду
data M o = R o | forall o1. B (o1 -> M o) (M o1)
Теперь я создаю для него Контекст, аналогичный описанному в статье на Haskell Wiki о Zipper, однако для простоты я использую функцию вместо структуры данных -
type C o1 o2 = M o1 -> M o2
Теперь, когда я пытаюсь написать функцию, которая разделяет значение данных на контекст и подзначение, проверщик типов жалуется:
ctx :: M o -> (M o1 -> M o, M o1)
ctx (B f m) = (B f, m) -- Doesn't typecheck
Ошибка -
Couldn't match type `o2' with `o1'
`o2' is a rigid type variable bound by
a pattern with constructor
B :: forall o o1. (o1 -> M o) -> M o1 -> M o,
in an equation for `ctx'
at delme1.hs:6:6
`o1' is a rigid type variable bound by
the type signature for ctx :: M o -> (M o1 -> M o, M o1)
at delme1.hs:6:1
Expected type: M o2
Actual type: M o1
In the expression: m
In the expression: (B f, m)
Тем не менее, я могу обойти это так -
ctx (B f m) = let (c,m') = ctx m in ((B f) . c, m') -- OK
Почему это второе определение проверяет тип, а не первое?
Кроме того, если я пытаюсь конвертировать ctx
для полной функции, проверяя R, я снова получаю ошибку проверки типов -
ctx (R o) = (id, R o) -- Doesn't typecheck
Ошибка -
Couldn't match type `o' with `o1'
`o' is a rigid type variable bound by
the type signature for ctx :: M o -> (M o1 -> M o, M o1)
at delme1.hs:7:1
`o1' is a rigid type variable bound by
the type signature for ctx :: M o -> (M o1 -> M o, M o1)
at delme1.hs:7:1
In the first argument of `R', namely `o'
In the expression: R o
In the expression: (id, R o)
Как я могу обойти эту ошибку?
Любая помощь приветствуется!
1 ответ
Давайте сначала посмотрим на неудачные случаи. Оба из них терпят неудачу по одной и той же причине, которая становится понятнее, когда вы добавляете в неявное forall
в подписи типа:
ctx :: forall o o1. M o -> (M o1 -> M o, M o1)
т.е. ваша функция должна работать не только для некоторых o1
, но для любого o1
,
В вашем первом случае,
ctx (B f m) = (B f, m)
мы знаем это
f :: (o2 -> M o)
а такжеm :: M o2
для какого-то типаo2
, но мы должны быть в состоянии предложить любой типo1
поэтому мы не можем предположить, чтоo1 ~ o2
,Во втором случае
ctx (R o) = (id, R o)
Здесь мы знаем, что
o :: o
но, опять же, функция должна быть определена для любогоo1
поэтому мы не можем предположить, чтоo ~ o1
,
Ваш обходной путь, кажется, работает только потому, что он вызывает сам себя, подобно индуктивному доказательству. Но без базового случая это просто циклическое рассуждение, и вы не можете написать базовый вариант для этой функции, потому что нет способа построить M o1
из M o
для любой комбинации o
а также o1
без использования нижнего значения.
Что вам, вероятно, понадобится, это определить другой экзистенциальный тип для контекста, вместо того, чтобы использовать просто кортеж. Не уверен, что это будет работать для ваших нужд, но это компилирует 1, по крайней мере:
data Ctx o = forall o1. Ctx (M o1 -> M o) (M o1)
ctx :: M o -> Ctx o
ctx (B f m) = case ctx m of Ctx c m' -> Ctx (B f . c) m'
ctx (R o) = Ctx id (R o)
1 Попробуйте использовать let
вместо case
за смешную ошибку GHC:)