哈斯克尔和国家

Cis*_*one 15 monads user-interface state haskell

Haskell是一种纯函数式编程语言.

我的问题是:使用Haskell解决涉及大量状态的问题有 哪些优点和缺点,例如GUI编程或游戏编程?

还有一个次要问题:有哪些方法以功能方式处理状态?

提前致谢.

Joh*_*n L 18

我先回答你的第二个问题.实际上有许多方法可以处理Haskell(和其他FP语言)中的可变状态.首先,Haskell确实支持IO中的可变状态,通过IORefmvar构造.使用这些对于命令式语言的程序员来说非常熟悉.也有专门的版本,如STRefTMVar,以及可变数组,指针,以及其他各种可变数据.最大的缺点是这些通常只能在IO或更专业的monad中使用.

在函数式语言中模拟状态的最常用方法是将state作为函数参数和返回值显式传递.例如:

randomGen :: Seed -> (Int, Seed)
Run Code Online (Sandbox Code Playgroud)

这里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
Run Code Online (Sandbox Code Playgroud)

现在,任何需要PRNG的函数都可以在Random monad中运行,以便根据需要请求它们.您只需要提供初始状态和计算.

runRandomComputation :: Random a -> Seed -> a
runRandomComputation = evalState
Run Code Online (Sandbox Code Playgroud)

(注意有些函数会大大缩短randomGen2的定义;我选择了最明确的版本).

如果您的随机计算也需要访问IO,那么您使用State的monad转换器版本,StateT.

特别值得注意的是STmonad,它实质上提供了一种机制,可以将IO特定的突变封装在IO的其余部分之外.ST monad提供STRefs,它是对数据的可变引用,也是可变数组.使用ST,可以定义这样的事情:

randomList :: Seed -> [Int]
Run Code Online (Sandbox Code Playgroud)

其中[Int]是一个无限的随机数列表(它最终会根据你的PSRG循环)来自你给它的起始种子.

最后,还有功能反应式编程.目前最着名的图书馆可能是YampaReactive,但其他人也值得关注.在FRP的各种实现中有几种可变状态的方法; 从我对它们的轻微使用来看,它们在概念上似乎与QT或Gtk +中的信令框架类似(例如,为事件添加侦听器).

现在,对于第一个问题.对我来说,最大的优点是可变状态与类型级别的其他代码分开.这意味着代码不能意外地修改状态,除非在类型签名中明确提到它.它还可以很好地控制只读状态与可变状态(Reader monad vs. State monad).我发现以这种方式构造我的代码非常有用,如果函数可能意外地突变状态,那么能够从类型签名中告诉它是有用的.

我个人对在Haskell中使用可变状态没有任何保留意见.最大的困难在于将状态添加到以前不需要它的东西上可能会很乏味,但在我用于类似任务的其他语言(C#,Python)中,同样的事情会很乏味.


Pau*_*aul 11

虽然我不怀疑人们会回答"使用状态monad",但我想指出另一个有用的方法:功能反应式编程(与Yampa或其他方式).