Mitigating state-threading through an application loop

Hello Haskellers, I'm implementing a simple tree-manipulating (for sports tournaments) application prototype, with SDL for graphics and simple user interaction. For reference, I've posted the code on hpaste. http://hpaste.org/55506 My question is about code organization: everything was simple and elegant until I started writing the program's display/event loop. Every function in this section has to be passed the same parameters - the application window to draw on, the font to display text with, the tree representing the current application state, etc. The font is an especially egregious example of the problem, because it's only used by one function but to get there it must be threaded through all of them (looking at the hpaste, you will see I don't want to call openFont on every invocation of drawTexts; what's needed is to call it once in main and have the resulting value available to drawTxt. So my question: how can I mitigate the ugliness of this state-threading? I understand this is one purpose for monads; am I supposed to implement a monad transformer for this? I think it would be great if I could define a type AppState as a tuple of the various things I need to thread, and specify some kind of automatic as-pattern, so that every function taking this AppState parameter would implicitly have its components bound to certain preset names. I've never seen anyone do this however. What is the right solution?

2011/12/20 Michael Serra
Hello Haskellers, I'm implementing a simple tree-manipulating (for sports tournaments) application prototype, with SDL for graphics and simple user interaction. For reference, I've posted the code on hpaste. My question is about code organization: everything was simple and elegant until I started writing the program's display/event loop. Every function in this section has to be passed the same parameters - the application window to draw on, the font to display text with, the tree representing the current application state, etc. The font is an especially egregious example of the problem, because it's only used by one function but to get there it must be threaded through all of them (looking at the hpaste, you will see I don't want to call openFont on every invocation of drawTexts; what's needed is to call it once in main and have the resulting value available to drawTxt. So my question: how can I mitigate the ugliness of this state-threading? I understand this is one purpose for monads; am I supposed to implement a monad transformer for this?
I think it would be great if I could define a type AppState as a tuple of the various things I need to thread, and specify some kind of automatic as-pattern, so that every function taking this AppState parameter would implicitly have its components bound to certain preset names. I've never seen anyone do this however. What is the right solution?
You can use the -XRecordWildcards extension[0] with a Reader or a State monad. data AppState = AppState { appStateSomething :: String } f AppState{..} = print appStateSomething Cheers, Thu [0] http://haskell.org/ghc/docs/latest/html/users_guide/syntax-extns.html#record...

On Tue, Dec 20, 2011 at 11:08 AM, Michael Serra
Hello Haskellers, I'm implementing a simple tree-manipulating (for sports tournaments) application prototype, with SDL for graphics and simple user interaction. For reference, I've posted the code on hpaste. My question is about code organization: everything was simple and elegant until I started writing the program's display/event loop. Every function in this section has to be passed the same parameters - the application window to draw on, the font to display text with, the tree representing the current application state, etc. The font is an especially egregious example of the problem, because it's only used by one function but to get there it must be threaded through all of them (looking at the hpaste, you will see I don't want to call openFont on every invocation of drawTexts; what's needed is to call it once in main and have the resulting value available to drawTxt. So my question: how can I mitigate the ugliness of this state-threading? I understand this is one purpose for monads; am I supposed to implement a monad transformer for this?
I think it would be great if I could define a type AppState as a tuple of the various things I need to thread, and specify some kind of automatic as-pattern, so that every function taking this AppState parameter would implicitly have its components bound to certain preset names. I've never seen anyone do this however. What is the right solution?
I've done that before in web-apps. It is conventional enough. To ease the threading-through of paramters you can use a Reader monad (perhaps ReaderT AppState IO). I don't know how well this fits in with the SDL libraries, though. Antoine
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On 12/20/11 11:08 AM, Michael Serra wrote:
Hello Haskellers, I'm implementing a simple tree-manipulating (for sports tournaments) application prototype, with SDL for graphics and simple user interaction. For reference, I've posted the code on hpaste.http://hpaste.org/55506 My question is about code organization: everything was simple and elegant until I started writing the program's display/event loop. Every function in this section has to be passed the same parameters - the application window to draw on, the font to display text with, the tree representing the current application state, etc. The font is an especially egregious example of the problem, because it's only used by one function but to get there it must be threaded through all of them (looking at the hpaste, you will see I don't want to call openFont on every invocation of drawTexts; what's needed is to call it once in main and have the resulting value available to drawTxt. So my question: how can I mitigate the ugliness of this state-threading? I understand this is one purpose for monads; am I supposed to implement a monad transformer for this?
I haven't looked at your specific example (for which I think the previous emails provide the answer you're seeking), but here's a general technique which I often use to help clarify things... Oftentimes I find that the kinds of state needed for various functions are disjoint (or nearly so), and so packaging it all up in a State monad just doesn't feel right. But remember that functions are data too! In your main/driver function, grab all the kinds of state and pass them in to the various functions which need them. Now, instead of passing the arguments around in your State, just pass a record containing all the partially applied functions. This works best when you know the basic API you want (it's specified by the record's type), but there are a bunch of different ways to get there. It's especially helpful when you have both pure and side-effecting ways to get there, or when the side-effects are concentrated in getting the parameters rather than in using them (e.g., dealing with commandline arguments). -- Live well, ~wren
participants (4)
-
Antoine Latter
-
Michael Serra
-
Vo Minh Thu
-
wren ng thornton