[plt-scheme] define/contract/provide ?
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