Это невозможно сделать в общем случае во всех экземплярах MonadIO
из-за типа IO
в отрицательной позиции. Есть несколько библиотек хака, которые делают это для конкретных экземпляров ( monad-control , monad-peel ), но были некоторые дебаты о том, являются ли они семантически звуковыми, особенно с что они справляются с исключениями и подобными странными IO
y вещами.
Редактирование: некоторые люди, похоже, заинтересованы в различии положительной / отрицательной позиции. На самом деле, не так много сказать (и вы, наверное, уже слышали это, но под другим именем). Терминология приходит из мира подтипирования.
Интуиция за подтипированием заключается в том, что «a
является подтипом b
(который я напишу a <= b
), когда a
может быть используется везде, где ожидалось b
». Решающий подтипирование во многих случаях прост; для продуктов (a1, a2) <= (b1, b2)
, когда a1 <= b1
и a2 <= b2
, например, это очень простое правило. Но есть несколько сложных случаев; например, когда мы должны решить, что a1 -> a2 <= b1 -> b2
?
Ну, у нас есть функция f :: a1 -> a2
и контекст, ожидающий функцию типа b1 -> b2
. Таким образом, контекст будет использовать возвращаемое значение f
, как если бы оно было b2
, поэтому мы должны потребовать a2 <= b2
. Трудность в том, что контекст будет поставлять f
с помощью b1
, хотя f
будет использовать его, как если бы он был a1
. Следовательно, мы должны требовать, чтобы b1 <= a1
- который оглядывается назад от того, что вы можете догадаться! Мы говорим, что a2
и b2
являются «ковариантными» или встречаются в «положительном положении», а a1
и b1
являются «контравариантными» или встречаются в «отрицательном положении».
(Быстро в сторону: почему «положительный» и «отрицательный»? Мотивирован умножением. Рассмотрим эти два типа:
f1 :: ((a1 -> b1) -> c1) -> (d1 -> e1)
f2 :: ((a2 -> b2) -> c2) -> (d2 -> e2)
Когда тип f1
должен быть подтипом f2
. Я излагаю эти факты (упражнение: проверьте это, используя правило выше):
e1 <= e2
. d2 <= d1
. c2 <= c1
. b1 <= b2
. a2 <= a1
. e1
находится в положительном положении в d1 -> e1
, который, в свою очередь, находится в положительном положении в типе f1
, причем e1
находится в положительном положении в типе f1
в целом (поскольку он ковариантен по сравнению с приведенным выше фактом), его положение во всем члене является произведением его положения в каждом подтерм: положительное * положительное = положительное. Аналогично, d1
находится в отрицательном положении в [42] который находится в положительном положении по всему типу. отрицательный * положительный = отрицательный, а переменные d
действительно контравариантны. b1
находится в положительном положении в типе a1 -> b1
, который находится в отрицательном положении в (a1 -> b1) -> c1
, который находится в отрицательном положении во всем типе. положительный * отрицательный * отрицательный = положительный, и он ковариантен. Вы получите эту идею.)
Теперь давайте посмотрим на класс MonadIO
:
class Monad m => MonadIO m where
liftIO :: IO a -> m a
Мы можем рассматривать это как явное объявление подтипирования: мы даем способ сделать IO a
подтипом m a
для некоторого конкретного m
. Сразу же мы знаем, что мы можем взять любое значение с конструкторами IO
в положительных позициях и превратить их в m
s. Но это все: у нас нет способа превратить конструкторы отрицательных IO
в m
s - для этого нам нужен более интересный класс.