[plt-scheme] Questions about (contract ...) form

From: Richard Cobbe (cobbe at ccs.neu.edu)
Date: Sun Nov 6 16:04:43 EST 2005

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


Posted on the users mailing list.