The semi-expanded code should probably be replaced with the real thing (if possible) It might help -- Matthias



On Feb 15, 2009, at 6:59 PM, Sam TH wrote:

I agree, but do you think that `lambda' should provide a better error
message for all the cases here, or that `with-contract' should handle
this case specially?  Or are you just objecting to the semi-expanded
code in the output there?

sam th

On Sun, Feb 15, 2009 at 6:51 PM, Matthias Felleisen
<[email protected]> wrote:

I am sorry, but

Welcome to DrScheme, version 4.1.4.3-svn13feb2009 [3m].
Language: Module; memory limit: 128 megabytes.
. begin (possibly implicit): no expression after a sequence of internal definitions in: ((define-syntax current-contract-region11 (convert-renamer
(λ (stx) (syntax (quote (region f1)))))) (expand-ssp-body
(current-contract-region1) (current-contract-region11) (with- contract-helper #<procedure:syntax-introducer> (quote (region f1)) ((g number?)) () (define
g 10))))


is indefensible.


On Feb 15, 2009, at 2:57 PM, Sam TH wrote:

Note that you get the same error message if you do this:

(define (f x)

 (define y x))
. begin (possibly implicit): no expression after a sequence of
internal definitions in: ((define y x))

Do you think `with-contract' should give a special error message here,
or that the error message in general should be improved?

sam th

On Sun, Feb 15, 2009 at 2:24 PM, Matthias Felleisen
<[email protected]> wrote:

Could we improve the error message for people who attempt to nest regions

#lang scheme

(with-contract f1
             ((y number?))
             (with-contract f2
                            ((x boolean?))
                            (define x #t)
                            (define y 1)))


or

#lang scheme

(define (f x)
 (with-contract
 f1
 ((y number?))
 (define y x)))

(f 10)

Thanks. -- Matthias



On Feb 14, 2009, at 11:24 PM, Stevie Strickland wrote:

In SVN I've added three new major features that involve contracts.
One allows for more fine-grained control of contracts, and the other
two allow for the use of contracts with signatures and units.

Contract Regions
----------------

Contract regions allow the programmer to protect a region of code
with a contract boundary.  In addition to the wrapped code, the
programmer also provides a name for the region which is used in
blame situations and a list of exported variables which can
either be protected with contracts or unprotected.  The region
provides a true contract boundary, in that uses of contracted
exports within the region are unprotected.  Contract regions are
specified with the with-contract form.  The following contract
region defines two mutually recursive functions:

 (with-contract region1
  ([f (-> number? boolean?)]
   [g (-> number? boolean?)])
  (define (f n) (if (zero? n) #f (g (sub1 n))))
  (define (g n) (if (zero? n) #t (f (sub1 n)))))

The internal calls to f and g are uncontracted, but calls to f
and g outside this region would be appropriately contracted.
First-order checks are performed at the region, so the
following region:

 (with-contract region2
  ([n number?])
  (define n #t))

results in the following error:

 (region region2) broke the contract number? on n;
  expected <number?>, given: #t

Notice that the blame not only gives the name of the region, but
describes what type of contract boundary was involved.

For contracting a single definition, there is the define/contract
form which has a similar syntax to define, except that it takes a
contract before the body of the definition.  To compare the two
forms, the following two definitions are equivalent:

(with-contract fact
 ([fact (-> number? number?)])
 (define (fact n)
  (if (zero? n) 1 (* n (fact (sub1 n))))))

(define/contract (fact n)
 (-> number? number?)
 (if (zero? n) 1 (* n (fact (sub1 n)))))

First order checks are similarly performed at the definition for
define/contract, so

 (define/contract (fact n)
  (-> number?)
  (if (zero? n) 1 (* n (fact (sub1 n)))))

results in

 (function fact) broke the contract (-> number?) on fact;
   expected a procedure that accepts no arguments without
   any keywords, given: #<procedure:fact>

Signature Contracts
-------------------

In addition to contract regions, units are also now contract
boundaries.  One way to use contracts with units is to add
contracts to unit signatures via the contracted signature form.

 (define-signature toy-factory^
  ((contracted
    [build-toys (-> integer? (listof toy?))]
    [repaint    (-> toy? symbol? toy?)]
    [toy?       (-> any/c boolean?)]
    [toy-color  (-> toy? symbol?)])))

Notice that contracts in a signature can use variables listed
in the signature.

Now if we take the following implementation of that signature:

 (define-unit simple-factory@
  (import)
  (export toy-factory^)

  (define-struct toy (color) #:transparent)

  (define (build-toys n)
    (for/list ([i (in-range n)])
      (make-toy 'blue)))

  (define (repaint t col)
    (make-toy col)))

We get the appropriate contract checks on those exports:

(define-values/invoke-unit/infer simple-factory@)
(build-toys 3)

 (#(struct:toy blue) #(struct:toy blue) #(struct:toy blue))

(build-toys #f)

 top-level broke the contract (-> integer? (listof toy?))
  on build-toys; expected <integer?>, given: #f

As before, uses of contracted exports inside the unit are
not checked.

Since units are contract boundaries, they can be blamed
appropriately.  Take the following definitions:

 (define-unit factory-user@
  (import toy-factory^)
  (export)
  (let ([toys (build-toys 3)])
    (repaint 3 'blue)))

 (define-compound-unit/infer factory+user@
  (import) (export)
  (link simple-factory@ factory-user@))

When we invoke the combined unit:

(define-values/invoke-unit/infer factory+user@)

 (unit factory-user@) broke the contract
  (-> toy? symbol? toy?)
 on repaint; expected <toy?>, given: 3

Unit Contracts
--------------

However, we may not always be able to add contracts to
signatures.  For example, there are many already-existing
signatures in PLT Scheme that one may want to implement, or a
programmer may want to take a unit value and add contracts to it
after the fact.

To do this, there is the unit/c contract combinator. It takes a list of imports and exports, where each signature is paired with a list of variables and their contracts for each signature. So if we had the
uncontracted version of the toy-factory^ signature:

 (define-signature toy-factory^
  (build-toys repaint toy? toy-color))

the following contracts would be appropriate for a unit that imports
nothing and exports that signature:

 (unit/c (import) (export))
 (unit/c (import) (export toy-factory^))
 (unit/c
 (import)
 (export (toy-factory^
          [toy-color (-> toy? symbol?)])))
 (unit/c
 (import)
 (export (toy-factory^
          [build-toys (-> integer? (listof toy?))]
          [repaint    (-> toy? symbol? toy?)]
          [toy?       (-> any/c boolean?)]
          [toy-color  (-> toy? symbol?)])))

Unit contracts can contain a superset of the import signatures and a
subset of the export signatures for a given unit value.  Also,
variables that are not listed for a given signature are left alone
when the contracts are being added.

Since the results of applying unit/c is a new unit, then adding
a contract can cause link inference to fail.  For example, if we
change the definition of simple-factory@ above to

 (define/contract simple-factory@
  (unit/c
   (import)
   (export (toy-factory^
            [build-toys (-> integer? (listof toy?))]
            [repaint    (-> toy? symbol? toy?)]
            [toy?       (-> any/c boolean?)]
            [toy-color  (-> toy? symbol?)])))
  (unit
    (import)
    (export toy-factory^)

    (define-struct toy (color) #:transparent)

    (define (build-toys n)
      (for/list ([i (in-range n)])
        (make-toy 'blue)))

    (define (repaint t col)
      (make-toy col))))

Then when we try to combine it with the factory-user@ unit, we
get:

 define-compound-unit/infer: not a unit definition
  in: simple-factory@

One way to solve this is to use define-unit-binding to set up the
static information for the new contracted value. Another possibility
for unit definitions is to use define-unit/contract:

 (define-unit/contract simple-factory@
  (import)
  (export (toy-factory^
           [build-toys (-> integer? (listof toy?))]
           [repaint    (-> toy? symbol? toy?)]
           [toy?       (-> any/c boolean?)]
           [toy-color  (-> toy? symbol?)]))

  (define-struct toy (color) #:transparent)

  (define (build-toys n)
    (for/list ([i (in-range n)])
      (make-toy 'blue)))

  (define (repaint t col)
    (make-toy col)))

More about these features can be found in the Reference, and a short section about signature and unit contracts has been added to the Guide.

Stevie
_________________________________________________
 For list-related administrative tasks:
 http://list.cs.brown.edu/mailman/listinfo/plt-scheme

_________________________________________________
 For list-related administrative tasks:
 http://list.cs.brown.edu/mailman/listinfo/plt-dev




--
sam th
[email protected]

_________________________________________________
 For list-related administrative tasks:
 http://list.cs.brown.edu/mailman/listinfo/plt-dev




--
sam th
[email protected]

_________________________________________________
 For list-related administrative tasks:
 http://list.cs.brown.edu/mailman/listinfo/plt-dev

Reply via email to