[racket] Understanding contracts

From: Alexander McLin (alex.mclin at gmail.com)
Date: Fri Oct 3 23:00:40 EDT 2014

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/20141003/0f486e25/attachment-0001.html>

Posted on the users mailing list.