Дай мне посмотреть, понимаю ли я, что ты здесь ищешь.У вас есть тип AV a
, который описывает вычисления, производящие что-то типа a
, где структура этих вычислений может быть сохранена таким образом, чтобы разрешить проверку.Вам нужен способ поднять произвольные функции в операции на AV
, сохраняя структуру, без необходимости создавать особые случаи для каждой операции.
Обычно для подъема функций в какую-то структуру можно использовать Functor
иApplicative
.Тем не менее, простой способ сделать это включает в себя преобразование структуры и непосредственное применение поднятой функции, не сохраняя приложение функции как часть структуры.
То, что вы хотите, гораздо более неудобно, и вот почему:
Допустим, у нас есть некоторая функция, которую мы хотим поднять, и два абстрактных значения соответствующего типа, к которым она будет применяться:
x :: AV A
x = ...
y :: AV B
y = ...
f :: A -> B -> C
f = ...
Предположим, существует функция liftAV2
, которая делает то, что вы хотите.Мы ожидаем, что тип lift2 f
будет AV A -> AV B -> AV C
, точно так же как liftA
для Applicative
.
Позже мы хотим проверить вычисления, произведенные с использованием lift2 f
, путем восстановлениязначения f
, x
и y
.Допустим, сейчас мы просто хотим извлечь первый аргумент.Предположим, существует функция extractArg1
, которая делает это, например, extractArg1 (liftAV2 f x y)
= x
.Какой тип extractArg1
?Здесь, в context , мы знаем, что он должен иметь тип AV C -> AV A
.Но какой это будет тип?Что-то вроде AV c -> AV a
?Это неправильно, потому что результат не просто любой тип a
, это тот тип, который использовался для создания значения AV c
.Предполагая, что значение, с которым мы работаем, было построено с использованием результата liftAV2 f
, мы знаем, что рассматриваемый тип существует , но у нас нет способа найти его вообще.
Здесь мы входим в страну, соответственно, экзистенциальных типов .Честно говоря, используя их, не меньше, не просто неправильно используя их с классами типов, как это часто бывает.
Вы, вероятно, можете выполнить то, что вам нужно, с некоторыми усилиями, но это становится довольно продвинутой территорией.Возможно, вы захотите использовать GADTs для начала, хотя я думаю, что вы, возможно, уже делаете это.Кроме того, работа с экзистенциальными типами чрезвычайно неуклюжа, потому что вы ограничены знанием только того, что они представляют в ограниченном контексте.
В вашем конкретном случае, возможно, будет проще дать AV
два параметра типа : один, представляющий конечный тип вычисления, и один, представляющий структуру вычисления, например:
data f :$ x = ...
data AV structure result where
...
AVApply :: AV f (a -> b) -> AV x a -> AV (f :$ x) b
Затем для проверки вычислений вы можете посмотреть на первый тип, чтобызнаю, что у тебя есть;для построения вычислений вы можете посмотреть на вторую, чтобы убедиться, что типы совпадают.Функция оценки будет иметь тип, подобный AV t a -> a
, отбрасывая структуру.Вы также можете «распаковать» вычисления, используя тип структуры, отбрасывая тип результата, если вам нужно разобрать структуру, чтобы, скажем, довольно распечатать ее.