对于IO monad,>> =和return的定义是什么?

Eri*_*ikR 38 monads haskell

在看到如何定义List和Maybe monad之后,我自然会对IO monad 的操作>>=return定义感到好奇.

ehi*_*ird 45

没有具体的实施IO; 它是一个抽象类型,确切的实现未被Haskell报告定义.实际上,没有什么能阻止实现实现IO及其Monad实例作为编译器原语,根本没有Haskell实现.

基本上,Monad用作接口IO,不能本身以纯的Haskell来实现.这可能是您在此阶段需要了解的全部内容,而深入了解实施细节可能会让您感到困惑,而不是提供洞察力.

也就是说,如果你看一下GHC的源代码,你会发现它代表IO a一个看起来像的函数State# RealWorld -> (# State# RealWorld, a #)(使用一个未装箱的元组作为返回类型),但这是误导性的; 它是一个实现细节,这些State# RealWorld值实际上并不存在于运行时.IO不是一个状态单子,1在理论上还是在实践中.

相反,GHC使用不纯的原语来实现这些IO操作; 在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
Run Code Online (Sandbox Code Playgroud)

这里unIO简单地从里面解开的功能IO构造.

重要的是要注意,它IO a表示可以运行以产生类型值的不纯计算的描述a.事实上,有一种方法可以从GHC的内部表示中获取价值IO并不意味着这种情况一般,或者你可以为所有monad做这样的事情.这纯粹是GHC的实施细节.

1状态单子是用于访问和跨越一系列计算的突变状态的单子; 它表示为s -> (a, s)(在哪里s是状态的类型),它看起来非常类似于GHC用于的类型IO,因此混淆.

  • @ JohnF.Miller:"IO"的状态monad模型可以对顺序计算进行建模(即使"IO"在实践中没有以这种方式实现),但它不能合理地表示并发性.无论如何,它肯定比有用的更令人困惑,因为这样的'IO`模型比状态monad更受限制(或者你可以做一些事情,比如在执行某些方面之后将世界状态恢复 - 影响,扭转它们). (7认同)
  • 我不同意IO不是State Monad.只有它携带的国家(通常称为"RealWorld")才是完全抽象的.这个事实不再使它成为一个真正的国家Monad,然后是'ST`Monad中`s`线程的抽象本质. (2认同)
  • 不完全的.它的工作原理是线性类型**和**事实上,没有办法创建`io`类型的值(除了最终从`main`接收它).这些点与Haskell中的无能力完全对应,以检查IO操作的monadic上下文,或者实际执行IO操作(除了'main`IO操作的隐式执行之外).但我认为我们大多数都在同一页上; 我只是不同意将Haskell的IO monad解释为操作"世界"的状态monad是无效的. (2认同)

Raf*_*cki 13

你会失望的,但>>=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 #))
Run Code Online (Sandbox Code Playgroud)

这意味着IOmonad被声明为monad的实例,并且它在那里被定义(并且它的实现很容易猜到).State State#>>=

有关monad的更多详细信息,请参阅Haskell wiki上的IO内部文章IO.查看Haskell文档也很有帮助,其中每个位置右侧都有小的"源"链接.

更新:还有另一个失望,这是我的答案,因为我没有注意到'#' State#.然而IO表现得像Statemonad携带抽象RealWorld状态

正如@ehird所写的State#是编译器的内部,>>=IOmonad是在GHC.Base模块中定义的:

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
Run Code Online (Sandbox Code Playgroud)

  • 然而,这不是真的。那是“State#”而不是“State”,正如在别处讨论的那样,IO monad 中发生的真正魔力并没有被它对状态令牌的操作所捕获。 (2认同)