[racket] Problem with response/output

From: Matthew Butterick (mb at mbtype.com)
Date: Sat Mar 7 15:31:05 EST 2015

Ah yes, please forgive my error. I was wrong when I said "the contract for
`response` also requires an (-> output-port? void?) procedure". I misread
it.

The moral of the story, however, remains the same — contracts are not final
because they're infallible; they're infallible because they're final.

On Sat, Mar 7, 2015 at 10:23 AM, Jay McCarthy <jay.mccarthy at gmail.com>
wrote:

> Oh and one more thing. The idea of the void? was to help catch
> mistakes where someone thought they could return a xexpr or response
> structure from these functions and have it returned.
>
> Jay
>
> On Sat, Mar 7, 2015 at 1:22 PM, Jay McCarthy <jay.mccarthy at gmail.com>
> wrote:
> > I'm catching up on this thread.
> >
> > The contracts should all be "void?" and it says "void" then it's
> > probably a typo and an error. "void" is a function that takes any
> > number of arguments and returns "void", which is not false, so "void"
> > is like an "any/c" contract, except that it is less efficient. (The
> > contract system will not put the same kind of wrapper when the
> > function can return anything.)
> >
> > I'm loathe to fix this typo though, because I suspect that a lot of
> > code relies on the "void" behavior. If anything, I should change it to
> > have "any" so that it is more efficient and has the same errors.
> >
> > Jay
> >
> >
> > On Fri, Mar 6, 2015 at 9:09 PM, André Matheus <amatheus at mac.com> wrote:
> >> If I understood what Matthew said correctly, the point of the contract
> >> requiring void is to prevent
> >> an error when someone uses a function returning a string rather than
> writing
> >> to the output port.
> >>
> >> But in this case, response should require returning void too, right?
> What
> >> confused me more than
> >> the error message and the contracts was that the same function worked
> when
> >> creating a response
> >> but didn’t work when using response/output, but response/output simply
> >> creates a response with
> >> default values, so my understanding was that the function should either
> work
> >> or fail on both.
> >>
> >> Em 06/03/2015, à(s) 21:40, Alexis King <lexi.lambda at gmail.com>
> escreveu:
> >>
> >> I do sort of agree that the void? requirement is strange and unneeded.
> It’s
> >> usually only used to indicate that a function provided by some module
> >> returns #<void>, but callback functions are usually specified with any
> as
> >> the return value to allow precisely this sort of thing.
> >>
> >> On Mar 6, 2015, at 16:26, Matthew Butterick <mb at mbtype.com> wrote:
> >>
> >>> From the error message, I changed the lambda to return (void) and then
> it
> >>> worked.
> >>> I think maybe the contract is wrong but frankly I don't understand much
> >>> about contracts.
> >>
> >>
> >> The contract, by definition, is always right ;)
> >>
> >> In this case, `response/output` takes as its first argument a procedure
> that
> >> accepts an output-port and returns void. [1] In contract-speak this is
> >> commonly written with dot notation as:
> >>
> >> (output-port? . -> . void?)
> >>
> >> but it appears in error messages, like the one above, with the
> equivalent
> >> notation:
> >>
> >> (-> output-port? void?)
> >>
> >> Note also that the error says the contract violation was "in the range
> of
> >> the 1st argument of" ... [giant contract follows]. That helps track
> down the
> >> error. The contract for the first argument is '(-> output-port? void?)'.
> >> This is a procedure contract. And "the range of" a procedure contract
> means
> >> the contract on the return value. Which in this case is `void?`. So the
> >> complaint is that the procedure given in the first argument is returning
> >> '11' when it should be returning void.
> >>
> >> Why is (-> output-port? void?) the contract for the first arg of
> >> `response/output`? The idea is that you write to the output-port
> directly
> >> rather than returning a value to the caller. Insisting on void as a
> return
> >> value imposes an extra measure of discipline, and sets an expectation.
> >>
> >> The reason your 'not-working' dispatcher is not working is that
> >> `write-bytes` does two things: it sends bytes to the output port, but
> then
> >> also returns the number of bytes written. [2] So this procedure:
> >>
> >> (λ (op) (write-bytes #"Hello world" op))
> >>
> >> is defective because it returns the number of bytes. Meaning, it breaks
> the
> >> contract, which demands void. (That's also why your error is '11':
> that's
> >> the number of bytes in "Hello world").
> >>
> >> But your revised procedure:
> >>
> >> (λ (op) (write-bytes #"Hello world" op) (void))
> >>
> >> Meets the contract because it ignores the return value from
> `write-bytes`
> >> and returns (void) instead.
> >>
> >> You should repeat this technique whenever you use `response/output`.
> >>
> >> You can also look into `response/full` and `response/xexpr`, which can
> be a
> >> more convenient way of making simple HTML or text responses.
> >>
> >>
> >>
> >> [1]
> >>
> http://docs.racket-lang.org/web-server/http.html?q=response%2Foutput#%28def._%28%28lib._web-server%2Fhttp%2Fresponse-structs..rkt%29._response%2Foutput%29%29
> >>
> >> which cross-references
> >>
> >>
> http://docs.racket-lang.org/web-server/http.html?q=response%2Foutput#%28def._%28%28lib._web-server%2Fhttp%2Fresponse-structs..rkt%29._response%29%29
> >>
> >> [2]
> >>
> http://docs.racket-lang.org/reference/Byte_and_String_Output.html?q=write-bytes#%28def._%28%28quote._~23~25kernel%29._write-bytes%29%29
> >>
> >> which cross-references
> >>
> >>
> http://docs.racket-lang.org/reference/Byte_and_String_Output.html?q=write-bytes#%28def._%28%28quote._~23~25kernel%29._write-string%29%29
> >>
> >>
> >>
> >> On Fri, Mar 6, 2015 at 3:48 PM, André Matheus <amatheus at mac.com> wrote:
> >>>
> >>> Hi, starting a project of mine, I've setup a dispatch rule and a
> function
> >>> to return the response.
> >>> To make things simple, I've used response/output, with a lambda
> writing to
> >>> the output-port.
> >>> However, I've got the error:
> >>>
> >>> response/output: contract violation
> >>>   expected: void?
> >>>   given: 11
> >>>   in: the range of
> >>>       the 1st argument of
> >>>       (->*
> >>>        ((-> output-port? void?))
> >>>        (#:code
> >>>         number?
> >>>         #:headers
> >>>         (listof header?)
> >>>         #:message
> >>>         bytes?
> >>>         #:mime-type
> >>>         (or/c bytes? #f)
> >>>         #:seconds
> >>>         number?)
> >>>        response?)
> >>>   contract from:
> >>>       <pkgs>/web-server-lib/web-server/http/response-structs.rkt
> >>>   blaming: /home/amatheus/Dropbox/focus/todagendas/teste.rkt
> >>>    (assuming the contract is correct)
> >>>   at: <pkgs>/web-server-lib/web-server/http/response-structs.rkt:41.2
> >>>   context...:
> >>>    /usr/share/racket/collects/racket/contract/private/blame.rkt:143:0:
> >>> raise-blame-error16
> >>>
> >>>
> /usr/share/racket/pkgs/web-server-lib/web-server/http/response.rkt:115:12
> >>>
> >>> So I've tried to use a response, with the same lambda, and it worked.
> >>> From the error message, I changed the lambda to return (void) and then
> it
> >>> worked.
> >>>
> >>> I think maybe the contract is wrong but frankly I don't understand much
> >>> about contracts.
> >>>
> >>> I've setup some code that exposes the problem. Navigating to "/working"
> >>> and "/fixed" works
> >>> fine; navigating to "/not-working" exposes the problem.
> >>>
> >>> #lang racket
> >>> (require web-server/dispatch
> >>>          web-server/servlet-env
> >>>          net/url
> >>>          web-server/http/request-structs
> >>>          web-server/http/response-structs)
> >>>
> >>> (define (not-working req)
> >>>   (response/output (λ (op) (write-bytes #"Hello world" op))))
> >>>
> >>> (define (working req)
> >>>   (response
> >>>    301 #"OK"
> >>>    (current-seconds) TEXT/HTML-MIME-TYPE
> >>>    empty
> >>>    (λ (op) (write-bytes #"Hello world" op))))
> >>>
> >>> (define (fixed req)
> >>>   (response/output (λ (op) (write-bytes #"Hello world" op) (void))))
> >>>
> >>> (define (url->request u)
> >>>     (make-request #"GET" (string->url u) empty
> >>>                   (delay empty) #f "1.2.3.4" 80 "4.3.2.1"))
> >>>
> >>> (define-values (agenda-dispatch agenda-url)
> >>>   (dispatch-rules
> >>>    [("working") working]
> >>>    [("not-working")  not-working]
> >>>    [("fixed") fixed]))
> >>>
> >>> (define (main)
> >>>   (serve/servlet agenda-dispatch
> >>>                  #:servlet-regexp #rx""
> >>>                  #:servlet-path ""))
> >>>
> >>> (module+ main
> >>>   (main))
> >>>
> >>> The contracts in response-structs.rkt:
> >>>
> >>> (provide/contract
> >>>  [struct response
> >>>          ([code number?]
> >>>           [message bytes?]
> >>>           [seconds number?]
> >>>           [mime (or/c false/c bytes?)]
> >>>           [headers (listof header?)]
> >>>           [output (output-port? . -> . void)])]
> >>>  [response/full (-> number? bytes? number? (or/c false/c bytes?)
> (listof
> >>> header?) (listof bytes?) response?)]
> >>>  [response/output (->* ((-> output-port? void?))
> >>>                        (#:code number?
> >>>                         #:message bytes?
> >>>                         #:seconds number?
> >>>                         #:mime-type (or/c bytes? #f)
> >>>                         #:headers (listof header?))
> >>>                        response?)]
> >>>  [TEXT/HTML-MIME-TYPE bytes?])
> >>>
> >>> Is the contract wrong or am I doing something weird?
> >>>
> >>> Thanks,
> >>>
> >>> André
> >>>
> >>> ____________________
> >>>   Racket Users list:
> >>>   http://lists.racket-lang.org/users
> >>>
> >>
> >> ____________________
> >>  Racket Users list:
> >>  http://lists.racket-lang.org/users
> >>
> >>
> >>
> >>
> >> ____________________
> >>   Racket Users list:
> >>   http://lists.racket-lang.org/users
> >>
> >
> >
> >
> > --
> > Jay McCarthy
> > http://jeapostrophe.github.io
> >
> >            "Wherefore, be not weary in well-doing,
> >       for ye are laying the foundation of a great work.
> > And out of small things proceedeth that which is great."
> >                           - D&C 64:33
>
>
>
> --
> Jay McCarthy
> http://jeapostrophe.github.io
>
>            "Wherefore, be not weary in well-doing,
>       for ye are laying the foundation of a great work.
> And out of small things proceedeth that which is great."
>                           - D&C 64:33
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20150307/c12aeb38/attachment-0001.html>

Posted on the users mailing list.