[plt-scheme] Why not simpler contracts?

From: Robby Findler (robby at cs.uchicago.edu)
Date: Wed Aug 8 02:00:16 EDT 2007

On 8/8/07, Henk Boom <lunarc.lists at gmail.com> wrote:
> Hi, I'd been using the contracts.ss library for a while. I don't mean
> any offense, but I find them almost completely unreadable =(. I know
> that obviously a lot of effort has gone into this library, but why was
> this particular syntax decided on?

The main advantage of the notation you're proposing below seems to be
that you can use the formal parameter and the special identifier
`result' when defining the contract on a function.

I did experiment with this kind of a notation, but ultimately decided
it didn't work for two reasons. First, it does not generalize
consistently to higher-order functions and I don't want the contract
library to bias people towards first-order programming. Second, I
eventually plan to move the contracts on a module into a separate
place, separated from the module itself, in order to get better reuse
of contracts and to make it easier for programmers that just want to
read a spec of some module, rather than having to read the entire
source code.

There is one other difference I notice: you are recording the source
code of the contract, but the contract library does not do that.
Instead, it rebuilds a name for the contract based on the combinators
used to build the contract. This mostly works well, but falls down
when you are using dependent contracts (as in the case of sqrt). So,
instead of trying to reproduce the entire code, the contract library
just records the source location of the reference to the variable that
had the contract and expects you to be able to find the contract's
text from there (presumably using check syntax). This should probably
send you directly to the contract, however. I'll think about that.

All that said, I don't think that the code is "almost completely
unreadable" in the particular case you give. Here's the way I would
write it:

(module m mzscheme
  (require (lib "contract.ss"))

  (define ((sqrt-post-cond x) result)
    (or (and (< x 1) (> result x))
        (and (> x 1) (< result x))))

  (provide/contract
   [my-sqrt
    (->d (and/c real? positive?)
         sqrt-post-cond)])

  (define my-sqrt sqrt))

and here are the error messages:

  21:50: top-level broke the contract
    (->d (and/c real? positive?) ...)
  on my-sqrt; expected <(and/c real? positive?)>, given: #f

  21:50: top-level broke the contract
    (->d (and/c real? positive?) ...)
  on my-sqrt; expected <(and/c real? positive?)>, given: -1

  21:50: m broke the contract
    (->d (and/c real? positive?) ...)
  on my-sqrt; expected <???>, given: 1

Of course, feel free to write your own contract library -- planet
makes it easy for you to distribute it to others and maybe we'll be
able to get good ideas from each others work.

Robby


Posted on the users mailing list.