My concept of how (simple) contracts should work was influenced by the
Bertrand Meyer /  Eiffel approach and spent many years of rolling my own
contracts in lesser languages to mimic this.

The main things I found pragmatically useful were:

   1. Being able to specify pre-conditions and post-conditions as simple
   predicates
   2. Being able to selectively turn off contracts (typically
   post-conditions) in well-tested code for performance.

With very simple hand-rolled assertions (plus some design discipline) I
reckon I achieved 80% pf the promise of contracts, with my debugging time
plunging by a factor of 10, and much cleaner designs.

When I finally programmed in Eiffel there were several added benefits of
language support that I appreciated:

   - More concise and clear expression of contracts
   - The implies operator
   - Proper support for invariant-checking
   - Proper support for covariant inheritance
   - Tools for automatically generating documentation
   - Better tools for turning off contracts by configuration

But I was only able to get to use Eiffel in one organisational setting —
quite a big negative!

* * *

Coming across to Racket I was thrilled to see that contracts are a big part
of the system, but they are certainly a bit different to what I was used to!

I'd like to share a sketch of how I would start to hand-roll the kind of
contracts I was used to in Racket.

My questions are:

   - Is it feasible to get similar behaviour from the official contract
   system?, or
   - Do these considerations call for building up an alternative system?


Restating my goals:

   1. Specify pre-conditions and post-conditions as simple predicates
   (boolean expressions)
   2. Selectively turn off contracts (mainly post-conditions) for
   performance in well-tested code.

Example: real square root function, and sketch of support below.

Now, I appreciate that the Racket contract system provides all sorts of
support for higher-level functional programming, but it concerns me that as
a big fan of contracts I've been reluctant to use them consistently in
Racket.

I hope to change that in my second decade of Racket (formerly PLT Scheme)
use, but may need some support and guidance!

Dan



#lang racket

(define pre-conditions-on #t)
(define post-conditions-on #t)

(define (implies a b)
  (or (not a) b))

(define-syntax-rule (pre [test? msg] ...)
  (unless (not pre-conditions-on)
    (unless test?
      (error (~a "Pre-condition violation: " msg))) ...))

(define-syntax-rule (post [test? msg] ...)
  (unless (not post-conditions-on)
    (unless test?
      (error (~a "Post-condition violation: " msg))) ...))

(define (real-sqrt x)
  (pre [(real? x) "real argument expected"]
       [(>= x 0) "non-negative argument expected"])

  (define result (sqrt x))
  ; Change the implementation to 1, 0, (- x) to trigger various
post-condition errors

  (post [(implies (= x 0) (= result 0)) "The sqrt of zero should be zero"]
        [(implies (> x 0) (> result 0)) "Positive numbers have positive
square-roots"]
        [(implies (> x 0) (<= (abs (- x (* result result))) 0.0000001))
         "result * result = x (to within error)"])
  result)


(real-sqrt 0)
(real-sqrt 9)
(real-sqrt -9) ; Pre-condition error

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to