[Haskell-cafe] Problem with monadic formlets

Jeremy Shaw jeremy at n-heptane.com
Sat Aug 29 16:09:46 EDT 2009

At Sat, 29 Aug 2009 15:46:42 +0100,
Chris Eidhof (formlets) wrote:
> Confirmed. checkM is broken, thanks for noticing! I'll have a look  
> into it, I'm not sure whether it can be fixed. I was thinking of  
> removing all the monadic stuff from the formlets. I think this will  
> make for a much cleaner interface, monadic checking can then be done  
> afterwards.

I am still a fan of (and use) this version of Form:

newtype Form xml m a = Form { deform :: Env -> State FormState (Collector (m (Failing a)), xml, FormContentType) }

not sure how I would feel about the removal of 'm' from the
Collector. But 'xml' is nicer for me than 'm xml' because my collector
and xml generator are often in different monads. I can, of course make
them be in the same monad if I want:

type MyForm a = Form (IO XML) IO a

but I also have the option of just doing:

type MyForm a = Form (IO XML) IO a


type MyForm a = Form (HSP XML) IO a

At present, I actually have my collector do all the validation and
update the database. As a use case, let's assume that the form is
creating a new user account. One possible error would be using a
username that is already in use. Doing that check requires a database
query. In fact, it seems best if it does a database update, so that
there is no race condition between checking if the name is in use, and
actually attempting to create the account with that username.

If you remove the ability to do IO in the collector, then I believe I
would need to:

 1. run the collector to do the pure part of the validation.

 2. if the pure part succeeds, use the returned value to do the impure

 3. if that fails, then redisplay the form using the same environment
 that I used for #1, but passing in the impure validation errors.

One potential drawback that I see with this is that it may make it
difficult to the pass the error messages back to the specific formlet
element that failed so that you can display the errors in-line.

[Note: the following discussion reflects the pre-0.6 design].

Currently the environment we pass in is something like:

type Env = [(String, Either String File)]

The first component of the tuple is the name of the element. aka,
input0, input1, etc.

I would propose that we also pass in a Failures argument:

type Failures = [(String, ErrorMsg)]

where the first component of the tuple is the name of the element
(input0, intpu1, etc) and the second element contains ErrorMsg.

or perhaps modify Env to:

type Env = [(String, (Maybe ErrorMsg, Either String File)]

We would need to modify the Failing data type to:

data Failing a = Failure [(String, ErrorMsg)] | Success a

so that Failures would contain their location. 

Not all errors correspond to a specific form element. Let's say that
you have 3 drop-down boxes that combine together to form a date
selector. You want to validate the result of all three combined to
make sure they picked a valid date, and if it is invalid, you produce
an error message for that group, not a specific element. The problem
then is that there is no 'location' that corresponds to that group, so
what do you put in the Failure tuple?

I think you can use freshName to generate an extra 'virtual' name that
corresponds to the group as a whole.

I have been meaning to prototype this in the near future and see if it
actually works. I'll try to get something worked up in the next two
weeks (my sister is getting married next week, so my schedule is
pretty full).

- jeremy

More information about the Haskell-Cafe mailing list