Haskell 是一种纯函数式编程语言。
我的问题是:
使用 Haskell 解决涉及大量状态的问题(例如 GUI 编程或游戏编程)的优缺点是什么?
还有一个次要问题:有哪些方法可以以功能方式处理状态?
提前致谢。
最佳答案
我先回答你的第二个问题。在 Haskell(和其他 FP 语言)中,实际上有很多方法可以处理可变状态。首先,Haskell 确实支持 IO 中的可变状态,通过 IORef
和 mvar
结构体。对于命令式语言的程序员来说,使用这些会非常熟悉。还有STRef
等专门的版本和 TMVar
,以及可变数组、指针和各种其他可变数据。最大的缺点是这些通常只在 IO 或更专业的 monad 中可用。
在函数式语言中模拟状态的最常见方法是将状态作为函数参数和返回值显式传递。例如:
randomGen :: Seed -> (Int, Seed)
这里
randomGen
接受一个种子参数并返回一个新的种子。每次调用它时,都需要跟踪下一次迭代的种子。这种技术始终可用于状态传递,但很快就会变得乏味。可能最常见的 Haskell 方法是使用 monad 来封装这种状态传递。我们可以更换
randomGen
有了这个:-- a Random monad is simply a Seed value as state
type Random a = State Seed a
randomGen2 :: Random Int
randomGen2 = do
seed <- get
let (x,seed') = randomGen seed
put seed'
return x
现在任何需要 PRNG 的函数都可以在 Random monad 中运行以根据需要请求它们。您只需要提供初始状态和计算。
runRandomComputation :: Random a -> Seed -> a
runRandomComputation = evalState
(请注意,有些函数大大缩短了 randomGen2 的定义;我选择了最明确的版本)。
如果您的随机计算也需要访问
IO
,然后您使用 State 的 monad 转换器版本,StateT
.特别值得注意的是
ST
monad,它本质上提供了一种机制来封装 IO 特定的突变,使其远离 IO 的其余部分。 ST monad 提供 STRefs,它是对数据的可变引用,也是可变数组。使用 ST,可以定义如下内容:randomList :: Seed -> [Int]
其中 [Int] 是从您提供的起始种子开始的无限随机数列表(它最终会根据您的 PSRG 循环)。
最后,还有 Functional Reactive Programming .可能目前最突出的图书馆是 Yampa和 Reactive ,但其他的也值得一看。在 FRP 的各种实现中,有几种处理可变状态的方法;从我对它们的轻微使用来看,它们在概念上似乎与 QT 或 Gtk+ 中的信号框架相似(例如,为事件添加监听器)。
现在,对于第一个问题。对我来说,最大的优点是可变状态在类型级别与其他代码分离。这意味着除非在类型签名中明确提及,否则代码不会意外修改状态。它还可以很好地控制只读状态与可变状态(Reader monad 与 State monad)。我发现以这种方式构建我的代码非常有用,并且能够仅从类型签名中判断一个函数是否可能会意外改变状态非常有用。
我个人对在 Haskell 中使用可变状态并没有任何保留。最大的困难是将状态添加到以前不需要的东西上会很乏味,但是在我用于类似任务的其他语言(C#、Python)中,同样的事情会很乏味。
关于user-interface - Haskell 和状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3944170/