InterfaceContract distilled :: how to implement (mostly)

T

T. Onoma

Distilled:

1. An InterfaceContract is a guarentee that a class provides
required "respond_to" methods. Ruby can verify this
on either defining or constructor events. So it is a strong
promise with reasonably low overhead.

2. An InterfaceContract may also specify the types of method
parameters. But this requires extra syntax, and Ruby can't
verify these at constructor or defining moments. So it is only
a weak promise, anyway.

3. To overcome the later a runtime TypeChecker is required.
This provides strong promise, but has higher overhead.
To improve performance this could be an option
that can be switched on or off either on the command line
or inline.

4. InterfaceContracts will require type definitions
to specify the base Interfaces. And it has been proposed
that standard built in Ruby classes be used as that basis.

6. Using these base classes amounts to providing a set of
correspoding hollow, immutable replicas of the base classes.
Classes that Implement these are checked to verify that
they fulfill the role of a complete superclass of the
implemented immutables.

Thoughts:

1. If there is too much coding overhaed for defining
InterfaceContracts it will get little use. This would
make it rather obscure and thus perhaps not worth
the effort.

2. But this can be addressed by not having special
Interface types. We can just use existing classes.
If we say: implement Klass (in contrast to mixins)
then we are saying that our new class must fullfill
the role of a superclass of Klass, prior to the
instantiation of our new class, or it is not valid.

3. When #implements is invoked a new frozen subclass
of Klass is created, the Interface, and a mixin
is inserted in our new class. So when our new class
is substantiated, the use of super calls the initialize
method of the mixin module which compares the
Interface to self. If it doesn't check-out an
ImplementationError is thrown.

4. This design is not as light weight as the one Sean
has put forth because it checks the interface everytime a
new object, that implements an interface, is created. But the
advantage is that no special hooks are required for dealing
with the dynamic adding or removing of methods to our classes
working to continue to ensure the contract is being kept.
The dynamic nature of Ruby remains intact. Also I think
that if we subsequently freeze our class, we could
have this mixin removed, as it would no longer be needed.
in an enviornemt where InterfaceCnntracts are bing used
freezing calss definition make a lot of sense, and could be
common place. Although this feature would have to be
added to core ruby itself.

5. This design dosen't account for parameter types b/c they
would have only a weak promise any way. So this is best
reserved for a Euphoria like type checker as I have descibed
elsewhere, or :pre and :post assertion methods as descirbed
by James. Each has advantages and disadvantges. The
Euphoric system has one major advantage in that it can
be integrated with InterfaceContracts such that the types
defined could double for Interface classes, which seems
useful considering the next point.

6. One problem becomes quite appearent when using this tool. That of
Granularity. Ruby's built in classes have too many methods for
this to be very useful. This may indicate that Ruby's classes,
somehow, need to be broken into smaller meaningful chunks, even
if it is merely a matter of namespace dividers inside the class that
have no effect on the class other then to group related methods,
and, of course, a means to reference them from the outside.

Implementation:

class ImplementationError < StandardError
end

module InterfaceContracts
end

class Module
def implements(*klasses)
klasses.each do |klass|
InterfaceContracts.class_eval %Q{
unless #{klass} === Class
class #{klass} < #{klass}; end
#{klass}.freeze
end
}
end
ifc_str = '[' +
klasses.collect {|k| "InterfaceContracts::#{k.name}" }.join(',')+']'
ifm = Module.new
ifm.module_eval %Q{
def initialize(*args)
#{ifc_str}.each do |k|
umeths = k.public_instance_methods(true) - self.public_methods
if umeths != []
raise ImplementationError, self.class.name + " as " +
k.name + "\n" + umeths.sort.join(', ')
end
end
super
end
}
class_eval { include ifm }
end
end

Example 1:

# this may seem trival but it is a guaranteed super class of String.

class MyClass < String
implements String
def initialize
super
p "I promise to quack like a String."
end
def extra; p "extra"; end
end
m1 = MyClass.new # => "I promise to quack like a String."
m2 = MyClass.new # => "I promise to quack like a String."

Example 2:

# show that the methods of Integer have not been implemented.

class MyClass < String
implements String, Integer
def initialize
super
p "I promise to quack like a String and Integer."
end
end
m1 = MyClass.new # => ImplementationError (see below)

MyClass as InterfaceContracts::Integer (ImplementationError)
+@, -@, abs, ceil, chr, coerce, div, divmod, downto, floor, integer?,
modulo, nonzero?, prec, prec_f, prec_i, quo, remainder, round, step,
times, to_int, truncate, zero?

# notice that the unimplemented methods are listed.

-t0
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top