Re: Yesod forms with page flow
On Tue, Jul 31, 2012 at 6:04 PM, David Flemström
<david.flemstrom@...> wrote:
> On Tue, Jul 31, 2012 at 4:34 AM, Michael Snoyman <michael@...>
> wrote:
>>
>> What I meant was that the path would be the same, but the query string
>> would be different. This would still allow the back button to work
>> correctly.
>
>
> For clarity, I'm referring to something like a link <a
> href="/wizard?page=first&field1=foo">Back</a> (which is how the link would
> have to look for your single-route system), not the browser's back button
> (accessed via [backspace] in most browsers). Is that the kind of system you
> meant (Obviously with the field1 part being encrypted/stored in the
> session/whatever)?
>
> I definitely see that working, but one could also simply provide a
> route<->wizard page isomorphism to the runFormWizard function, so this is
> really a non-issue anyways (i.e. one could trivially replace
> "/wizard?page=first" with "/wizard/first")
We're completely on the same page (pardon the pun). I was strictly
speaking from an ease-of-use perspective, the two approaches are
completely isomorphic.
>
>> I'm also not sure what you mean about the wall with
>> yesod-form: you're free to use separate routes for GET and POST if you
>> want, it's mostly for convenience that all the examples use the same
>> route.
>
>
> Yes, now I do of course simply use generateFormGet and generateFormPost, but
> were those two always separated? Maybe I'm confusing yesod-form with
> digestive-functors (that definitely had that issue for a long time), but the
> point remains that the API you displayed in that short code snippet wouldn't
> allow for that separation of responsibilities. : )
Actually, for *almost* all use cases, you can just call runFormPost
and ignore the result, and it will act like generateFormPost. The one
exception is if you use it from a POST handler for a previously
submitted form, in which case runFormPost will pick up the old
results. That was the purpose of adding the generateFormPost function.
(generateFormGet is a bit more important, as it's more likely to have
previous query-string parameters.)
I would recommend for a wizard API to have both a `generate` and `run`
function, but in most cases you should be able to get full usability
from just the `run` versions.
>
> I'll start working on some prototypes of this. Things that concern me
> include that if one wants a static guarantee that all pages of a wizard have
> been filled out (so that the user does't do pure def >>> wizardPage1 >>>
> wizardPage3 (imagine some form of kleisli arrows) which would leave some
> fields of the data structure potentially undefined). Using Applicative for
> the form fields is definitely not enough, because then there can't be a
> tree-like wizard structure, but I could see the Monad interface working,
> albeit sans some of the static safety guarantees an Arrow would allow for.
I think a Monad interface should work perfectly, you would just need
to have some short-circuiting logic. Something like:
x <- form1
case x of
Foo -> do
y <- foo
return $ Left y
Bar -> do
y <- bar1
z <- bar2
return $ Right (y, z)
If you get to `bar1` and the form hasn't been submitted yet, then a
new form will be returned immediately. I guess we would need some kind
of combination of Writer (for the previously parsed values) and Error.
Michael