[plt-scheme] define/contract/provide ?

From: Greg Hendershott (greghendershott at gmail.com)
Date: Mon Mar 22 14:51:44 EDT 2010

Thank you. There's a bit more to this than I realized. I just need to
digest it. This is really helpful.

On Sun, Mar 21, 2010 at 6:55 PM, Matthias Felleisen
<matthias at ccs.neu.edu> wrote:
>
> Greg, while Matthew may think that his macro works, if you actually run it, you will see that it doesn't. I put the entire scenario into one buffer, like this:
>
>
>> #lang scheme/load
>>
>> (module a scheme
>>
>>   (define-syntax-rule (define/contract/provide (id args ...) (contract ...)
>>                         body-expr ...)
>>     (begin
>>       (define/contract (id args ...) (contract ...) body-expr ...)
>>       (provide id)))
>>
>>   (define/contract/provide (f x) (number? . -> . number?)
>>     x))
>>
>> (module z scheme
>>   (require 'a)
>>
>>   (f "10") ; => contract failure, as expected
>>   )
>>
>> (require 'z)
>
>
> The last require triggers a contract failure, as promised. If you read it carefully, however, you see that it misses the point:
>
>> Welcome to DrScheme, version 4.2.5.1-svn18mar2010 [3m].
>> Language: scheme/load.
>> . . a:12.28: 'a broke the contract (-> number? number?) on f; expected <number?>, given: "10"
>
>
> Instead of blaming module z for the contract violation -- and it obviously does violate the contract by applying f to a string instead of a number -- the macro blames module a.
>
> Why? The define/contract form establishes a contract boundary between the region between "(" and ")" and the rest of the module. If the module decides to hand out the function w/o further protection -- via a contract at the module boundary -- it assumes responsibility for all further uses of f. Since module z abuses f then -- not knowing from the module interface that f should be applied to numbers -- the contract system correctly blames a. The reasoning is correct though misleading because you can't figure out from the error message who called f with the wrong value.
>
> As you can easily see, for any n one could construct a chain of n+1 modules where the last one abuses f and yet a is blamed.
>
> If you really wish to protect the function with respect to the module and the region, try this macro instead:
>
>> #lang scheme/load
>>
>> (module a scheme
>>
>>   (define-syntax-rule
>>     (define/contract/provide (id args ...) contract body-expr ...)
>>     (begin
>>       (define/contract (id args ...) contract body-expr ...)
>>       (provide/contract [id contract])))
>>
>>   (define/contract/provide (f x) (number? . -> . number?)
>>     x))
>>
>> (module z scheme
>>   (require 'a)
>>
>>   (f "10") ; => contract failure, as expected -- with correct blame
>>   )
>>
>> (require 'z)
>
>
>
> The other messages explain why we chose to go with this concept of contract over others. -- Matthias
>
>


Posted on the users mailing list.