[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: contractual programming



If you aren't interested in this, junk it now.  It's longer than I thought it
would end up being...

/// Well, something like the Common Lisp ASSERT (test some arbitrary
/// predicate and signal a value if it is false) would do part of what you
/// want.  It might want to be part of the base language, just to encourage
/// people to use it, but it would be trivial to add it as a library.  The
/// only problem is that you have to sprinkle these around your program in
/// specific places.
/// 
/// The next step would be to build a table associating a "contract"
/// predicate with each class.  A call to (CHECK-CONTRACT <object>) would
/// retrieve the contract associated with that class (if any), then proceed
/// as for ASSERT.  Again, this is trivial to write as a portable library.

Which many programmers really don't want to do.  Not to say that they don't
think it's a good idea, but it can clutter up code and in a value-oriented
language (like a LISP dialect, for example), it can detract from the
readability of the code, especially:

	;; syntax for define-method might be d.-m. name (params) form)
	;; sorry -- don't have manual handy...
	(define-method (do-something x)
		(do-something-else x (method (blah-blah) blah-blah)))

..becomes...

	(define-method (do-something x)
		(begin
			(check-range x)
			(do-something-else x (method ...))...)

Where a 'begin'/'sequence' is needed.  Also, this is one type of contract
that has been neglected in the discussion (partially because I'm writing
this from my work-place and have [unfortunately] other responsibilities) --
The notion of range-checking input to a function is so good for program
resiliance, that I'd argue for a special form to do it with.  This could be
a Dylan parameter 'specializer', or it could be included as an optional
keyed parameter to define-method, ergo:

	(define-method (do-something x)
		domain-check: (and (>= x 0) (<= x 10))
			;; 'domain' because not a check of return value.
			;; range-check: valuable documentation, eh?
		(do-something-else x (method (blah-..........

..or...

	(define-method (do-something (x <some-class> range: (and (>= x 0).....)

Both have advantages and disadvantages since a range check implies that the
method has already been selected, and that next-method should not be used,
while the specializer implies that several ranges could be used.  These would
have to be placed (in the generic functions method list) after singletons,
but before un-ranged class-restricted parameters.

Personally, I'd rather see the first, since it restricts the notation to
actual contract checking, rather than some exotic flow of control mechanism
that may be difficult to read/maintain.

/// The final step would be to modify all the setters to run check-contract
/// after they do their thing.  I'm not sure if this could be written
/// somehow as a mix-in method on SET! and INITIALIZE -- it's a bit unclear
/// since the former is a syntax-form and the latter is perhaps being
/// revised.  But I'm sure there's some way to get this effect.  I
/// certainly wouldn't want this to be the default, however.  Contracts
/// have their charm, but I don't want to slow down *all* modifications to
/// check them.

On the issue of efficiency, I think it's important to be able to disable
*all* contract-checking in a production version of the program.  This helps
discourage people from using contracts as a special flow of control mechanism
since a more efficient production model could not be produced.  For places
where the programmer wished the production version to do run-time error
checking, the ad-hoc 'check-contract' may be best, but in keeping with the
style of the rest of Dylan, I would ask that it be defined to either return
the object itself, or on failure to raise an exception (that may be returned
from after correction...).

I am generally opposed to conditional compilation since this can produce wildly
unreadable code like C's stdio.h which in most implementations is a hairball
deserving of only its author.  But allowing toggling of contract checking for
production code wouldn't cause these problems.

Also, when some kind of 'trace' package is installed, one might want to be
able to toggle contracts off and on on a class-by-class and method-by-method
basis.  Built-in improved debugability.

/// -- Scott

	-- Scot		(Llamas?!)

P.S.  Yes, what about macros?  This is the third (and final for those of you
getting sick of discussing things outside the Dylan spec) thing that
disappointed me in the spec.  I trust that it, if nothing else, will be fixed.