如何通过隐藏"状态"更改来在类型sig中编写没有IO的haskell函数

Bab*_*san 7 monads haskell functional-programming referential-transparency unsafe-perform-io

我在haskell中编写了一个函数,它接受一些参数,如Word32,String(忽略currying)并输出IO Word32.现在,这是一个真正意义上的函数:对于相同的输入,输出将始终相同.没有副作用.函数返回IO Word32而不是Word32的原因是该函数在循环中多次更新许多32位线性反馈移位寄存器(lfsr)和其他寄存器,以便计算最终的Word32输出.

我的问题是:鉴于此函数实际上没有副作用,是否可以在函数实现中隐藏这些寄存器更新,以便该函数返回Word32而不是IO Word32?如果是这样,怎么样?

Don*_*art 13

是! Haskell可以做到这一点.

ST monad

如果你实际上使用了可变状态(寄存器),那些完全隐藏在函数外部的观察者,那么你就是ST monad,一个仅用于记忆效应的monad.您通过runST退出ST世界,当退出该功能时,所有效果都保证不可见.

正是处理本地可变状态的正确计算环境.

纯粹的功能状态:国家monad

但是,如果您实际上没有改变寄存器或单元格,而是多次更新纯函数值,则可以使用更简单的环境:State monad.这不允许可变状态,但给出了局部状态的错觉.

IO和unsafePerformIO

最后,如果你有本地的,可变的效果,比如在STmonad中,但由于某种原因,你将需要在该状态下进行IO操作(例如通过FFI调用),你可以模拟STmonad,几乎与通过使用unsafePerformIO而不是runST引入本地IO环境来提高安全性.由于IO monad没有很好的类型来强制执行抽象,因此您需要手动确保自己无法观察到副作用.


fuz*_*fuz 5

如果使用FFI导入该功能,只需IO从返回类型中删除.否则,使用unsafePerformIO :: IO a -> aSystem.IO.Unsafe.请注意,此函数是Haskell中最危险的函数之一.不要使用它,如果你不是真的不知道后果.但为了你的目的,似乎没关系.

  • 应该注意的是,`unsafePerformIO`的少数"正确"用法之一是对编译器的一个平坦的断言,即函数实际上是外部纯的,这正是这个问题的关键所在."unsafePerformIO"可以咬你的大部分方法都来自于在某种意义上只是"大部分"纯粹的函数. (6认同)
  • @TomMD:有时候我认为谈论纯度的"副作用"与Haskell程序员倾向于"做某事"有关,这会适得其反.不纯函数中最阴险的错误 - 实际上 - 在大多数不纯的语言中 - 往往是那些涉及对外部世界的只读访问的错误. (2认同)