哈斯克尔和国家 [英] Haskell and State

查看:145
本文介绍了哈斯克尔和国家的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

第二个问题:有什么方法可以以功能方式处理状态?

解决方案

我要回答你的第二个问题问题首先。实际上有很多方法来处理Haskell(和其他FP语言)中的可变状态。首先,Haskell在IO中通过 IORef mvar 构造支持可变状态。使用这些将让程序员对命令式语言非常熟悉。还有专门的版本,如 STRef TMVar ,以及可变数组,指针和其他各种可变数据。最大的缺点是它们通常只在IO或更专用的monad中可用。

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

  randomGen :: Seed  - > (Int,Seed)

这里 randomGen 需要一个种子参数并返回一个新的种子。每次调用它时,都需要跟踪下一次迭代的种子。这种技术总是可用于状态传递,但它很快变得乏味。

可能最常用的Haskell方法是使用monad来封装这种状态传递。我们可以用这个替换 randomGen

   - 随机monad只需一个Seed值作为状态
类型随机a =状态种子a

randomGen2 :: Random Int
randomGen2 = do
种子< - get
let(x,seed')= randomGen seed
放入种子'
return x



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

  runRandomComputation :: Random a  - >种子 - > a 
runRandomComputation = evalState

(注意有一些函数大大缩短了randomGen2的定义;如果你的随机计算也需要访问 IO ,那么你可以使用monad变换器版本的状态, StateT



特别值得注意的是 ST monad,它基本上提供了一种机制来将IO特定的突变从IO的其余部分中分离出来。 ST monad提供了STRefs,它是对数据的可变引用,也是可变数组。使用ST,可以定义如下所示的内容:

  randomList :: Seed  - > [Int] 

其中[Int]是随机数的无限列表在你的PSRG上)从你给它的起始种子开始。



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

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



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


Haskell is a pure functional programming language.

My question is: What are the advantages and disadvantages of using Haskell to solve problems involving lots of state, for example GUI programming or game programming?

Also a secondary question: what methods are there to handle state in a functional way?

Thanks in advance.

解决方案

I'm going to answer your second question first. There are actually many ways to handle mutable state in Haskell (and other FP languages). First of all, Haskell does support mutable state in IO, through IORef and mvar constructs. Using these will feel very familiar to programmers from imperative languages. There are also specialized versions such as STRef and TMVar, as well as mutable arrays, pointers, and various other mutable data. The biggest drawback is that these are generally only available within IO or a more specialized monad.

The most common way to simulate state in a functional language is explicitly passing state as a function argument and returned value. For example:

randomGen :: Seed -> (Int, Seed)

Here randomGen takes a seed parameter and returns a new seed. Every time you call it, you need to keep track of the seed for the next iteration. This technique is always available for state passing, but it quickly gets tedious.

Probably the most common Haskell approach is to use a monad to encapsulate this state passing. We can replace randomGen with this:

-- 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

Now any functions which need a PRNG can run within the Random monad to request them as needed. You just need to provide an initial state and the computation.

runRandomComputation :: Random a -> Seed -> a
runRandomComputation = evalState

(note there are functions which considerably shorten the definition of randomGen2; I chose the most explicit version).

If your random computation also needs access to IO, then you use the monad transformer version of State, StateT.

Of special note is the ST monad, which essentially provides a mechanism to encapsulate IO-specific mutations away from the rest of IO. The ST monad provides STRefs, which are a mutable reference to data, and also mutable arrays. Using ST, it's possible to define things like this:

randomList :: Seed -> [Int]

where [Int] is an infinite list of random numbers (it'll cycle eventually depending on your PSRG) from the starting seed you give it.

Finally, there's Functional Reactive Programming. Probably the current most prominent libraries for this are Yampa and Reactive, but the others are worth looking at also. There are several approaches to mutable state within the various implementations of FRP; from my slight use of them they often seem similar in concept to a signalling framework as in QT or Gtk+ (e.g. adding listeners for events).

Now, for the first question. For me, the biggest advantage is that mutable state is separated from other code at the type level. This means that code can't accidentally modify state unless it's explicitly mentioned in the type signature. It also gives very good control of read-only state versus mutable state (Reader monad vs. State monad). I find it very useful to structure my code in this way, and it's useful to be able to tell just from the type signature if a function could be mutating state unexpectedly.

I personally don't really have any reservations about using mutable state in Haskell. The biggest difficulty is that it can be tedious to add state to something that didn't need it previously, but the same thing would be tedious in other languages I've used for similar tasks (C#, Python).

这篇关于哈斯克尔和国家的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆