Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell and State

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.

like image 620
CiscoIPPhone Avatar asked Oct 15 '10 16:10

CiscoIPPhone


People also ask

What is a state in Haskell?

The Haskell type State describes functions that consume a state and produce both a result and an updated state, which are given back in a tuple. The state function is wrapped by a data type definition which comes along with a runState accessor so that pattern matching becomes unnecessary.

What is a state monad?

The state monad is a built in monad in Haskell that allows for chaining of a state variable (which may be arbitrarily complex) through a series of function calls, to simulate stateful code. It is defined as: newtype State s a = State { runState :: (s -> (a,s)) }

What does >> mean in Haskell?

Essentially, a >> b can be read like "do a then do b , and return the result of b ". It's similar to the more common bind operator >>= .

What are monads Haskell?

What is a Monad? A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.


2 Answers

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

like image 131
John L Avatar answered Oct 24 '22 18:10

John L


While I do not doubt that people will be answering with "use the state monad", I'd like to point out another useful method: Functional reactive programming (with Yampa or otherwise).

  • The Yampa Arcade: http://www.haskell.org/yale/papers/haskell-workshop03/yampa-arcade.pdf
  • FRP GUI Toolkit: http://www.haskell.org/fruit/
  • Other Yampa Resources: http://lambdor.net/?p=44
like image 29
Paul Avatar answered Oct 24 '22 20:10

Paul