7

我在haskell中编写了一个函数,它需要一些参数,如Word32,String(忽略压缩)和输出IO Word32。现在,这是一个函数真正意义上的:对于相同的输入,输出始终是相同的。没有副作用。函数返回IO Word32而不是Word32的原因是函数为了计算最终的Word32输出而在一个循环中多次更新多个32位线性反馈移位寄存器(lfsr)和其他寄存器。如何通过隐藏'状态'变化来在没有IO的情况下编写haskell函数

我的问题是:由于此功能实际上有没有副作用,是有可能隐藏功能的实现,使得该函数返回Word32,而不是IO Word32里面那些寄存器更新?如果是这样,怎么样?

+0

是否由FFI导入? – fuz 2011-05-20 15:35:12

+1

我是否正确理解该函数的主体是(部分)写入命令式风格的,并且您使用本地定义的'IORef'来保存LSFR?也许你应该考虑使用'STRef'而不是'ST' monad中运行计算。 – rkhayrov 2011-05-20 15:38:53

+1

你可以发布你的函数的代码吗?也许它应该在IO中摆在首位。 – augustss 2011-05-20 15:50:26

回答

13

是的! Haskell可以做到这一点。

的ST单子

如果你实际使用的可变状态(寄存器),这完全是从功能外观察者隐藏,那么你在the ST monad,只有记忆效应的单子。你通过runST进入ST世界,当你退出该功能时,所有效果都保证不可见。

这恰恰是处理局部可变状态的正确计算环境。

纯功能性的状态:国家单子

但是,如果你没有真正变异寄存器或细胞,而是更新单纯的功能性价值很多时候,一个简单的环境中可用:the State monad。这不允许可变状态,但给出了本地状态的错觉。

IO和unsafePerformIO

最后,如果你有本地的,可变的效果,就像在ST单子,但出于某种原因或其他,你将需要IO操作上的状态(如通过FFI呼叫),您可以通过使用unsafePerformIO而不是runST来引入本地IO环境,以几乎同样的安全性模拟ST monad。由于IO monad没有很好的类型来强制抽象,所以您需要手动确保副作用不会被观察到。

+0

ST monad的另一个好处是它非常高效。我经常发现ST代码比等效的IO代码快。 – 2011-05-20 22:54:25

+0

@John L,实际上,IO是根据ST定义的,带有一些魔术额外的钩子。 – 2011-05-20 22:57:27

+0

唐,可变状态是完全隐藏的功能外的观察员。我阅读了1994年由John Launchbury和Simon Peyton发表的一篇名为Lazy Functional State Threads的论文,它说*有些算法对内部使用可更新状态至关重要,尽管它们的外部规范是纯功能的。我们提出了一种安全地封装有状态计算的方法,在非严格的纯功能语言*的背景下处理多个命名的可变对象。 纸,谈到ST是有点太技术,我第一次读它。我会再读一遍,用ST monad实现f8,f9。 – 2011-05-21 05:28:53

5

如果您使用FFI导入了该函数,只需从返回类型中删除IO即可。否则,请使用System.IO.UnsafeunsafePerformIO :: IO a -> a。请注意,这个函数是Haskell中最危险的函数之一。不要使用它,如果你不确定后果的话。但为了您的目的,这似乎没问题。

+6

应该注意的是,'unsafePerformIO'的少数“正确”用法之一是作为编译器的一个平面断言,一个函数实际上是外部纯的,这正是这个问题的关键。 “unsafePerformIO”的大部分方式都可能会让你产生这种情况,因为它在某些意义上只是“大部分”纯粹的函数上使用。 – 2011-05-20 15:40:38

+0

也许最恼人的'unsafePerformIO'的错误使用是存在静态缓冲区(不是线程安全的使用),所以要非常小心! – 2011-05-20 15:45:07

+2

@TomMD:有时候我认为用“做某事”的“副作用”来谈纯度是适得其反的,就像Haskell程序员倾向于那样。不纯的功能中最隐秘的错误 - 在大多数不纯的语言中 - 实际上 - 往往是涉及到外部世界的只读访问。 – 2011-05-20 17:13:29

3

是的,这将是合法使用unsafePerformIO。 但是,只有确定没有可见的效果才行。

相关问题