lui*_*dro 22 io monads haskell
从概念上讲,执行输出的计算似乎与仅执行输入的计算非常不同.从某种意义上说,后者更为纯粹.
举个例子,我希望有一种方法可以将我的程序中仅输入的部分与可能实际写出的部分分开.
那么,为什么只有Monad没有输入?
任何原因导致I monad(以及可以合并到IO Monad中的O Monad)的任何原因?
编辑:我主要是指输入为阅读文件,而不是与用户交互.这也是我的用例,我可以假设输入文件在程序执行期间不会改变(否则,可以获得未定义的行为).
scl*_*clv 21
我不同意bdonlan的回答.确实,输入和输出都不是更"纯粹",但它们是完全不同的.将IO视为单个"罪孽箱"并将所有效果挤在一起是非常有效的,它确实可以确保某些属性更难.例如,如果您知道许多函数只从某些内存位置读取,并且永远不会导致这些位置被更改,那么很高兴知道您可以重新排序它们的执行.或者如果你有一个使用forkIO和MVars的程序,那么根据它的类型知道它也不是在读取/ etc/passwd会很高兴.
此外,除了堆叠的变压器之外,还可以以一种方式构成单子效应.你不能用所有的 monad(只是免费的monad)来做这件事,但对于这样的情况,你真正需要的只是.例如,iospec软件包提供了纯粹的IO规范 - 它没有单独的读写,但它确实将它们分开,例如STM,MVars,forkIO,soforth.
http://hackage.haskell.org/package/IOSpec
关于如何干净地组合不同monad的关键想法在" 数据类型单点纸"(很好的阅读,非常有影响力,不能推荐,等等)中有所描述.
bdo*_*lan 12
IO monad的"输入"侧输出与输入一样多.如果您消耗了一行输入,那么您消耗该输入的事实将传达给外部,并且还可以记录为不纯的状态(即,您之后不再使用相同的行); 它和输出操作一样多putStrLn
.此外,必须根据输出操作对输入操作进行排序; 这又限制了你可以将两者分开的程度.
如果你想要一个纯粹的只读monad,你应该使用reader monad.
也就是说,你似乎对组合monad可以做什么有点困惑.虽然你确实可以组合两个monad(假设一个是monad变换器)并获得某种混合语义,但你必须能够运行结果.也就是说,即使你可以定义一个IT (OT Identity) r
,你如何运行它?IO
在这种情况下你没有root monad,所以main必须是纯函数.这意味着你有main = runIdentity . runOT . runIT $ ...
.这是无稽之谈,因为你从纯粹的语境中获得了不纯净的效果.
换句话说,IO monad的类型必须是固定的.它不能是用户可选择的转换类型,因为它的类型被钉入main
.当然,你可以调用它I (O Identity)
,但你没有获得任何; O (I Identity)
将是一个无用的类型,I []
或者O Maybe
,因为你永远无法运行任何这些.
当然,如果IO
保留为基本IO
monad类型,则可以定义以下例程:
runI :: I Identity r -> IO r
Run Code Online (Sandbox Code Playgroud)
这很有效,但同样,你不能轻易地拥有这个我monad下面的任何东西,而且你并没有从这种复杂性中获得太多.无论如何,将一个输出monad转换为List基本monad 甚至意味着什么呢?