Каковы определения для >> = и возврата для монады IO? - PullRequest
37 голосов
/ 12 февраля 2012

Посмотрев, как определяются монады List и Maybe, мне, естественно, стало интересно, как определяются операции >>= и return для монады IO.

Ответы [ 3 ]

44 голосов
/ 12 февраля 2012

Конкретной реализации для IO не существует;это абстрактный тип, точная реализация которого не определена в отчете Haskell.В самом деле, ничто не мешает реализации, реализующей IO и ее экземпляр Monad в качестве примитивов компилятора, вообще без реализации на Haskell.

По сути, Monad используется как интерфейс до IO, что само по себе не может быть реализовано в чистом Haskell.Это, вероятно, все, что вам нужно знать на этом этапе, и погружение в детали реализации, скорее всего, приведет к путанице, а не к пониманию.представляет IO a как функцию, похожую на State# RealWorld -> (# State# RealWorld, a #) (в качестве возвращаемого типа используется распакованный кортеж ), но это вводит в заблуждение;это деталь реализации, и эти значения State# RealWorld на самом деле не существуют во время выполнения.IO is не монада состояний, 1 в теории или на практике.

Вместо этого GHC использует нечистые примитивы для реализации этих операций ввода-выводаоперации;State# RealWorld «значения» служат только для остановки операторов переупорядочения компилятора путем введения зависимостей данных от одного оператора к другому.

Но если вы действительно хотите увидеть реализацию GHC return и (>>=),вот они:

returnIO :: a -> IO a
returnIO x = IO $ \ s -> (# s, x #)

bindIO :: IO a -> (a -> IO b) -> IO b
bindIO (IO m) k = IO $ \ s -> case m s of (# new_s, a #) -> unIO (k a) new_s

, где unIO просто разворачивает функцию из конструктора IO.

Важно отметить, что IO a представляет описание нечистых вычислений, которые можно запустить, чтобы получить значение типа a.Тот факт, что есть способ получить значения из внутреннего представления GHC IO, не означает, что это справедливо в целом, или что вы можете сделать это для всех монад.Это просто деталь реализации со стороны GHC.

1 Монада состояний - это монада, используемая для доступа и изменения состояния в серии вычислений;он представлен как s -> (a, s) (где s - это тип состояния), что выглядит очень похоже на тип, который GHC использует для IO, поэтому возникает путаница.

13 голосов
/ 12 февраля 2012

Вы будете разочарованы, но монада >>= в IO не так уж интересна. Процитирую источник GHC:

{- |
A value of type @'IO' a@ is a computation which, when performed,
does some I\/O before returning a value of type @a@.

There is really only one way to \"perform\" an I\/O action: bind it to
@Main.main@ in your program.  When your program is run, the I\/O will
be performed.  It isn't possible to perform I\/O from an arbitrary
function, unless that function is itself in the 'IO' monad and called
at some point, directly or indirectly, from @Main.main@.

'IO' is a monad, so 'IO' actions can be combined using either the do-notation
or the '>>' and '>>=' operations from the 'Monad' class.
-}
newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))

Это означает, что IO монада объявлена ​​как экземпляр State State# монады, и ее >>= определена там (и ее реализацию довольно легко угадать) .

См. IO внутри статьи на вики Haskell для более подробной информации о монаде IO. Также полезно взглянуть на Документы на Haskell , где каждая позиция имеет маленькую ссылку "Источник" справа.

Обновление: И вот еще одно разочарование - мой ответ, потому что я не заметил '#' в State#. Однако IO ведет себя как State монада, несущая абстрактное RealWorld состояние

Как писал @ehird State# является внутренним компонентом компилятора, а >>= для монады IO определено в GHC.Base module:

instance  Monad IO  where
    {-# INLINE return #-}
    {-# INLINE (>>)   #-}
    {-# INLINE (>>=)  #-}
    m >> k    = m >>= \ _ -> k
    return    = returnIO
    (>>=)     = bindIO
    fail s    = failIO s

returnIO :: a -> IO a
returnIO x = IO $ \ s -> (# s, x #)

bindIO :: IO a -> (a -> IO b) -> IO b
bindIO (IO m) k = IO $ \ s -> case m s of (# new_s, a #) -> unIO (k a) new_s
1 голос
/ 12 февраля 2012

Они не делают ничего особенного, а просто для последовательности действий. Было бы полезно, если бы вы думали о них с разными именами:

>> = становится "и затем, используя результат предыдущего действия,"

>> становится "а потом"

return становится "ничего не делать, но результат бездействия"

Это превращает эту функцию:

main :: IO ()
main = putStr "hello"
   >>  return " world"
   >>= putStrLn

становится:

main :: IO ()
main = putStr "hello" and then,
       do nothing, but the result of doing nothing is " world"
       and then, using the result of the previous action, putStrLn

В конце концов, в IO нет ничего волшебного. Это так же волшебно, как точка с запятой в C.

...