在看到如何定义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,因此混淆.
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的实例,并且它在那里被定义(并且它的实现很容易猜到).StateState#>>=
有关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)