[racket] Understanding contracts

From: Alexander McLin (alex.mclin at gmail.com)
Date: Fri Oct 3 22:55:38 EDT 2014

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/9bfe4d71/attachment.html>

Posted on the users mailing list.