[racket] Understanding contracts

From: Matthias Felleisen (matthias at ccs.neu.edu)
Date: Sat Oct 4 14:54:31 EDT 2014

You're right in that the error should be shown in context. We'll think about it, and thanks for bring it up -- Matthias



On Oct 3, 2014, at 11:00 PM, Alexander McLin wrote:

> Making a correction here, my wish error message should say this.
> 
>  in: the 1st argument of
>        (->*
>         ((and/c number? (between/c 0 1)))
>         (#:clamp-range boolean?)
>         number?)
> 
> Because I understand that the phrase "the range of" means the last item in the -> form which is the ->* contract.
> 
> On Fri, Oct 3, 2014 at 10:55 PM, Alexander McLin <alex.mclin at gmail.com> wrote:
> Actually I wasn't quite ready to move on.
> 
> When I apply (curve 3) where (define curve (cubic-bezier a b) a and b are bezier-control-point?
> 
> I get the following contract error:
> 
> ---------------------------------------------------------------------------
> (curve 3)
> . . cubic-bezier: contract violation
>   expected: (and/c number? (between/c 0 1))
>   given: 3
>   which isn't: (between/c 0 1)
>   in: the 1st argument of
>       the range of
>       (->
>        bezier-control-point?
>        bezier-control-point?
>        (->*
>         ((and/c number? (between/c 0 1)))
>         (#:clamp-range boolean?)
>         number?))
>   contract from: 
>       /home/alexander/Workspace/Racket/cubic-bezier.rkt
>   blaming: /home/alexander/Workspace/Racket/experimental.rkt
>   at: /home/alexander/Workspace/Racket/cubic-bezier.rkt:8.11
> 
> ----------------------------------------------------------------------------
> 
> Is there a way I can fine-tune the error message so instead of saying
> 
>  in: the 1st argument of
>       the range of
>       (->
>        bezier-control-point?
>        bezier-control-point?
>        (->*
>         ((and/c number? (between/c 0 1)))
>         (#:clamp-range boolean?)
>         number?))
> 
> It says
> 
>  in: the 1st argument of
>       the range of
>        (->*
>         ((and/c number? (between/c 0 1)))
>         (#:clamp-range boolean?)
>         number?)
> 
> The above contract is really what is coming into play here when using curve, the surrounding -> form is a distraction and I stare at it for a few moments mentally parsing to find the right part that is being violated. The -> contract is only relevant to cubic-bezier and I think confusing because the lambda that is returned from cubic-bezier may be called after some time has passed and — at least for me — cubic-bezier is long since gone, and thus out of mind.
> 
> Alexander McLin
> 
> 
> 
> On Fri, Oct 3, 2014 at 9:59 PM, Alexander McLin <alex.mclin at gmail.com> wrote:
> Good evening Mathias,
> 
> After studying your program and mine, and running various permutations, I understand where I was going wrong. It appears that while doing manual experimentation I had gotten confused to which module I was actually in the REPL when calling various functions by hand. Also my test cases weren't fine grained enough to detect each unique contract violation generated by suitable bad inputs.
> 
> Thanks Mathias! Your examples have helped to sharpen my understanding of how contract boundaries work.
> 
> Alex
> 
> On Thu, Oct 2, 2014 at 2:36 PM, Matthias Felleisen <matthias at ccs.neu.edu> wrote:
> 
> If you use this as the program:
> 
> #lang racket
> 
> ;; contracts set up boundaries between two regions of a program, say two modules
> 
> ;; ---------------------------------------------------------------------------------------------------
> ;; the library
> (provide (contract-out
>            [struct bezier-control-point ((x (and/c number? (between/c 0 1))) (y number?))]
>            [cubic-bezier (-> bezier-control-point?
>                              bezier-control-point?
>                              (->* ((and/c number? (between/c 0 1)))
>                                   (#:clamp-range boolean?)
>                                   number?))]))
> 
> (struct bezier-control-point (x y) #:transparent)
> 
> (define (cubic-bezier a b)
>   ;; now produce a curve via Bezier triangulation
>   (lambda (x #:clamp-range [cr #f])
>     x))
> 
> ;; ---------------------------------------------------------------------------------------------------
> ;; the module that uses it
> (module+ test
>  (require (submod ".."))
> 
>  (define a (bezier-control-point 0.1 5.0))
>  (define b (bezier-control-point 0.3 9.0))
> 
>  ((cubic-bezier a b)
>   ;; a contract violation because i isn't comparable
>   (sqrt -1)))
> 
> 
> drracket or raco test file.rkt will show contract errors.
> 
> 
> 
> 
> On Oct 2, 2014, at 1:50 PM, Alexander McLin <alex.mclin at gmail.com> wrote:
> 
> > Spencer, I'm calling cubic-bezier from within a (module+ test (require submod "..")...) form that itself is defined in the file where cubic-bezier is defined. Also I tried from within REPL and requiring the code file.
> >
> > Matthias,
> >
> > I ran your example and it works exactly the way I wanted. The lambda returned from cubic-bezier properly raises contract violations when bad inputs are given for the required parameter and optional keyword.
> >
> > Examining the differences between your example and my original implementation, my functions are directly defined in top level in the file, after #lang racket, not wrapped within a module. And I'm running my test cases within a (module+ test (require submod "..")) form.
> >
> > In my test cases within (module+ test...), violations are correctly reported when I give bad inputs to cubic-bezier and bezier-control-point structs. Except the lambda, no violations are reported with bad inputs. Is it because I'm incorrectly using modules? I had thought that module+ and (require submod "..") would allow the contract system to come into full force.
> >
> >
> >
> > On Thu, Oct 2, 2014 at 9:20 AM, Matthias Felleisen <matthias at ccs.neu.edu> wrote:
> >
> > Let's make Spencer's question concrete. Say we have this situation:
> >
> > #lang racket
> >
> > ;; contracts set up boundaries between two regions of a program, say two modules
> >
> > ;; ---------------------------------------------------------------------------------------------------
> > ;; the library
> > (module server racket
> >   (provide (contract-out
> >             [struct bezier-control-point ((x (and/c number? (between/c 0 1))) (y number?))]
> >             [cubic-bezier (-> bezier-control-point?
> >                               bezier-control-point?
> >                               (->* ((and/c number? (between/c 0 1)))
> >                                    (#:clamp-range boolean?)
> >                                    number?))]))
> >
> >   (struct bezier-control-point (x y) #:transparent)
> >
> >   (define (cubic-bezier a b)
> >     ;; now produce a curve via Bezier triangulation
> >     (lambda (x #:clamp-range [cr #f])
> >       x)))
> >
> > ;; ---------------------------------------------------------------------------------------------------
> > ;; the module that uses it
> > (module client racket
> >   (require (submod ".." server))
> >
> >   (define a (bezier-control-point 0.1 5.0))
> >   (define b (bezier-control-point 0.3 9.0))
> >
> >   ((cubic-bezier a b)
> >    ;; a contract violation because i isn't comparable
> >    (sqrt -1)))
> >
> > ;; ---------------------------------------------------------------------------------------------------
> > ;; run program run
> > (require 'client)
> >
> > I assume you want to see other violations. Can you explain with this example w/o going into Bezier?
> > (I just know enough about Bezier to draw curves.)
> >
> > -- Matthias
> >
> >
> >
> >
> > On Oct 1, 2014, at 10:46 PM, Alexander McLin <alex.mclin at gmail.com> wrote:
> >
> > > Hello,
> > >
> > > I've been working on a sample project to better understand how to use the contract system. I created a simple Bezier curve implementation and am using (provide (contract-out...) to attach contracts to the provided bindings.
> > >
> > > Basically I have a procedure called cubic-bezier that accepts two control point structs used to define the curve. It returns another procedure that actually generates the curve, it accepts an integer parameter that lies on [0, 1] and an optional keyword #:clamp-range. The generator procedure returns a number.
> > >
> > > The control point structure is a simple posn type that accepts X and Y fields where X must be between 0 and 1, and Y is allowed to be any number.
> > >
> > > Here are the contracts I defined provisionally:
> > >
> > > (provide (contract-out
> > >           [struct bezier-control-point ((x (and/c number? (between/c 0 1)))
> > >                                                       (y number?))]
> > >           [cubic-bezier (-> bezier-control-point?
> > >                                      bezier-control-point?
> > >                                     (->* ((and/c number? (between/c 0 1)))
> > >                                            (#:clamp-range boolean?)
> > >                                            number?))]))
> > >
> > > For the contract attached to cubic-bezier using ->, my thinking was to use ->* to generate the contract for the procedure returned by cubic-bezier but it's not working, meaning I'm not getting the any contract violations I'm expecting when giving bad inputs to cubic-bezier's value.
> > >
> > > How can I attach a more complex contract to cubic-bezier's value?
> > >
> > > Thank you
> > > Alexander McLin
> > > ____________________
> > >  Racket Users list:
> > >  http://lists.racket-lang.org/users
> >
> >
> 
> 
> 
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20141004/d38bf35f/attachment-0001.html>

Posted on the users mailing list.