[plt-scheme] define/contract/provide ?

From: Matthias Felleisen (matthias at ccs.neu.edu)
Date: Sun Mar 21 18:55:14 EDT 2010

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.