I finally finished the Haskell blog project that I've been doing for a long time! You're looking at it now (unless you are reading this a few months/years after I wrote it, in which case I will probably have again re-implemented my blog software in my new language-du-jour...)
The blog software itself is not particularly interesting — fairly standard features, Atom feeds etc. It uses HDBC Sqlite for storage, and HStringTemplate for rendering (a nice library, BTW). For framework stuff, it uses my own Ella library. I didn't find a forms/validation library I could use, and ended up just using a few adhoc bits and pieces. I've used the lovely pandoc to allow reStructuredText both for my own posts and for comments, which is a nice feature IMO.
The main interest for me has been the learning process. You get a much better, rounded understanding of a language from a project like this than you do from the small code samples that people knock around.
The project nearly failed at the last hurdle. Everything was working, but when I uploaded to my server, it failed on some URLs. I realised it was a memory problem — the CGI program must have been killed for using too much memory.
At first, I thought the limits on the server must be unreasonably small. Understanding the output of +RTS -s -RTS is kind of difficult. When I eventually found out that GHC compiled programs never release any memory back to the operating system, I realised that it's the first figure—the total amount of memory allocated in the heap—that was killing me. On the bigger pages, this was over 160 Mb. At that point I stopped complaining to my web host!
By changing to ByteString instead of Data.Text for StringTemplates, and using ByteStrings in a few other places, I achieved a 4-5 fold reduction in memory usage, along with a significant speed up. Most pages now only use about 10-15 Mb to render, which is OK for a short running process I think. It's not ideal, especially when an additional 1k comment on a page seems to require at least 300k extra memory to render, but it's good enough for now. Profiling further will be very hard, as I suspect it will mainly be to do with the guts of HStringTemplate.
I'll be blogging about the experience of developing this over the next few days/weeks, and what I've learnt. It's certainly been enjoyable overall, although it's definitely had its pain points too!
I've put redirection in for all the old, crufty URLs, so there shouldn't be any broken links. Feed readers will likely be confused, sorry!
If you have problems getting through my spam protection, please let me know. It enforces a 10 second wait before it accepts submissions, which serves to prevent thoughtless comments as well as spam :-)
Comments §
I did indeed wonder why there were 24 new posts in my reader! No worries though, every feed does it to me at some point or another.
@Simon: Thanks, it's nice to know that it's useful to someone! A lot of the posts have been filled with boring details (mainly documented for my own benefit) on the one hand, and moaning on the other.
I'm also pleased to know that the 'Related' section works well — it was the first time in a good while that I did some raw SQL that would have been much harder to write using an ORM than by hand. It was quite pleasing to find that I haven't completely forgotten how to get some mileage out of SQL!
@MightyByte: Yes, I've looked at formlets. I concluded that it was far too simplistic and inflexible, especially in terms of the HTML that it produced.
AFAICS, most of the problems I have with it are not fixable. I did actually start to write a post about Haskell web frameworks in general. Here are my comments on formlets:
The major problem is that it has this abstraction which is very neat, but that very neatness means that trying to adapt it for use in the real world will break it horribly. There are all kind of real world constraints for web forms (like CSRF protection, custom HTML output, javascript integration, control over field names). It can't handle some of these at least, and I wasn't going to invest in it when I strongly suspected that it wouldn't be able to handle the rest in any nice way.
- automatic name generation: yes. That seems unavoidable given the formlets design goals.
- labels: the formlets library (at least the formlets-hsp layer) only sets the 'name' not the 'id'. So you can still manually set the id for the form element and the label element. Would be useful if the formlets library had a function that could generate a unique id. I think that should be possible, but I can't remember. I may have implemented it once..
- validation: the order should not matter. You can do, (,) <$> form1 <*> form2, or (,) <$> form2 <*> form1, and in your validation `check` (\(f1, f2) -> ...) or (\(f2,f1) -> ..) If you want to stick other controls in between form1 and form2 that is doable as well. Though perhaps not pretty.
- checkboxes: yes, you need to use generalInputMulti. formlets-hsp has support for checkboxes and multi-select input boxes
- validation errors: yeah, that is annoying. I implemented a prototype once that recorded the error location and let you insert the error messages inline. But the internals of formlets changed at the same time and I have never ported it to the latest version. In part because my solution didn't feel that pretty. See Demo.hs and TextDemo.hs:
http://src.seereason.com/examples/formlets-inline/
not sure what you mean by custom html output. You can use plug if you just want to wrap some extra divs around an element. With formlets-hsp, at least, there is a function that let's you set extra attributes on a form control. And, you can always use input', generalInput, etc, directly to implement whatever html you want. The low-level formlets library does not know anything about HTML. In fact, as the TextDemo above shows, you can use formlets in a plain old console app with plain text and keyboard input.
I have done some experimentation with generating javascript controls and integrating them with formlets. But it is still pretty hacky at the moment.
I am not clear what you think is problematic with regards to CSRF protection.
The biggest issue with formlets, IMO, is that you have to be careful to feed the formlet the same data both times. A formlet is run twice, the first time you use the HTML that is generated. When the user submits the form, you run the formlet again to get the validation function. So, you have to be sure that you pass the same (or similar enough) arguments to the formlet both times so that validation function is looking for the same field names that the form is actually submitting. In practice, this is not usually that hard, but it not obvious at first and can lead to issues.
As for control over the field names.. well that is the point of formlets. If you allow the developer to decide the field names, then when you compose two forms you might have clashing field names. By generating them automatically, you don't have that problem.
Of course, if you are manually setting the id fields, then you are back in the same boat. And, if you programmatically set the id fields, then it is hard use CSS to style the forms.
This is just one of many reasons why I say that the web is not very modular.
Anyway, formlets are interesting for what they offer:
1. composable forms
2. automatic creation of form data extract / validation function
3. 'easy' to redesign a form element (such as a date selection), because even if the form html is very different, you can just swap it in, (provided that it has the same type), and trust that it will just work.
They are certainly sufficient and usable for some designs. But they can also be annoying and limiting. They could be potential time savers if you are prototyping a site and changing your forms around a lot, even if you later have to make the forms static when you work on making the site look good.
But, hopefully there is something better that remains to be discovered. Unfortunately, the lameness of html+css+javascript combined with the horribleness of IE, makes doing anything great extremely hard ;)
- jeremy
ps. The formlets-hsp library is here: http://hackage.haskell.org/package/formlets-hsp. It is not a fork of formlets. It depends on the formlets library, and just adds some functions for generating various elements. That same could be done to add support for blazehtml, etc, with out having to modify formlets itself.
@Jeremy:
Thanks for your reply, and correcting any mis-information about formlets I may have spread.
I guess I'm not convinced that having composable forms is that useful in real life. In my experience, forms are not that big that you need to worry about conflicting field names, and simple combinations of forms are rare — or when you add new fields, certain aspects of the form and validation change significantly. The best example I can think of where forms are re-used is in Django's auth/forms.py — but even there, where re-use can be achieved by subclassing, there are cases where opportunities for re-use are spoilt by subtle differences.
My Haskell website makes heavy use of form composition. Formlets were hugely beneficial. I'll grant you that for common tasks like user login/registration, shopping cart checkout, etc, form composition isn't a huge deal. But there are plenty of real life applications where it is.