On 1 Dec 2010, at 21:16, Daniel Weinreb wrote:
I have always liked the idea of having protocols say more than just "these are the functions and these are the arguments, which are optional, ane maybe what their types are. I'd love it if there were a way to say "in order to fulfill this contract, doing write-string of a string must behave exactly the same as doing write-char on each." You could imagine all kinds of integrity constratints. You could specify that some function be commutative, that some be associative with respect to each other, that one have no side effects, that one be idempotent, and so on. We could start by having a well-known template for documenting/commenting the functions in a protocol to be able to say things like this.
I would also like to specify that methods on a specific generic function should always halt, and would like to enforce that statically. ;-)
I'm only half joking: The fact that you cannot solve the halting problems puts some boundaries on what you may and may not be able to express in such contracts. On top of that, it is extremely hard to be precise enough when specifying such contracts.
Let's take your example "write-string must behave exactly the same as doing write-char on each". Let's assume these functions are implemented as follows for the default case:
(shadow 'write-char) (shadow 'write-string)
(defgeneric write-char (stream char) (:method ((stream t) (char char)) (cl:write-char char stream)))
(defgeneric write-string (stream string) (:method ((stream t) (string string)) (loop for char across string do (write-char stream char))))
Now assume somebody implements their own stream class and does the following:
(defmethod write-char ((stream my-stream) (char char)) (write-string stream (make-string 1 :initial-element char)))
(defmethod write-string ((stream my-stream) (string string)) (cl:write-string string stream))
This breaks your suggested contract. Why? Somebody else may want to provide some form of mixin functionality like this:
(defmethod write-char :around (stream char) (incf *write-counter*) (call-next-method))
With the original methods, this correctly counts he written characters, but with the methods for my-stream, most characters will not be counted anymore. Your suggested contract seems to suggest that this :around method is correct and the methods for my-stream break the contract. Is that what you intended?
Pascal