[racket] Problem with response/output
>
> 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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20150306/de6aa0c0/attachment-0001.html>