From: "Aleksi Niemelä" Date: 2000-07-10T14:51:50+02:00 Subject: [ruby-talk:03912] Re: require, ensure, and Design by Contract Dave: > True, but you _can_ determine: > > 1. all the methods in the current class > 2. when any methods are _added_ to a module > 3. the current subclasses of a class > 4. when a class or module is used to extend another I'm not going to try my wings with this one, but I thought about it (too) and assumed that the basic DBC should be quite possible thing to do once we have a hook in the interpreter for adding methods. I didn't know we have such hook already. But just as Dave writes at 2., the beast exists already. The (from the top my head) example what replacing could do. > Using these combinations, I _think_ there's enough power to > be able to > wrap existing methods in pre- and -post condition handlers (where the > post-condition handler also checks the class invariant), Me too. > and to be > able to perform the wrapping on subclasses when needed. Could you say what's the need here? Why do we have to wrap them? > def incr(n) > n + 1 > end > { post: result == n.old + 1 } What if the post is real method and working like my example later? Can't we pass the copy of the arguments? (Probably not, because it leads to some other weird problems... But even so, I don't follow you here. Patrick: > In Eiffel, invariants are checked before *and* after the > execution of an > *exported* routine ... > I do not have the rationale available in my mind right now ;-), Neither do I. Assuming there's no side-effects and we check the invariant after each call, I can't see how the object could be in invalid state when we enter the method. Well, anyway it's just an optimization to rip off the invariant check before precondition check. (And Ruby will allow weird side-effects quite probably, it's up to developer whether to mess around :). > - Inheriting assertions. Should be no problem. Unless you're talking about something else than: class Foo def invariant raise "always wrong (for example, Foo is a 'abstract' base class" end class Bar < Foo def foo puts "foo" end end # Bar.new.foo # raises invariant error at Foo class Bar def invariant # raise "would raise, but we're not in abstract base class anymore :)" end end Bar.new.foo # should print "foo" > - Enforcing that pre-/postconditions are > weakened/strengthened in a > subclass by enforcing the use of another keyword. I'm not sure we're really implementing full-scale Eiffel-type of DBC here. I think I saw someone (maybe Dave) saying it's not entirely clear we have need for automatic invariant since we can always do it explicitly: class Foo def invariant # as usual end def pre_foo invariant # call explicitly end def foo end end Anyway, it's not hard to do it implicitly either. > - Convenient implementation of "old" expressions. No idea. > - Recursive assertions. If "pre" or "post" calls a feature that > returns a boolean and that itself has a "pre" and/or > "post", these > contracts are *NOT* checked. Oh, this is new to me. Could you elaborate? Why assertions should not be checked? Anyway, the replacing method could be like following. First real definition and then what our DBC workaround has created by itself: class Foo def foo puts "foo" end end -> class Foo def invariant # automatically created empty stub end def pre_foo # automatically... end #aliased orig_foo to point foo def foo(*args) old_args = args.dup error = pre_foo(args) raise error if error # or whatever will be the way to signal orig_foo(args) error = post_foo(old_args) raise error if error error = invariant raise error if error end def post_foo # automatically... end end And then we can add the check for no checking recursion by changing the core of 'foo' a little bit. if $DBC_recursion == 0 $DBC_recursion += 1 error = pre_foo(args) raise error if error # or whatever will be the way to signal orig_foo(args) error = post_foo(old_args) raise error if error error = invariant raise error if error $DBC_recursion -= 1 end This is not going to work with multithreading, but what works ;?). I think this is displaying clear need for thread-wide global area, and maybe there's already such thing but I'm just an ignorant developer :). - Aleksi