[racket] Problem with response/output

From: Alexander D. Knauth (alexander at knauth.org)
Date: Fri Mar 6 20:44:10 EST 2015

For that the contract says
(output-port? . -> . void)
When it should say
(output-port? . -> . void?)


On Mar 6, 2015, at 8:16 PM, Matthew Butterick <mb at mbtype.com> wrote:

> Curiously, the contract for `response` also requires an (-> output-port? void?) procedure [1]. The source code agrees. But the examples given in the docs don't include a (void) return value. Yet they do work. So perhaps there's a tiny bug where `response` is being more lenient than it's supposed to be.
> 
> 
> [1] http://docs.racket-lang.org/web-server/http.html?q=response#%28def._%28%28lib._web-server%2Fhttp%2Fresponse-structs..rkt%29._response%29%29
> 
> 
> On Fri, Mar 6, 2015 at 4:40 PM, Alexis King <lexi.lambda at gmail.com> wrote:
> 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

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

Posted on the users mailing list.