[racket] Problem with response/output

From: Jay McCarthy (jay.mccarthy at gmail.com)
Date: Sat Mar 7 13:23:15 EST 2015

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


Posted on the users mailing list.