ANNOUNCE: Phooey -- a Functional UI library for Haskell
Announcing Phooey <http://conal.net/phooey>, a functional UI library for Haskell. GUIs are usually programmed in an "unnatural" style, in that implementation dependencies are inverted, relative to logical dependencies. This reversal results directly from the imperative orientation of most GUI libraries. While outputs depend on inputs from a user and semantic point of view, the imperative approach imposes an implementation dependence of inputs on outputs. Phooey ("*Ph*unctional *oo*s*e*r *y*nterfaces") retains the functional style, in which outputs are expressed in terms of inputs. In addition, Phooey supports dynamic input bounds, flexible layout, and mutually-referential widgets. As an example of Phooey's style, below is a simple shopping list GUI. The * total* displayed at the bottom of the window always shows the sum of the values of the *apples* and *bananas* input sliders. When a user changes the inputs, the output updates accordingly. Phooey is structured as an arrow, and this example uses arrow notation. The code: ui1 :: UI () () ui1 = title "Shopping List" $ proc () -> do a <- title "apples" (islider 3) -< (0,10) b <- title "bananas" (islider 7) -< (0,10) title "total" showDisplay -< a+b I am working on a paper about Phooey. For now, please see the Haddock documentation <http://darcs.haskell.org/packages/phooey/doc> (which includes more examples), and try the code via darcs get http://darcs.haskell.org/packages/phooey --partial Directions for building are in the README<http://darcs.haskell.org/packages/phooey/README>file. Distribution tarballs are here <http://darcs.haskell.org/packages/phooey/dist>. Comments and collaboration are very welcome! Cheers, - Conal P.S. I'm very grateful for Don Stewart's recent pointers on how to create & release Haskell projects.
From: Conal Elliott To: haskell@haskell.org Sent: Tuesday, December 12, 2006 7:17 AM
GUIs are usually programmed in an "unnatural" style, in that implementation dependencies are inverted, relative to logical dependencies. This reversal results directly from the imperative orientation of most GUI libraries. While outputs depend on inputs from a user and semantic point of view, the imperative approach imposes an implementation dependence of inputs on outputs.
Hi, This looks really interesting, but I'm struggling to understand what you mean by "reversal". Can you elaborate on what you mean by "the imperative approach imposes an implementation dependence of inputs on outputs." In the model-view-controller pattern for example, I see no such reversal. Also, suppose you have a gui consisting of an edit widget such that when the user types something it gets lexed, parsed, and fontified as a Haskell program, ie from the user's point of view the widget maintains a syntax highlighted view of the code which is "continuously" updated. Would your system do the lexing/parsing for every key that was pressed or only once, when the widget needs to be re-rendered (a typical gui for Windows would only propagate changes and re-draw the gui (ie lex/parse/fontify) when there are no event messages pending)? In other words, does your system solve the problem of automatic caching of updates that would be solved in the imperative setting by the (admittedly rather messy) waiting for "nothing else happening"? Regards, Brian. -- http://www.metamilk.com _______________________________________________ Haskell mailing list Haskell@haskell.org http://www.haskell.org/mailman/listinfo/haskell
On 12/12/06, Brian Hulley <brianh@metamilk.com> wrote:
This looks really interesting, but I'm struggling to understand what you mean by "reversal". Can you elaborate on what you mean by "the imperative approach imposes an implementation dependence of inputs on outputs." In the model-view-controller pattern for example, I see no such reversal.
I'll try to explain with an example. If it's not clear, or you think the reasoning doesn't apply to MVC, please let me know. The code makes a top-level frame and a panel for holding widgets, creates slider input and text output. It then installs event handlers for the input widget, to change the state of output widget (to show the square of the input value). After initializing the output, the code set the panel's and the frame's layouts and shows the frame. import Graphics.UI.WX z1 :: IO () z1 = start $ do -- Create frame, panel, slider input, and text output f <- frame [] pan <- panel f [] s <- hslider pan True 0 10 [ selection := 3 ] t <- textEntry pan [] -- output-updater to be called initially and when input changes let upd = do n <- get s selection set t [ text := show (n*n) ] upd set s [ on command := upd ] -- Lay out panel and frame set pan [ layout := fill $ column 0 [ hwidget s, hwidget t ] ] set f [ layout := hwidget pan ] where hwidget :: Widget w => w -> Layout hwidget = hfill . widget Notice that although, logically, the output depends on the input (being a rendering of its square), the code places into the input widget a dependency on the output widget, because the output widget contains the mutable state altered by the input widget's event handler (upd). Moreover, the output widget contains no reference to the input widget. Thus, the implementation dependencies are opposite of the logical dependencies. This inversion of dependencies would seem to be a direct result of the imperative approach, which states the actions that must be taken on the output state as a consequence of changes to the input state. I'll reply separately to your question about efficient evaluation/updating. Cheers, - Conal
On Tue, 12 Dec 2006 17:54:26 -0800, you wrote:
Notice that although, logically, the output depends on the input (being a rendering of its square), the code places into the input widget a dependency on the output widget, because the output widget contains the mutable state altered by the input widget's event handler (upd). Moreover, the output widget contains no reference to the input widget. Thus, the implementation dependencies are opposite of the logical dependencies. This inversion of dependencies would seem to be a direct result of the imperative approach, which states the actions that must be taken on the output state as a consequence of changes to the input state.
I don't think it's the result of an imperative vs. functional approach. It's basically a matter of push vs. pull, and while the functional (especially lazy functional) approach is most naturally a pull technique, you can do both push and pull using either imperative or functional approaches. In fact, before the advent of event-driven programming (the ultimate push technique), most user interaction in imperative programming was based on pull techniques. (And it's somewhat ironic that event-driven programming is typically implemented via a polling loop or other such pull mechanism.) The bottom line is that you have to respond to sequenced, asynchronous events, which are awkward to model using purely pull techniques. You can alleviate some of the "invertedness" of the dependencies by using a publish/subscribe model, where the subscriber (the output widget in this example), subscribes to notifications by registering itself with the publisher (the input widget). It still ends up working more or less the same way under the hood, but at least the only _explicit_ linkage visible in the source code goes from output widget -> input widget rather than the other way around. I haven't studied Fran or the other functional user interaction implementations in any detail, so it's possible that I'm repeating something here that they already do, but I think the most useful thing that could come out of any work in this area would be a notation that allows the programmer to set up the notification links in some sort of declarative way (i.e., "This is how all of the widgets are supposed to be hooked up together"). There are still some sticky bits, because there's always the possibility that the order in which the connections are wired up will have an effect on whether the final contraption works as expected, but it should be possible to avoid problems in that regard with the addition of some dependency notations. Steve Schafer Fenestra Technologies Corp. http://www.fenestra.com/
Push vs pull, rather than imperative vs functional, as the crux of the inversion issue makes a lot of sense to me. And I see your point about publish/subscribe removing the dependency inversion, while keeping a push implementation and an imperative style. I also see how pull can be done easily in an imperative setting. As for doing push in a functional approach, do you mean under the hood, or somehow a visible push model? Phooey does push under the hood, but hides it from the user. I don't know what a functional, visible push model would look like. If I understand your final paragraph below, you're describing what I'm up to with Phooey: specify connections (dependencies) in a simple, declarative way, and have the notifications all set up correctly. Thanks for your comments, Steve. They're getting me closer to a clear explanation, which will be helpful in the paper I'm writing. Regards, - Conal On 12/13/06, Steve Schafer <steve@fenestra.com> wrote:
On Tue, 12 Dec 2006 17:54:26 -0800, you wrote:
Notice that although, logically, the output depends on the input (being a rendering of its square), the code places into the input widget a dependency on the output widget, because the output widget contains the mutable state altered by the input widget's event handler (upd). Moreover, the output widget contains no reference to the input widget. Thus, the implementation dependencies are opposite of the logical dependencies. This inversion of dependencies would seem to be a direct result of the imperative approach, which states the actions that must be taken on the output state as a consequence of changes to the input state.
I don't think it's the result of an imperative vs. functional approach. It's basically a matter of push vs. pull, and while the functional (especially lazy functional) approach is most naturally a pull technique, you can do both push and pull using either imperative or functional approaches. In fact, before the advent of event-driven programming (the ultimate push technique), most user interaction in imperative programming was based on pull techniques. (And it's somewhat ironic that event-driven programming is typically implemented via a polling loop or other such pull mechanism.)
The bottom line is that you have to respond to sequenced, asynchronous events, which are awkward to model using purely pull techniques. You can alleviate some of the "invertedness" of the dependencies by using a publish/subscribe model, where the subscriber (the output widget in this example), subscribes to notifications by registering itself with the publisher (the input widget). It still ends up working more or less the same way under the hood, but at least the only _explicit_ linkage visible in the source code goes from output widget -> input widget rather than the other way around.
I haven't studied Fran or the other functional user interaction implementations in any detail, so it's possible that I'm repeating something here that they already do, but I think the most useful thing that could come out of any work in this area would be a notation that allows the programmer to set up the notification links in some sort of declarative way (i.e., "This is how all of the widgets are supposed to be hooked up together"). There are still some sticky bits, because there's always the possibility that the order in which the connections are wired up will have an effect on whether the final contraption works as expected, but it should be possible to avoid problems in that regard with the addition of some dependency notations.
Steve Schafer Fenestra Technologies Corp. http://www.fenestra.com/ _______________________________________________ Haskell mailing list Haskell@haskell.org http://www.haskell.org/mailman/listinfo/haskell
Hi,
Thanks for your comments, Steve. They're getting me closer to a clear explanation, which will be helpful in the paper I'm writing.
I was going to polish up PropLang before releasing it, but if you're writing a paper, and I go and release PropLang just after you've put the finishing touches on it, you'll hunt me down and kill me :) As such, here is an announcement that PropLang exists, and is cool. See the darcs repo: http://www.cs.york.ac.uk/fp/darcs/proplang/ - and hopefully see some documentation in the future :) No arrows, but seems to have the same underlying idea. The darcs repo has a text editor (it had another sample, I'm sure, but I can't see it!) Some selected hilights: -- CREATE VARIABLES -- is a document open document <- newVar False -- is a document modified modified <- newVar False -- the filename of the document, if its been saved filename <- newVar Nothing -- SET UP RELATIONS BETWEEN PROPERTIES txt!enabled =<= document new!enabled =< with1 document not saveas!enabled =< with2 document modified (&&) --save!enabled =< with2 document modified (&&) save!enabled =< (&&) =$$= document =$= modified close!enabled =<= document revert!enabled =<= modified -- SUPPORTS EVENTS TOO new!onClicked += newDocument gui save!onClicked += saveDocument gui saveas!onClicked += saveAsDocument gui close!onClicked += closeDocument gui open!onClicked += openDocument gui -- COOL AND SIMPLE UNDO revert!onClicked += (txt!text -<- lasttxt) modified =< with2 (txt!text) (lasttxt) (/=) let titleformat d m f = "TextEditor" ++ (if d then " - " ++ maybe "<untitled>" id f else "") ++ (if m then " *" else "") -- REALLY COOL SETTING OF THE TITLE AUTOMATICALLY window!text =< titleformat =$$= document =$= modified =$= filename -- CREATE A STATUS BAR WITH THE WORD COUNT IN ONE LINE! sb!text =< (\x -> "Word count: " ++ show (length $ words x)) =$$= txt!text I'll write up a tutorial or something at some point. It's written using Gtk2Hs, and works fine on both Linux and Windows. GuiHaskell is written using this: http://www-users.cs.york.ac.uk/~ndm/projects/guihaskell.php Thanks Neil
On Wed, 13 Dec 2006 09:53:20 -0800, you wrote:
As for doing push in a functional approach, do you mean under the hood, or somehow a visible push model? Phooey does push under the hood, but hides it from the user. I don't know what a functional, visible push model would look like.
I meant visible. In the absence of hardware interrupts or the like, you still have to have some kind of "prime mover" to get the ball rolling; that could be a simple polling loop or whatever. In effect, it would invoke a function on each input widget in turn, and the consequences of those function invocations would ripple through the system (and eventually to the output widgets). Of course, it would likely be "functional" in name only, as I suspect that there would rarely be occasion to use functions whose return type is anything other than IO (). Steve Schafer Fenestra Technologies Corp. http://www.fenestra.com/
Hi Conal, Looks like an interesting system. A few questions: - What is the underlying Arrow here, and how does it differ from Yampa? (And the obvious follow-on: If they are similar, why not just use Yampa?) - How will your layout model deal with layouts that aren't rectilinear? - Do you support events or switching (in the FRP sense)? - How do you plan to deal with dynamic collections (opening/closing new windows at runtime, or adding/removing widgets from a running GUI)? IMO, this is the biggest challenge to presenting a purely functional API but using a standard GUI toolkit for the underlying widget set. Regards, -Antony On 12/12/06, Conal Elliott <conal@conal.net> wrote:
Announcing Phooey <http://conal.net/phooey>, a functional UI library for Haskell.
GUIs are usually programmed in an "unnatural" style, in that implementation dependencies are inverted, relative to logical dependencies. This reversal results directly from the imperative orientation of most GUI libraries. While outputs depend on inputs from a user and semantic point of view, the imperative approach imposes an implementation dependence of inputs on outputs.
Phooey ("*Ph*unctional *oo*s*e*r *y*nterfaces") retains the functional style, in which outputs are expressed in terms of inputs. In addition, Phooey supports dynamic input bounds, flexible layout, and mutually-referential widgets.
As an example of Phooey's style, below is a simple shopping list GUI. The *total* displayed at the bottom of the window always shows the sum of the values of the *apples* and *bananas* input sliders. When a user changes the inputs, the output updates accordingly.
Phooey is structured as an arrow, and this example uses arrow notation. The code:
ui1 :: UI () () ui1 = title "Shopping List" $ proc () -> do a <- title "apples" (islider 3) -< (0,10) b <- title "bananas" (islider 7) -< (0,10) title "total" showDisplay -< a+b
I am working on a paper about Phooey. For now, please see the Haddock documentation <http://darcs.haskell.org/packages/phooey/doc> (which includes more examples), and try the code via
darcs get http://darcs.haskell.org/packages/phooey --partial
Directions for building are in the README<http://darcs.haskell.org/packages/phooey/README>file. Distribution tarballs are here <http://darcs.haskell.org/packages/phooey/dist>.
Comments and collaboration are very welcome!
Cheers,
- Conal
P.S. I'm very grateful for Don Stewart's recent pointers on how to create & release Haskell projects.
_______________________________________________ Haskell mailing list Haskell@haskell.org http://www.haskell.org/mailman/listinfo/haskell
Hi Antony, Phooey is much simpler and less ambitious than Yampa & Fruit. Semantically, it's the same arrow, namely functions from input flows to output flows. Internally, it does simple data-driven evaluation. No "events" & "switching" in the FRP sense. I didn't try to address dynamic collections. Pretty simple stuff. As for non-rectilinear layout, I think I could wrap up Phooey-style anything that the underlying library's layout mechanism provides. Layout in wxHaskell is particularly nice to work with, as they're already functional. The interest driving me to release & write up Phooey is as I mentioned in my reply to Brian. I wanted to understand and clearly express a pathway of transformation (refactoring) between the imperative style and a simple functional style. That transformation is what the paper-in-progress is about. Regards, - Conal On 12/12/06, Antony Courtney <antony.courtney@gmail.com> wrote:
Hi Conal,
Looks like an interesting system.
A few questions: - What is the underlying Arrow here, and how does it differ from Yampa? (And the obvious follow-on: If they are similar, why not just use Yampa?) - How will your layout model deal with layouts that aren't rectilinear? - Do you support events or switching (in the FRP sense)? - How do you plan to deal with dynamic collections (opening/closing new windows at runtime, or adding/removing widgets from a running GUI)? IMO, this is the biggest challenge to presenting a purely functional API but using a standard GUI toolkit for the underlying widget set.
Regards,
-Antony
On 12/12/06, Conal Elliott <conal@conal.net> wrote:
Announcing Phooey <http://conal.net/phooey>, a functional UI library for Haskell.
GUIs are usually programmed in an "unnatural" style, in that implementation dependencies are inverted, relative to logical dependencies. This reversal results directly from the imperative orientation of most GUI libraries. While outputs depend on inputs from a user and semantic point of view, the imperative approach imposes an implementation dependence of inputs on outputs.
Phooey ("*Ph*unctional *oo*s*e*r *y*nterfaces") retains the functional style, in which outputs are expressed in terms of inputs. In addition, Phooey supports dynamic input bounds, flexible layout, and mutually-referential widgets.
As an example of Phooey's style, below is a simple shopping list GUI. The *total* displayed at the bottom of the window always shows the sum of the values of the *apples* and *bananas* input sliders. When a user changes the inputs, the output updates accordingly.
Phooey is structured as an arrow, and this example uses arrow notation. The code:
ui1 :: UI () () ui1 = title "Shopping List" $
proc () -> do a <- title "apples" (islider 3) -< (0,10) b <- title "bananas" (islider 7) -< (0,10)
title "total" showDisplay -< a+b
I am working on a paper about Phooey. For now, please see the Haddock documentation <http://darcs.haskell.org/packages/phooey/doc> (which includes more examples), and try the code via
darcs get http://darcs.haskell.org/packages/phooey --partial
Directions for building are in the README<http://darcs.haskell.org/packages/phooey/README>file. Distribution tarballs are here <http://darcs.haskell.org/packages/phooey/dist>.
Comments and collaboration are very welcome!
Cheers,
- Conal
P.S. I'm very grateful for Don Stewart's recent pointers on how to create & release Haskell projects.
_______________________________________________ Haskell mailing list Haskell@haskell.org http://www.haskell.org/mailman/listinfo/haskell
participants (5)
-
Antony Courtney -
Brian Hulley -
Conal Elliott -
Neil Mitchell -
Steve Schafer