[plt-scheme] Questions about (contract ...) form
Overall, I suspect the problem is that we don't have a good story for
contracts+macros. Sounds like a nice research topic to me!
To avoid the problem and make progress, just stick a dynamic check into
the expansion of the stream-cons macro and move on.
Robby
At Sun, 6 Nov 2005 16:04:43 -0500, Richard Cobbe wrote:
> On Sun, Nov 06, 2005 at 02:32:35PM -0600, Robby Findler wrote:
> > At a high level `contract' is used as the definition of some kind of
> > boundary between "things". In the case of provide/contract (which
> > expands to contract), the things are modules. In the case of
> > define/contract, the things are definitions (but define doesn't work as
> > well as a module leading to some strange things with define/contract
> > sometimes ... but that's neither here nor there). So the first question
> > to answer is what the "things" are.
>
> In this case, they're modules. I could have made this clearer by
> describing more of the context. Here's what I've got currently:
>
> (module ministream mzscheme
>
> (require (lib "contract.ss"))
>
> (define-struct stream ())
> (define-struct (stream:empty stream) ())
> (define-struct (stream:cell stream) (car cdr))
>
> (define-syntax stream-cons
> (syntax-rules ()
> [(_ a d)
> (make-stream:cell a (delay d))]))
>
> (define stream-car
> (lambda (str)
> (if (stream:empty? str)
> (raise (make-exn:fail:contract
> "can't take car of empty stream"
> (current-continuation-marks)))
> (stream:cell-car str))))
>
> (define stream-cdr
> (lambda (str)
> (if (stream:empty? str)
> (raise (make-exn:fail:contract
> "can't take cdr of empty stream"
> (current-continuation-marks)))
> (force (stream:cell-cdr str)))))
>
> (define stream-null (make-stream:empty))
> (define stream-null? stream:empty?)
>
> (provide stream-cons)
> (provide/contract [stream? (-> any/c boolean?)]
> [stream-car (-> stream? any)]
> [stream-cdr (-> stream? stream?)]
> [stream-null stream?]
> [stream-null? (-> stream? boolean?)]))
>
> But I've discovered that blame doesn't get assigned the way I think it
> should. If some other module does the following:
>
> (stream-cdr (stream-cons 3 4))
>
> then ministream gets blamed for violating stream-cdr's contract. That's
> what I've written, of course, but that's not what I want --- it's not
> ministream's fault, it's the client's fault for constructing a bogus
> stream in the first place. Ideally, the contract system would blame the
> client for violating stream-cons's contract; I just can't figure out how
> to write that contract.
>
> Basically, I'm trying to figure out how to do this
>
> (provide/contract [stream-cons (-> any/c stream? stream?)])
>
> given that stream-cons has to be a macro. (Actually, I don't really
> care about the contract on stream-cons's return value; I'm confident
> that I got that right. <grin>)
>
> > > b) Is negative-blame meaningful when the value in question is not a
> > > function?
> >
> > Yes. The contract language is set up so that each contract should be
> > between two parties (this also works for N parties, when you think of
> > N+1 pairwise contracts with some common one "thing" that connects them
> > together -- if you need something like that directly we can talk more
> > about it.). One party is responsible the value initially and things
> > that flow in the first direction (positive) and the other is
> > responsible for values that flow back into the original place. So, in
> > the case of functions, positive is for the function itself (it's arity)
> > and for results of the function. Negative is for arguments to the
> > function (or results of argument functions, etc).
>
> OK. I'm not quite sure how this applies to my situation. I know one of
> the two symbols will be 'ministream and the other will be the client
> module, but I'm not sure which is which. Based on what you say above,
> it sounds like the client module should be the negative blame, because
> it's (kind of) a function argument, but I'm not at all sure about that.
>
> More generally, I don't completely understand the forward and backward
> directions. There's an obvious relation to positive and negative
> positions here, but it looks like it's more complex than that.
>
> > But there are other kinds of connections between "things" (beyond
> > functions) that allow backwards flow.
> >
> > > c) If these symbols are supposed to be module names, how do I get the
> > > name of the module which contains the use of stream-cons? I've
> > > seen syntax-source-module, but that can potentially return a module
> > > path index, and it's not clear how to extract the module name from
> > > that.
> >
> > See module-source-as-symbol in mzlib/private/contract-helpers for what
> > provide/contract does.
>
> Thanks for the pointer; I'll check that out.
>
> > > Second, the manual says that "contract-source, if specified, indicates
> > > where the contract was assumed." What does it mean to assume a
> > > contract?
> >
> > Assume as in "to take it on", not assume as in "prove an implication".
>
> I'd sort of figured that was what you meant, but it's still not clear to
> me what it means to "take on" a contract. Wild guess: is this a way of
> making it appear as though the contract was applied to the value in one
> module even though the actual contract form appears somewhere else? For
> instance, I'd guess that provide/contract uses that to make the contract
> look as though it comes from the use of provide/contract, not its
> definition. If that's the case, then, I could just use the default, as
> I'd like the contract to appear as though it comes from ministream.
>
> > > Or is there a better way to do this altogether?
> >
> > I guess provide/contract doesn't work for you because you want to
> > export a macro?
>
> Yup.
>
> Richard
> _________________________________________________
> For list-related administrative tasks:
> http://list.cs.brown.edu/mailman/listinfo/plt-scheme