Экзистенция, вероятно, не то, что вы хотите здесь; нет никакого способа «наблюдать» фактические типы, привязанные к e
или w
в значении Dangerous a
, поэтому вы полностью ограничены операциями, данными вам Error
и Show
.
Другими словами, единственное, что вы знаете о w
, это то, что вы можете превратить его в String
, так что это может быть просто String
(игнорируя приоритет для упрощения вещей), и единственный Что вы знаете о e
, так это то, что вы можете превратить его в String
, вы можете превратить в него String
s, и у вас есть выдающееся значение этого (noMsg
). Невозможно утверждать или проверять, что эти типы такие же, как и любые другие, поэтому после помещения их в Dangerous
невозможно восстановить какую-либо специальную структуру, которая может иметь эти типы.
Сообщение об ошибке говорит о том, что, по сути, ваш тип для runDangerous
утверждает, что вы можете превратить Dangerous
в (Either e a, [w])
для любого e
и w
, которые есть соответствующие случаи. Это явно не так: вы можете превратить Dangerous
в этот тип только для один выбор из e
и w
: тот, с которым он был создан. w1
только потому, что ваш тип Dangerous
определен с переменной типа w
, как и runDangerous
, поэтому GHC переименовывает одну из них, чтобы избежать конфликта имен.
Тип, который вам нужно дать runDangerous
выглядит следующим образом:
runDangerous
:: (forall e w. (Error e, Show e, Show w) => (Either e a, [w]) -> r)
-> Dangerous a -> r
, что, учитывая функцию, которая будет принимать значение типа (Either e a, [w])
для любого вариантов e
и w
, если у них есть заданные экземпляры, и Dangerous a
, производит результат этой функции. Это довольно сложно обдумать!
Реализация так же просто, как
runDangerous f (Dangerous m) = f $ runState (runErrorT m) []
, что является тривиальным изменением вашей версии. Если это работает для вас, отлично; но я сомневаюсь, что экзистенциальный - это правильный способ достичь того, что вы пытаетесь сделать.
Обратите внимание, что вам понадобится {-# LANGUAGE RankNTypes #-}
для выражения типа runDangerous
. Кроме того, вы можете определить другой экзистенциальный для вашего типа результата:
data DangerousResult a = forall e w. (Error e, Show e, Show w) =>
DangerousResult (Either e a, [w])
runDangerous :: Dangerous a -> DangerousResult a
runDangerous (Dangerous m) = DangerousResult $ runState (runErrorT m) []
и извлеките результат с помощью case
, но вам нужно быть осторожным, иначе GHC начнет жаловаться на то, что вы позволили e
или w
бежать - что эквивалентно попытке пройти недостаточно полиморфная функция к другой форме runDangerous
; то есть тот, который требует больше ограничений на то, что e
и w
выходит за рамки того, что гарантирует тип runDangerous
.