[#3986] Re: Principle of least effort -- another Ruby virtue. — Andrew Hunt <andy@...>

> Principle of Least Effort.

14 messages 2000/07/14

[#4043] What are you using Ruby for? — Dave Thomas <Dave@...>

16 messages 2000/07/16

[#4139] Facilitating Ruby self-propagation with the rig-it autopolymorph application. — Conrad Schneiker <schneik@...>

Hi,

11 messages 2000/07/20

[ruby-talk:03912] Re: require, ensure, and Design by Contract

From: Aleksi Niemel<aleksi.niemela@...>
Date: 2000-07-10 12:51:50 UTC
List: ruby-talk #3912
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

In This Thread

Prev Next