[racket] contracts on liberal function inputs: avoiding duplicate effort
>
> Of course, these are not really contracts as we understand them. The docs
> for 'make-contract' say not to do this, if you read them carefully. So
> beware. I'm kind of curious what other people think of this application of
> the contract system, actually.
Why is it an abuse of the contract system to define a contract that uses a
projection to change the value?
The docs for make-contract say "The projection must either produce the
value, *suitably wrapped to enforce any higher-order aspects of the
contract*, or signal a contract violation." Is it never true that changing
a value can be the "suitable wrapping"? (Or m I overlooking some other
relevant part of the docs?)
Consider this contract. It checks whether a value can be converted to a
path. If so, it returns the converted value. If not, it fails. (This one's
in quasicode, but I wrote a real one and it works as expected.) If this is
abuse, it's very useful abuse.
(define coerce/path?
(make-contract
#:name 'coerce/path?
#:projection (λ (b)
(λ (x)
(if (can-be-path? x)
(convert-to-path x)
(raise-blame-error
b x
'(expected: "~a" given: "~e")
'can-be-path? x))))))
On Mon, Nov 18, 2013 at 10:25 AM, Ryan Culpepper <ryanc at ccs.neu.edu> wrote:
> On 11/18/2013 12:03 PM, Matthew Butterick wrote:
>
>> In a function that permits liberal inputs, I often find that the input
>> processing I do in a contract is duplicated at the beginning of the body of
>> the function. Is this avoidable?
>>
>> Certain functions want to be liberal with input because there are
>> multiple common ways to represent the data. For instance, I have a function
>> that operates on CSS RGB colors. This function should be prepared to accept
>> these forms of input & understand that they're all the same:
>>
>> "#c00"
>> "#cc0000"
>> '("204" "0" "0")
>> #xcc0000
>> '(0.8 0 0)
>>
>> Let's say that my internal representation of an RGB color is described by
>> the contract rgb-color/c:
>>
>> (define rgb-color/c (list/c (real-in 0 1) (real-in 0 1) (real-in 0 1)))
>>
>> But I can't use rgb-color/c as the input contract for the function
>> because it's too narrow. So I make a second contract that tests for things
>> that can be converted to an rgb-color:
>>
>> (define (rgb-colorish? x) (or/c rgb-color/c [tests for the other input
>> formats ...] )
>>
>> To determine if the input is rgb-colorish?, this contract usually just
>> ends up trying to convert the input to rgb-color. If it works, then the
>> contract returns true.
>>
>> But after the contract returns, I have to convert the input to an
>> rgb-color anyhow. So I'm doing exactly the same work that the contract just
>> finished. If the conversion is expensive, I'm doing it twice.
>>
>
> We usually just don't worry about the conversion happening twice. Or we do
> what Matthias said. Or we apply a fast approximate contract (or none at
> all) and then do the conversion and error checking together inside the
> function.
>
> But... by abusing the contract system a little bit, though, we can get it
> to do the conversion for you.
>
> Here's a "contract" combinator that takes a conversion function that
> returns #f to indicate failure/rejection and any other value to represent
> the converted result.
>
> > (define (make-named-conversion-contract name convert)
> (define ((proj blame) v)
> (cond [(convert v )
> => values]
> [else
> (raise-blame-error blame v
> '(expected: "~a" given: "~e")
> name v)]))
> (make-contract #:name name #:projection proj))
>
> Here's a "contract" that checks that a value is real, and if so produces
> its absolute value.
>
> > (define abs/c
> (make-named-conversion-contract
> 'abs/c
> (lambda (v) (and (real? v) (abs v)))))
>
> And here's a function using the converting "contract":
>
> > (define/contract f (-> abs/c real?)
> (lambda (x) x))
> > (f 10)
> 10
> > (f -12) ;; <-- !!!
> 12
> > (f 'hello)
> f: contract violation
> expected: abs/c
> given: 'hello
> ....
>
> Of course, these are not really contracts as we understand them. The docs
> for 'make-contract' say not to do this, if you read them carefully. So
> beware. I'm kind of curious what other people think of this application of
> the contract system, actually.
>
> Ryan
>
>
> ____________________
> 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/20140215/fd9d4620/attachment-0001.html>