[Haskell-cafe] Looking for a more functional way to do this

Brian Hulley brianh at metamilk.com
Wed Aug 6 16:34:31 EDT 2008


Jefferson Heard wrote:
> Adrian, my understanding is that it's not that simple, because I need
> to preserve the state between calls to GLUT's callbacks, which all
> return IO ().
> 
> 2008/8/6 Adrian Neumann <aneumann at inf.fu-berlin.de>:
>> There is the State Monad...
 >>>
>>>
>>> data ProgramState  = ProgramState {
>>>    some_associative_data :: Map String String
>>>  , position :: GL.Vector3 Float
>>>  , look_at :: GL Vector3 Float
>>>  , selectables :: Map GLuint NamedObject
>>>  }
>>>
>>> render :: IORef ProgramState -> IO ()

You might find it easier to think in terms of a Reader monad where each 
component of your ProgramState above is now a separate IORef.

Then you can just use a function:

	mkCallback :: ReaderT ProgramStateRefs IO () -> IO ()

to create the necessary callbacks for GLUT, and there is no need to 
interleave any state between calls (since it's all kept in the IO monad 
directly).

Eg:
   data ProgramStateRefs = ProgramStateRefs
     { some_associative_data :: IORef (Map String String)
     , ...
     }

   main = do
     r <- createProgramStateRefs
     let
       mkCallback :: ReaderT ProgramStateRefs IO a -> IO a
       mkCallback (ReaderT r_ma) = r_ma r
     GLUT.renderCallback $= mkCallback onRender
     ...

   onRender :: ReaderT ProgramStateRefs IO ()
   onRender = do ...

You can then go further and use phantom types to build specialized 
monads by newtyping the (ReaderT ProgramStateRefs IO) to limit the 
operations possible in each callback (e.g. to prevent calls to rendering 
methods inside a keyboard handler etc) though at some point there is a 
tradeoff between how much you really need to enforce statically and how 
much time you have to devise suitable schemes of phantom type parameters 
to enforce it.

(Disclaimer: the above code is untested and may contain errors ;-) )

Regards,
Brian.


More information about the Haskell-Cafe mailing list