[racket] formlet-fill

From: Jay McCarthy (jay.mccarthy at gmail.com)
Date: Mon Nov 21 08:32:18 EST 2011

Your version that breaks mainly breaks because you are mutating the record
and recomputing the formlet, rather than saving the formlet in the
continuation for both the display and the processing.

As far as what you'd like to write, I think that's a good start, but
there's a few problems.

1) A line like

{(to-string (required (text-input))) . => . name}

fails to explain how the name can go back into the formlet on the LHS,
because in general formlets may not be able to have a default value like
that.

2) A line like

(formlet-fill record-formlet record)

seems to indicate that you will fill the formlet from the contents of
record, which is a hash table, but the results of the formlet are six
values, so there is no connection between the thing that comes in and the
thing that comes out. And in general that's pretty hard because the output
can be any computation of the values bound by the formlet.

I can imagine implementing something like...

(define record-formlet
            (formlet
             ;; Define the input format
             ([name (hash-ref this 'name)]
              [company (hash-ref this 'company)]
              [address (hash-ref this 'company)]
              [city (hash-ref this 'city)]
              [state (hash-ref this 'state)]
              [zip (hash-ref this 'zip)])
             ;; Define the display format / input mapping
             (#%# ,{(to-string (required (text-input))) . <=> . name}
                  ,{(to-string (required (text-input))) . <=> . company}
                  ,{(to-string (required (text-input))) . <=> . address}
                  ,{(to-string (required (text-input))) . <=> . city}
                  ,{(to-string (required (text-input))) . <=> . state}
                  ,{(to-string (required (text-input))) . <=> . zip})
             ;; Define the output format
             (values name company address city state zip)))

where the rest of your code works, except you'd also need to use the same
servlet when you process (because it may use the old contents as defaults.)
I can imagine a set of macros for restricted kinds of matching input and
output (like hash tables or lists)

Right now, a formlet's type is basically (Nat -> (values Display (Bindings
-> Answer)), but this would make it so its type was something like (Nat
LastAnswer -> (values Display (Bindings -> Answer)), including the formlets
that are returned from to-string, required, and text-input.

Does that seem more palatable?

Jay

On Sat, Nov 19, 2011 at 7:32 AM, Jordan Schatz <jordan at noionlabs.com> wrote:

> Let me see if I can explain what I'm after better. The app that I am
> porting is mostly CRUD (Create Read Update Delete), one twist though is
> that we never explicitly use create, if a user wants to create a new
> record they actually edit a copy of the most recent record, so a form
> always has values (though not necessarily the same "default" values).
>
> If I understand the docs, then this:
> http://noionlabs.com/formlets/save-formlet.rkt
> Is the right way to do it. The general pattern is that to have a formlets
> with values I need to have a formlet-maker function, that creates the
> formlet, and I need to save that formlet for the formlet-process
> function.
>
> The docs seemed to suggest that I needed to save that formlet (and not
> use formlet-maker to make a new one) for use with formlet-process, but
> this http://noionlabs.com/formlets/generate-new-formlet.rkt does work. I
> assume its not common/best practice though since its easy to break:
> http://noionlabs.com/formlets/breaks.rkt
>
> What I'd like to be able to write is:
> http://noionlabs.com/formlets/formlet-fill.rkt
>
> I don't really want to make a new formlet, or save it for use in the
> processing step, I just want to be able to modify the values in the
> formlet before sending it to the client.
>
> Shalom,
> Jordan
>
> On Fri, Nov 18, 2011 at 04:10:29PM -0700, Jay McCarthy wrote:
> > You need to make a new formlet that knows about the default values.
> >
> > We could imagine a macro that makes this nicer, but in general it will
> need
> > to be something that creates a new one.
> >
> > Is there some reason you don't want to do that?
> >
> > I can imagine something like (formlet-display) that took the old values,
> > but then it would be more monad-like than arrow-like, and wouldn't really
> > be a formlet. If you can clarify your use case, it may be worth
> > investigating.
> >
> > Jay
> >
> > On Fri, Nov 18, 2011 at 1:13 PM, Jordan Schatz <jordan at noionlabs.com>
> wrote:
> >
> > >
> > > I have a pattern of user interaction that is common in my web app:
> > >
> > > I have a db record, I present it to the user in a form, the user edits
> > > the form, submits it, server side logic validates it, and then returns
> > > the same form with the updated record.
> > >
> > > I want to port the app to Racket, and I thought formlets would be just
> > > the thing, but I don't see how to fill the values into the formlet
> > > without making a new formlet... seems I need a (formlet-fill
> > > (some-values)) to go along with the formlet-display and
> formlet-process.
> > >
> > > Here is what I have: (Which doesn't work)
> > >
> > > ----------------------------------------------------------------------
> > > #lang web-server/insta
> > >
> > > (require web-server/formlets)
> > >
> > > (define record (hasheq 'title "this is a title"
> > >                       'body "post body"))
> > >
> > > (define (start request)
> > >  (edit-record request))
> > >
> > > (define (edit-record request)
> > >  (local [(define (record-formlet our-values)
> > >            (formlet
> > >             (#%# ,{(text-input #:value (string->bytes/utf-8 (hash-ref
> > > our-values 'title))) . => . title}
> > >                  ,{(text-input #:value (string->bytes/utf-8 (hash-ref
> > > our-values 'body))) . => . body})
> > >             (values title body)))
> > >
> > >          (define (handle-response request)
> > >            (define-values (title body)
> > >              (formlet-process our-formlet request))
> > >            (set! record (hasheq 'title title
> > >                                 'body body))
> > >            (start (redirect/get)))
> > >
> > >          (define (response-generator embed/url)
> > >            (response/xexpr
> > >             `(html
> > >               (form ([action ,(embed/url handle-response)])
> > >                     ,@(formlet-display our-formlet)
> > >                     (input ([type "submit"]))))))]
> > >
> > >          (send/suspend/dispatch response-generator)))
> > >
> > > ----------------------------------------------------------------------
> > >
> > > It seems like this would be a very common pattern... what is the
> "normal"
> > > way to do it?
> > >
> > > Thanks,
> > > Jordan
> > > _________________________________________________
> > >  For list-related administrative tasks:
> > >  http://lists.racket-lang.org/listinfo/users
> > >
> >
> >
> >
> > --
> > Jay McCarthy <jay at cs.byu.edu>
> > Assistant Professor / Brigham Young University
> > http://faculty.cs.byu.edu/~jay
> >
> > "The glory of God is Intelligence" - D&C 93
>



-- 
Jay McCarthy <jay at cs.byu.edu>
Assistant Professor / Brigham Young University
http://faculty.cs.byu.edu/~jay

"The glory of God is Intelligence" - D&C 93
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20111121/6ef775ff/attachment.html>

Posted on the users mailing list.