[racket] Problem with response/output

From: André Matheus (amatheus at mac.com)
Date: Fri Mar 6 21:09:11 EST 2015

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 <mailto: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 <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 <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 <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 <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 <mailto: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 <http://lists.racket-lang.org/users>
>> 
>> 
>> ____________________
>>  Racket Users list:
>>  http://lists.racket-lang.org/users <http://lists.racket-lang.org/users>
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20150306/74a398da/attachment-0001.html>

Posted on the users mailing list.