Friday, 20 April 2007

Haskell for GUI Programing

Haskell has two popular graphical toolkits, Gtk2Hs and wxHaskell. I have only used Gtk2Hs, therefore, my comments come from my experience with it, but since i will speak about GUI programing in general, i guess they could perfectly apply for wxHaskell too.

Programing on Haskell offers many benefits; and i have found it gives a good combination of simplicity and robustness for the graphical user interface development model.

Simplicity: it's very easy to program graphical user interfaces on Haskell; you can get a window and a very useful widget inside it with few lines of code. Haskell as a declarative language takes to the extreme the rapid application development cycle, I'd dare to say that it perfectly can compete with the so called scripting languages in this regard.

GUI programing using gtk2hs is just a big IO monad; and though many Haskell'ers can find this 'ugly' , don't fool yourself, monadic programing and the 'do' notation offers a concise and clean way of writing easy-to-read imperative-like code, and it's when the code becomes bigger and bigger when you realize of its beauty; graphical interfaces can gradually grow very much, so i see this like an advantage and good reason for using Haskell too.

Robustness: I think we pretty much know the main difference and advantages of Haskell over other traditional programing languages here (laziness, purely functional, type inference, etc) and how all these features make a perfect combination to keep your program bug-free and easy to change/fix ; for example, it's amazing to see how you can design and develop a whole graphical user interface without mutable variables or global variables (i admit i found this a bit difficult to believe before using Haskell) ,but it's possible! , and it offers so _many_ advantages, the more significant for me has been the lack of bugs coming from these imperative features, it also helps you to reason about the layout design from other perspective, which is very interesting and benefits a more modular approach instead of an ad-hoc design where you just keep updating bunch of global data structures in-place through signals and events.

Other definitive characteristic that makes robustness possible is the static typing and type system. Haskell helps you to reason about 'types' all the way down along your code, and that's not less true for GUI programming. Many dynamic and weakly typed languages have tried to show to the programming community they are the best way of getting GUI stuff done right and they usually strength the fact of rapid development cycles in opposition to lower-level approaches; really, i can understand this position a bit, most of the first GUI systems came from these kind of languages, nevertheless, i think most of them are in disadvantage with Haskell due to the type system.

Types help both to the machine and to our human brain to think in terms of a common abstraction as an interface for an universe of different values. We can reason 'in harmony' with our computer in an easier way through these abstractions than with a dynamic, typeless language. This kind of reasoning, evidently, will help to the programmer to get more robust programs (and i think i don't need to explain here why that's important for GUIs) and at the same time i have realized this reasoning fits well into the different GUI components representations too; what i am saying here, is that types are practically (and conceptually) a better representation than the objects offered by most of the object-oriented programing languages to work and manipulate graphical elements, and i highly think this is going to contribute to the popularity of Haskell in the future, as a possible replacement or improvement for many aspects of the OOP approach.

And if those aren't enough reasons to use Haskell for GUIs; here is the third one: it's fun.

Since Haskell gives you so many features to make simplicity and robustness of code a reality without too much effort, you pretty much can enjoy coding without worrying about low-level details or possibles unwanted side-effects behaviours, what else could you ask for? :-)

Ok, too much keyboarding for now ... if you still aren't convinced, there is only one way of knowing for sure, just give it a try!

Let's say, there are just different approaches, and they pretty much depend on the whole design of your code.

Himerge doesn't use any mutable state, and i assume is because of two visible characteristics on its global design:

1 - Small functions with well defined behaviours. 2 - A coherent data flow passing through the arguments of each functions.

If not all, i think these two points are fundamental to omit possible mutable states on the code.

One of the effects of this design, is that functions can contain many arguments (5~6), nevertheless, using the Haskell type signature, i have found this even easier to maintain than using mutable states.

Say your program maintains a pretty deep data structure that consists of nested records. What do you do if you need to update a field somewhere deep down in this structure in response to an event? You can't just poke at the field like in Java/any other imperative language. You need to reconstruct the entire thing. *Very* inconvenient.

Yes, you can solve this by putting every field inside an IORef, but then you can't apply pure processing functions to the structure anymore, so you loose the functional approach.

This makes IO monad based GUI programming in Haskell very clumsy, for all but the smallest programs.

Sadly, functional reactive libraries in Haskell doesn't support more than a few of the basic widgets. Maybe this will change.

I'd rather program a GUI intensive program with a large "model" structure in Scala, than in Haskell, if it weren't for the fact that I love all other aspects of Haskell so much ;)

... Yes, you can solve this by putting every field inside an IORef, but then you can't apply pure processing functions to the structure anymore, so you loose the functional approach. ...

An IORef is just a mutable reference inside the IO monad, so i don't get what you mean you can't use 'pure processing' functions.

As the IO monad, you can perfectly use pure functions inside it, if that's what you mean, and that's pretty much the way the IORef data type works, so, the purity of the approach is preserved.

... This makes IO monad based GUI programming in Haskell very clumsy, for all but the smallest programs. ...

I don't agree. Haskell is a perfect language for prototyping and rapid development, so the programs tend to be smaller than other approaches (it can even compete with scripting languages); even if you are using monadic code, or a big IO monad, it will more likely keep smaller than an equivalent program in an imperative language. And considering the Brook law of "the time it takes to write a program depends mostly on its length" , i think Haskell can actually be less clumsy than most languages out there.

... Sadly, functional reactive libraries in Haskell doesn't support more than a few of the basic widgets. Maybe this will change. ...

Let's hope it changes, but, that doesn't stop anyone of getting GUI stuff done in Haskell. Right now there are several popular bindings for graphical interfaces in Haskell, and it is amazing to see how (for example) gtk2hs supports many of the gtk-2.8 library options, and it is actually one of the few languages with support for this version of the API, according to the languages bindings list of the gtk site. So, the thing isn't that bad as many people might believe.

I do think you can get a "serious" GUI application on Haskell with the current toolkits available. It will very much depend upon thinking 'less' imperatively and more 'functional' at the end ;-)

To do that in Haskell someOtherField needs to be an IORef, or you'd have to write lots of code to reconstruct the entire model type. So, the easiest and most practical thing to do is to let it be an IORef, which is what you like to do, right?

But then how do you apply the pure function f :: Model -> Model to gui.model?

You can't, because if the function is supposed to do something with the value *inside* someOtherField, it has to have the type Model -> IO Model.

Your code example is _very_ imperative , so it makes more sense in an imperative design, and probably much more from an imperative language. That's why i ended up my last comment considering that it is important to think functional while using a functional language, many things are very evident when you do so.

I see you also imply many mis-conceptions in your posts, for example, that IO functions aren't pure or .. not _so_ pure like other functions, if that can make sense at all, so i really don't get what you mean.

A function of type , f :: Model -> IO Model would be as pure as f :: Model -> Model , making the IORef solution pretty valid, nevertheless, i still think there are better ways , or if you want to call it like this, more functional ways of solving this:

Notice how your code is omitting many obvious concepts here, for example, mapping and returning a value of the same data type that is received, plus, practically all the representation comes from the ADT, and not a mutable global structure, even, the behaviour of the function is itself determined by the representation you want to give to the ADT, and it does it in a perfectly pure way.

This kind of code and design is so common in Haskell (and functional programing), that the language itself even offers a class, called Functor, that helps to do precisely that, to delegate the behaviour of a function to a specific data type.

You can't, because if the function is supposed to do something with the value *inside* someOtherField, it has to have the type Model -> IO Model.

That's an 'imperative' perception. There is no need to do anything *inside*. Not in this case.

But isn't the point of Haskell to do pure processing on your data?

Always, that's what you do even using IO monads.

You keep assuming an IO function is 'impure' or introduces some impurity to the program. I recommend you to check some documentation about it, because it isn't so.

The pure function I'm talking about is an entirely different function. It doesn't have anything to do with the event handler.

I want to be able to write both the event handler, that does direct updating of fields, AND the pure function that processes the data structure. Imagine a program that both responds to events AND does some kind of core program functionality that is entirely purely functional. The problem is that you would need two different data structures for this, an imperative one that contains references, and a more "functional" one without references.

Now, say you have a preference panel where the user can configure local settings for a selected package, like what arguments to pass to "configure". When the user press "Apply" for this panel, the lbi field needs to be set to Nothing, to indicate that the configure step needs to be re-run (say a background thread handles building the package).

I have one IORef to the whole model, because I want to be able to update the model. But I also have an IORef for each package, because it is convenient to be able to directly poke at a package. Like in the apply button event:

If I didn't have an IORef around each package, I'd have to write more code here. I'd have to write a function that has the type Model -> Model, that just changes the particular field inside the particular package inside the model. Then update the model using the "global" IORef. That's pretty inconvenient, and thanks to the per-package IORef I don't have to do that. And this, wrapping a field inside an IORef, is something you'd do for every specific field/data structure that you want to directly update, to avoid the incovenience of reconstructing the whole model.

So far so good, but what if I want to apply this function to the model:

show :: Show a => a -> String

I can't because I can't instantiate the IdeModel structure in the Show class. Atleast not if I want to show the packages, since they are wrapped in IORef's.

Do you see the problem? You choose between the direct-updating feature,and being able to apply normal pure Haskell functions in various ways to the data you are working with.

You don't really choose between that when programming in an imperative language because the distinction between references and values is not that strong.

Note that, in my opinion, you NEED the direct-updating feature if you are arguing that GUI programming in the IO monad using gtk2hs is as easy as doing the same style of GUI programming in an imperative language.

To point this out was the reason for my original comments.

Btw, all of this only applies if you really do any fundamental processing of your data structure that best can be done in a pure way. Otherwise it's of course fine to have references everywhere.

If I didn't have an IORef around each package, I'd have to write more code here. ...

You probably will, but it might still be less code than in an imperative language.

I'd have to write a function that has the type Model -> Model, that just changes the particular field inside the particular package inside the model. Then update the model using the "global" IORef.

That was one of the point of my comment , that is _actually_ possible and convenient to avoid mutable state in GUI programming using Haskell, you keep thinking in terms of _updating_ , because that's the 'imperative' way of doing it.

I precisely believe that designing your program with a coherent data flow passing through functions arguments avoid the mutable state approach (among other design patterns). If that's the way you want to do it, it's up to you, but i have personally found it easier to reason about it instead of global or local mutable structures.

Do you see the problem?

Seriously, i don't see it. Either the IORef and the non-usage of mutable state is a valid pure approach in Haskell.

And i really find the example of the Show class irrelevant for the subject. If a data type can belong or not to a class doesn't necessarily make a function more pure than other.

Note that, in my opinion, you NEED the direct-updating feature if you are arguing that GUI programming in the IO monad using gtk2hs is as easy as doing the same style of GUI programming in an imperative language.

It's precisely my point, that Haskell programming without mutable references can be as easy or even easier to develop than the imperatives approaches.

But you need to design your _whole_ program functionally. And that might require a mind-change too.

All your points, though valid at some degree, are still coming from an imperative perspective, for example, you keep insisting in monadic code being impure (or at least, what i have understood so far) , or that you _need_ a mutable data structure because you need to change a specific model.

Let's see if i try to explain myself better.

You have to treat your _whole_ program as a function, instead of some parts of it; if you start looking at it from that perspective, you will realize, that mutable states can be omitted, or might be unnecessary to accomplish a similar functionality.

My main point is that, encapsulating, or packaging possible mutable variables around your code, though it might give you a smaller code, i have found that a design, where you can access _all_ the possible model state through the normal variable declarations and along with well defined data types (and i mean, data types designed with the whole program as a function in mind) can offer more advantages instead of the common approach of a global structure , or even a local one to update in-place; some them: easier to track variable's life and contents, less functions inside the IO monad (Show available), it can avoid the creation of ADT with mutable state, and many more.

And with all this, the code will more likely be smaller than in an imperative language. I know it's difficult to believe it, i myself couldn't believe it either, until i started trying it by myself, and i invite you to do it too.

A function f :: a -> I0 a is not a pure (referentially transparent) function, hence the IO type. Okay, you can argue that the whole program is a function that transforms the world, but that's not the usual view of programs.

I took a look at hImerge just to see if there's something I'm missing, but it turns out that you are using mutable state all over the place AFAICS, by storing and manipulating it in the widgets directly. This is not different to using IORefs to capture mutable state. This approach doesn't solve the problem I mention.

f :: a -> IO a is a perfectly pure function, it just happen to contain code with side-effect. That's why monads are usually explained as containers, since they just encapsulate this code, but preserves the purity of the function.

gtk2hs takes care of the mutable state, i myself didn't have to explicitly create and manipulate such a references (like using IORef types) on my code; as opposed to the common practice of including such a behaviour and design on GUI programing. I probably had to be clearer about it though.

Say your program maintains a pretty deep data structure that consists of nested records. What do you do if you need to update a field somewhere deep down in this structure in response to an event? You can't just poke at the field like in Java/any other imperative language. You need to reconstruct the entire thing.

*Very* inconvenient.

(I didn't read all the comments so I hope this is not a dupe but anyway ...)

This problem is not especially linked to GUI design, but more generally to any haskell program manipulating complicated and deep datatypes.

I'm currently developing a program of that sort, so I stumbled on this problem : how can I modify one field lost deep into my complicated datatype ? haskell offers syntactic sugar for the simple case :

> myData {myField = newValue}

but it doesn't work when you want to change a field wiithin "myField".

Well, there is an elegant solution : "functional references". Basically, it allows you to build getters and setters functions for all your datatypes, with the huge advantage of being composable. That way, you can do things that looks like :

it is interesting, that you stay away from IORefs (I can't do that). Could you tell me how do you achieve this: two buttons, one when clicked, prints number of times it has been clicked to stdout, seconed button resets to 0 on click.

it is interesting, that you stay away from IORefs (I can't do that). Could you tell me how do you achieve this: two buttons, one when clicked, prints number of times it has been clicked to stdout, seconed button resets to 0 on click.

As explained in some previous comments , there _exist_ IORefs , but they are internal to the gtk2hs library ; so I just use them 'implicitly' if you want to call it so.

It's just a matter of how you design your own application , from my second post on this thread:1 - Small functions with well defined behaviours.2 - A coherent data flow passing through the arguments of each functions.

These are two factors that really helps to reduce or even remove the need for explicit IORefs in your application code.

Following a more pragmatic explanation of your button example; one way that comes from the top of my head right now to avoid explicit IORefs would be to encapsulate the count into one of the object components of gtk2hs ; for example you could save the value inside a widget container.

This will evidently be using mutable state , but underneath your code , and it won't bring out any unnecessary IORef to the application explicitly. This approach have been substantially productive for me , in any case, IORefs are (in my opinion) more a way to address internal behaviour inside an application (library bindings in this case) , than a common user level construct, and it's the way I am using them so far.