On Wed, Jun 22, 2011 at 5:23 AM, Jasper Van der Jeugt <m@jaspervdj.be> wrote:
No, inputList is more applicable for a dynamic list, whereas your list
consists of a fixed set of elements. Your problem can be solved more
easily by creating a simple combinator, as I've described on the
stackoverflow thread.

Thanks everyone for the input on this.  What I've been thinking, and what I believe I'm seeing in other peoples solutions, is that the fundamental issue in building these types of forms is the interpretation of later parts of the form depends on the interpretation of the earlier parts of the form (e.g., a hidden field giving the length of the list).

Of course applicative doesn't let you do this as it only lets you form fixed sequences.  The additional power required is precisely what what the monad structure adds.  Specifically, I'm thinking the join operator should let you specify this wonderfully.  Under Yesod's GForm this would be

join :: GForm s m xml (GForm s m xml a) -> GForm s m xml a

The idea would be that your outer GForm would read (using the list example) the hidden length parameter and use that to construct and return the (now fixed structure as you know its length) inner GForm using applicative composition like (as in the SO thread).  Apply join and you get a GForm returning a list.

I was wanting to work it out exactly, but that would be several more days as I'm a bit short on time and still not that familiar with the libraries, so I'll just throw a rough sketch of usage

data Person = Person { age :: Int, name :: String }

personForm :: Maybe Person -> Form s m Person
personForm mperson = fieldsToDivs $ Person
  <$> intField "Age" (fmap age mperson)
  <*> stringField "Name" (fmap name mperson)

peopleForm :: [Maybe Person] -> Form s m [Person]
peopleForm mpeople = join $ build
  <$> fieldsToDivs $ intField "Number" (Just $ length mpeople)
  where
    build n = traverse personForm $ take n $ mpeople ++ repeat Nothing

The join should be pretty trivial to implement as GForm is a monad transformer stack.  The only extra bit would be to do the equivalent of wrapping the in inner form in a deeperFormIdent before splicing it in (as I believe is currently being done with with the digest functors list handling design).

Cheers!  -Tyson