[web-devel] Re: On the state of Haskell web frameworks

Christopher Done chrisdone at googlemail.com
Wed Sep 22 13:58:19 CEST 2010


On 22 September 2010 12:27, Chris Eidhof <chris at eidhof.nl> wrote:
>> 3. Takusen: I needed to talk to a PostgreSQL database to save/read
>> pastes, just a few lines (for setup) in an isolated module:
>> http://github.com/chrisdone/amelie/blob/master/src/Amelie/DB.hs
>
> Would you use this for larger projects? I always feel that this is too low-level (coming from Rails). There is Michael's work on his persistence library, Kevin's work, and I'm also playing around in this area.

I'm using HaskellDB at work which automatically derives your tables
into types. That's quite nice but it needs some work to be more usable
as it's kind of cumbersome to program in (mostly with writing type
annotations). I need to use PostgreSQL at work because other people
need to be able to use my database in different languages and
applications, I used it for Amelie because I'm used to it. Looks like
the Persistence library has a PostgreSQL backend so I should try that
out. It's pros and cons with Takusen:

1. I like that you have flexibility over implementing the left fold
for Takusen, but also don't like that I have to do that.
2. I like that you have the flexibility to construct a value however
you want, doing transformations and things,

    , tags     = maybe [] (splitWhen (==',')) tags'

but I also don't like having to write out every field I want in order
to construct a value:

    makePaste pid' title' content' tags' author' lang' chan' created'
an_of expires xs =
        DB.result' (paste:xs) where
          paste = Paste { pid = pid', ...

This is also annoying:

updatePaste :: Paste -> DBM mark Session ()
updatePaste Paste{..} = DB.execDDL (DB.cmdbind stmt params) where
  stmt = unwords ["update paste set"
                 ,fieldSpec
                 ,"where id = " ++ show pid]
  fieldSpec = intercalate "," $ map spec fields
    where spec (key,_) = key ++ " = ?"
  params = map snd fields
  fields = [("title",DB.bindP title)
           ,("content",DB.bindP content)
           ,("tags",DB.bindP $ intercalate "," tags)
           ,("author",DB.bindP author)
           ,("language",DB.bindP $ fmap lid language)
           ,("channel",DB.bindP $ fmap cid channel)
           ,("annotation_of",DB.bindP annotation_of)]

It's both manual and not type checked.

So what I'd want from a DB library is the ability to retrieve from the
database type-correct value automatically, and write them or update
them back, but be able to transform certain fields which don't make
sense in the DB but make sense in Haskell. Likewise, for JSON, I have
problems with my data types not making any sense for JSON, i.e., when
I send a value from JavaScript to a Haskell server as JSON, I don't
want to have to send the `id' field -- but this `id' is in my local
data type because it's useful for the Haskell code. Likewise when
rendering it to JSON it outputs the `id' field or some $foo field
which shouldn't be shown. How do you remove old and add new fields to
and from JSON/DB/XML/whatever without having to implement *everything*
yourself? I also don't want to have to sacrifice the clarity of my
Haskell data types in order to adhere to a particular representation
of it (JSON, DB, etc.) just because I'm using deriving
(Typeable,Data). How is your work going?

> That is great. Maybe, in larger projects the boundaries might not be so clear. I think design patterns are overrated for small projects, but become really beneficial once you scale up.

That's true, as the project sizes increases so does the need for
design consistency. I'm going to add an RSS feed to amelie, and this
isn't a page. It's a another type of resource. In this way I should
abstract Pages into Resources, but it's nice to have a common
vocabulary and way of doing things so I'll update it to Controllers,
Models, Views, etc.

Formlets are an interesting one, because technically they combine the
controller and the view for forms. In the end I decided it was a view
and put it in the HTML module, because I have separate logic in the
Pages module for getting the value of the form and then talking to the
DB, etc. I found myself encoding some non-view-ish properties in the
formlet, though. I suppose this is one of those cases where you just
do your best to separate the view and the controller without
sacrificing clarity and concision. What do you think about how
formlets fit into the MVC pattern?

> Have you looked at the web-routes package? Again, this might only be useful for more complex applications. The idea is that you use datatypes to encode all possible URLs, and linking is then done by building a value of that datatype: it is not possibly anymore to build invalid URLs.

This looks basically perfect as a replacement for my Routes and Links
modules if a little involved. I'll look into it when I get home. Looks
like I should be able to drop my custom modules entirely. I'll let you
know how I get on with it.

Cheers!


More information about the web-devel mailing list