advice on architecting a library (and haskell software in general)

hi all, i'm looking for advice on how to architect a simple widget library for vty/hscurses, but these beginner-level questions should apply to haskell applications in general. input/requests on any aspect of my design would be greatly appreciated - in return perhaps you'll get something you'd actually want to use! i have studied in detail various haskell curses/vty apps, including yi, hmp3, riot, and hscurses.widgets/contactmanager. my immediate goal is to produce a set of composable widgets and a flexible framework that - as much as possible - reduces the amount of code a user has to write. my eventual goal is to make a functional gui library, along the lines of fruit/wxfruit/fg. to this end i've also read their literature, but i still do not understand enough about arrows/yampa/afrp. i currently have a tree of widgets, some of which can receive focus. this allows them to alter the program's key handling, but in a certain order based on the hierarchy. e.g., in my program, at the top-level, hitting either 'q' or 'f12' quits the program. when we focus on a container, pressing 'tab' will cycle focus among the subwidgets. when we focus on a text box, the key bindings layer, so that 'q' inserts a character into the text box, and 'f12' still exits and 'tab' still cycles focus. here's a simplified synopsis of the types (omits details like layout): class Widget w where output :: Bool {- whether we have focus -} -> w -> Output data Output = Output Image (Maybe CursorPos) KeyHandler type Image = [[(Char,Attr)]] -- Attr is just the text decoration (colors) type CursorPos = (Int,Int) type KeyHandler = ??? i'm guessing the most flexible type for KeyHandler could be Key -> IO (), but is this really the only/best approach? (every time i fall back to IO, i feel i'm missing something/a puppy dies/etc.) currently, this is what i have: type KeyHandler = Key -> AppState -> AppState -- maps to a state updater data AppState = AppState { rootWidget :: Widget, actualAppState :: ..., ... } but this has a number of apparent issues, including: - no decoupling of UI and app - that is, the key handlers that the widgets return have complete knowledge of the application state. so for instance, this particular text box knows that its key handler should be: handler key state = let oldPos = cursorPos $ someTextBox $ rootWidget $ state in case key of Left -> state { rootWidget { someTextBox { cursorPos = oldPos - 1 } } } ... - on key press, can't save to a file or otherwise do anything in IO. - this isn't going to scale - as my AppState grows and grows, we're throwing away and reconstructing a lot of state. and the coding style it demands is pretty clumsy, as demonstrated above. but if we switch to IO (): - still doesn't help decouple the library from the app. the above example key handling code snippet would be the same (i.e. still very clumsy), except that we'd be reading/writing the state from/to an IORef or MVar. - certainly, not all actions need IO. in fact, my current application is just a viewer, and thus needs no IO at all. - requires a global IORef or MVar - i don't know how to address the performance problem without resorting to sprinkling IORefs or MVars everywhere in the state structure, thus strangling the app into IO everywhere other open questions: - how should the "top-most" code work? currently, my app's main has a tiny event loop that feeds keys through a Chan to a State-ful function ([Key] -> State AppState [Image]) and then to the final drawing function ([Image] -> IO ()). however, depending on the resolutions to the above issues, this may radically change. - how should i compose the various key handlers? this, again, will depend. related work: yi/hmp3 also have one large piece of state in a global mvar (allows multiple threads to update it/trigger a redraw, instead of only redrawing in response to key events), with no attempt to decouple the UI and app parts of that state. event handling is done by a lexer that matches regex key patterns to IO () - this doesn't couple key handling with the UI components, and is thus not composable. (i thought the idea of using a lexer as the state automata was good - there may be some way to make this more composable, too, if the regex ever fails.) riot's UI code operates mostly in StateT Riot IO (), where Riot is again a monolithic application state. again, no attempt at decoupling or composability is made. hscurses.widgets operates mostly in IO (), and 'activating' (focusing) on widgets hands over the event loop completely. i hope i explained my design problems clearly. i've used haskell a bunch for various small text-processing scripts, but decided to try to use it for a "real" application that has little to do with parsing or other purported strengths of the language. i believe other new haskellers may relate to some of these issues. thanks! yang

Yang wrote:
hi all, i'm looking for advice on how to architect a simple widget library for vty/hscurses, but these beginner-level questions should apply to haskell applications in general. input/requests on any aspect of my design would be greatly appreciated - in return perhaps you'll get something you'd actually want to use!
i have studied in detail various haskell curses/vty apps, including yi, hmp3, riot, and hscurses.widgets/contactmanager. my immediate goal is to produce a set of composable widgets and a flexible framework that - as much as possible - reduces the amount of code a user has to write. my eventual goal is to make a functional gui library, along the lines of fruit/wxfruit/fg. to this end i've also read their literature, but i still do not understand enough about arrows/yampa/afrp.
Currently, the design of a functional UI library (be it graphical or for tty) is still an open research problem. Somehow, the arrow based approaches like Fruit etc. are not yet satisfying. A predecessor to this approach is FranTk. The early library Fudgets is in essence based on arrows but precedes their invention. The most recent development in this direction is Phooey. In the mean time, the medium level libraries wxHaskell and gtk2hs have gathered acceptance, mostly because they implement a full widget set while still being reasonably succinct. A predecessor is HToolkit drawing from ObjectIO. Despite being close to their imperative cousins, they already supersede them in expressiveness. They make an excellent base for further experiments, be it for arrows (wxFruit) or other recent approaches like PropLang. You have two options: * Search for the grail. Most likely, this doesn't involve much coding but much searching and has the risk of not finding it. But as the tty doesn't have many different widgets, you can concentrate on the high level ideas, i.e. how to get rid of IO and IORefs. Pumping your brain with ideas - as you already do - surely helps. * Implement an existing design. This way, you'll actually program something. I'd propose to implement something medium level along the lines of wxHaskell that can later be utilized in a high level approach. Maybe you can even create a cross platform interface, i.e. one that works for a tty and for graphical UIs at the same time. The author of HToolkit wrote a proposal on how to transparently enclose Windows, Mac and Gnome.
try to use it for a "real" application that has little to do with parsing or other purported strengths of the language.
Well, Haskell's "only" strength is that it gives you enough power to express your ideas, i.e. to compose great things from small ones. The strength of your ideas is your business :) In this sense, monadic parser combinators are not an inherent strength of the language, they happen to be powerful by themselves. Regards, apfelmus
participants (2)
-
apfelmus@quantentunnel.de
-
Yang