Могут ли действия IO в отрицательной позиции дать неожиданные результаты? - PullRequest
0 голосов
/ 26 января 2019

Кажется, есть некоторые недокументированные сведения о разнице между Monad IO и IO.Замечания здесь и здесь ) намекают на то, что IO a может использоваться в отрицательной позиции, но может иметь непредвиденные последствия:

Цитировать Snoyman 1 :

Однако мы знаем, что некоторые потоки управления (например, обработка исключений) не используются, поскольку они несовместимы с MonadIO.(Причина: MonadIO требует, чтобы IO находился в положительном, а не отрицательном положении.) Это позволяет нам знать, например, что foo безопасно использовать в монаде на основе продолжения, такой как ContT или Conduit.

И Kmett 2 :

Я склонен экспортировать функции с ограничением MonadIO ... всякий раз, когда ему не нужно выполнять IO-подобное действие в отрицательной позиции (в качестве аргумента).

Когда мой код должен принять другое монадическое действие в качестве аргумента, я обычно должен остановиться и подумать об этом.

Есть ли опасность в такомфункции, о которых должны знать программисты?

Означает ли это, например, что выполнение произвольного действия, основанного на продолжении, может переопределить поток управления, давая неожиданные результаты таким образом, чтобы защитить интерфейс на основе Monad IO от безопасности?

1 Ответ

0 голосов
/ 26 января 2019

Есть ли опасность в таких функциях, о которых должны знать программисты?

Нет опасности.Скорее наоборот, смысл, который делают Снойман и Кметт, состоит в том, что Monad IO не позволяет вам поднимать вещи с IO в отрицательном позитиве.

Предположим, вы хотите обобщить putStrLn :: String -> IO ().Вы можете, потому что IO находится в положительной позиции:

putStrLn' :: MonadIO m => String -> m ()
putStrLn' str = liftIO (putStrLn str)

Теперь предположим, что вы хотите обобщить handle :: Exception e => (e -> IO a) -> IO a -> IO a.Вы не можете (по крайней мере, только с MonadIO):

handle' :: (MonadIO m, Exception e) => (e -> m a) -> m a -> m a
handle' handler act = liftIO (handle (handler . unliftIO) (unliftIO act))

unliftIO :: MonadIO m => m a -> IO a
unliftIO = error "MonadIO isn't powerful enough to make this implementable!"

Вам нужно что-то еще.Если вам интересно, как вы это сделаете, взгляните на реализацию функций в lifted-base.Например: handle :: (MonadBaseControl IO m, Exception e) => (e -> m a) -> m a -> m a.

...