Rich Internet Applications using Haskell

Hi all, I just though I'd let y'all know that I've started work on "Dingo", a small framework for creating Rich Internet Applications (RIA) in Haskell. The basic idea is that you write Haskell code and the framework generates all the browser-related bits you need (HTML, JS, state serialization, etc.) at runtime. Writing an application is centered around Widgets and Events which are just what you'd expect if you were writing a Gtk+/Swing/SWT/Qt application. The way the application starts is that you provide a "start the application" callback to a "runServer" function. When a client connects to the server the first time, the "main" callback gets run and can set up the main application widget however it wants. There's a basic example in Main (see attachment). It doesn't really do much interesting, but it does demonstrate that state transfer and updates (client->server and server->client) work and that events work at a basic level. I've attached a tarball of my not-even-close-to-pre-alpha code -- I won't bother putting this on Hackage since it's not actually useful for any real work yet. To get something working in a browser ASAP I've taken some... uhm, liberties with code quality, so I will not be held responsible for self-induced eye-gouging as a result of looking at the code. The current status is basically: * Uses Happstack-server for serving HTTP. Given Dingo's extremely simple serving needs it may be a bit of a "heavy" dependency, but it works for now and it's not really a high priority to change this. It's also definitely NOT eye-gouging-inducing, so that's a plus :). * Only very simple widgets are included: A "panel" container widget (corresponds to an unstyled <div/> currently) and "input", "select" and "button" elements (corresponding to their HTML counterparts). The idea is that Panel will be expanded to a more general container which can lay out subwidgets horizontally or vertically. * ALL mutable widget state is transferred to/from the browser for every callback. This isn't an issue with the simple example in Main.hs, but it might become an issue for larger widget hierarchies. * Only ONE client supported at a time. You read that right. Weird things will happen if you try to connect multiple clients. Immediate plans: * Find a better way to handle state transfer to lessen the burden of having to write JSON <-> JS and JSON <-> server-state translations by hand. I'm thinking that some of the ideas from the "Invertible Syntax" paper ( http://www.informatik.uni-marburg.de/~rendel/unparse/rendel10invertible.pdf ) may apply for this. Text.JSON.Generic may also be workable, but I had some trouble getting it to work with Text values and it doesn't help with generating the widget-specific JavaScript. * On a related note, I need a better way of generating JavaScript than string concatenation. Unfortunately, HJScript seems to be *too* strict about typing. * Expand the selection of widgets with more complex widgets so that I can get a feel for how the current Widget type class may need to be generalized/tweaked. Something requiring CSS would be good too, so I can hopefully get a basic theming mechanism down. * Get rid of the "one client only" limitation so that Dingo may actually become somewhat useful in practice. * A way of tracking client-side and server-side updates so that only things that have actually changed are transferred between client and server. For the server side this should be easy enough and for the client I'm thinking of something like attaching a "change()" event handler to all relevant DOM elements. Comments and suggestions appreciated. Cheers,

On 2011-02-10 08:12, Bardur Arantsson wrote: [--snip--]
Immediate plans: [--snip--]
Oh, and I forgot: Widget composability. I'm not really sure how best to proceed with this and whether it's really necessary. The current "workaround" is to have ordinary Haskell functions (running in the CallbackM monad) which take a set of event handlers as callbacks and which return a container widget. Something like: mkMyLastNameFormWidget :: CallbackM -> CallbackM Panel mkMyLastNameFormWidget onChange = do p <- mkPanel label <- mkLabel "Last name:" input <- mkInput addToPanel p label addToPanel p input return p Getting at the "composed" state is not trivial, though. Currently that would require mkMyLastNameFormWidget to also return the "input" widget which would break encapsulation. Cheers,

On Thu, Feb 10, 2011 at 1:12 AM, Bardur Arantsson
Hi all,
I just though I'd let y'all know that I've started work on "Dingo", a small framework for creating Rich Internet Applications (RIA) in Haskell. The basic idea is that you write Haskell code and the framework generates all the browser-related bits you need (HTML, JS, state serialization, etc.) at runtime.
Writing an application is centered around Widgets and Events which are just what you'd expect if you were writing a Gtk+/Swing/SWT/Qt application. The way the application starts is that you provide a "start the application" callback to a "runServer" function. When a client connects to the server the first time, the "main" callback gets run and can set up the main application widget however it wants.
Awesome! I worked on something similar. It's a very interesting and challenging problem. I wish I had more time to work on it. But at the very least, I can try to support your efforts :)
The current status is basically:
* Uses Happstack-server for serving HTTP. Given Dingo's extremely simple serving needs it may be a bit of a "heavy" dependency, but it works for now and it's not really a high priority to change this. It's also definitely NOT eye-gouging-inducing, so that's a plus :).
Yeah. I think ultimately you end up needing all the other high-level stuff something like happstack offers anyway.
Immediate plans:
* Find a better way to handle state transfer to lessen the burden of having to write JSON <-> JS and JSON <-> server-state translations by hand. I'm thinking that some of the ideas from the "Invertible Syntax" paper ( http://www.informatik.uni-marburg.de/~rendel/unparse/rendel10invertible.pdf) may apply for this. Text.JSON.Generic may also be workable, but I had some trouble getting it to work with Text values and it doesn't help with generating the widget-specific JavaScript.
Yeah, I ran into this type of problem as well. I have used the 'json' library and the 'RJson' library. Using generics is nice because it seems like you don't have to write anything on the server-side. But that can mean that the javascript side is really ugly. At first it seems like you are going to be able to get away with just one type that you define in Haskell which gets automatically converted to JSON (via generics or Template Haskell), and then the JSON gets automatically converted to a javascript object. But in my experience you ultimately end up wanting three different types. The native Haskell representation, the native javascript object, and the JSON representation that is used to transfer information between the sever and the client. I have hypothesized that maybe what you want is a new special language where you define your types, and a compiler which parses that specification and outputs Haskell data types, javascript objects, and code for Haskell<->JSON and javascript<->JSON conversions. * On a related note, I need a better way of generating JavaScript than
string concatenation. Unfortunately, HJScript seems to be *too* strict about typing.
Yeah. I have used HJScript a fair bit. It is a bit brutal. My current opinion is that HJscript is good if you are trying to create a 'binding' to existing javascript code. You can use it as a way to create a type-safe interface to existing javascript code. But it is really tedious if you are trying to use it to create a bunch of new javascript code... At some level, this is not just a HJScript issue. When writing Haskell code, you want nice algebraic data types. And when you are writing javascript code you want nice objects. But there is no really nice way to automatically convert between the two. I often end up resorting to creating my Haskell ADTs by hand and the javascript objects by hand, and then writing code by hand to convert between the two 'optimal' representations. This makes it much nicer to work with the data in each of the languages -- but it is also really tedious to write. Unfortunately, it is not pure 'boilerplate' code either. If it was a mechanical transformation, then it could be easily automated. But I feel like the transformation requires a lot of human smarts to figure out how to best represent things on both sides. Though, there is still a lot of boilerplate involved... that is where the special language could come in useful.. it would allow you to specify the Haskell vs Javascript representations, but then automate a lot of the conversion process, and help keep both halves in-sync. But It's still pretty vague in my mind :) Comments and suggestions appreciated. Hopefully I will get a chance to look at this sometime in the near future. If you have any happstack specific issues, please ask on the mailing list or #happs channel. We are happy to accommodate. But if you don't complain, we won't know anything needs to be fixed :) - jeremy
participants (2)
-
Bardur Arantsson
-
Jeremy Shaw