[ANN] Ruby/Interface

Ruby/Interface 0.1-1

Homepage/Documentation: http://interface.rubyforge.org/

=== PURPOSE

Ruby/Interface was designed to help developers understand the purpose of Ruby
objects encountered at run-time. Ruby is very dynamic, and it is sometimes
hard to determine what function any given object serves, and the methods to
which it responds to fulfill that function.

Ruby/Interface tries to solve this problem by “tagging” objects as
implementing an interface. The implementation is enforced by comparing an
object’s methods against the methods expected by the interface. When an
object is created from a class which claims to implement an interface, or
which includes a module that does, the object is flagged as properly
implementing that interface so long as its methods take the same number of
parameters as the methods described by the interface.

  Sean O'Dell

Sean O'Dell wrote:

Ruby/Interface 0.1-1

Homepage/Documentation: http://interface.rubyforge.org/

=== PURPOSE

Ruby/Interface was designed to help developers understand the
purpose of Ruby
objects encountered at run-time. Ruby is very dynamic, and it is
sometimes
hard to determine what function any given object serves, and the
methods to
which it responds to fulfill that function.

Ruby/Interface tries to solve this problem by “tagging” objects as
implementing an interface. The implementation is enforced by comparing an
object’s methods against the methods expected by the interface. When an
object is created from a class which claims to implement an interface, or
which includes a module that does, the object is flagged as properly
implementing that interface so long as its methods take the same
number of
parameters as the methods described by the interface.

Very nice... I like it!

Curt

Some comments:

I see you're using per-class tagging, hence being unable to track
changes in the singleton class. I'd point you again to the last sections
of http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/102555
which I believe you didn't read.
There's an inconsistency in the code: you're checking the methods of the
object (including its singleton methods) with Object#method but storing
the result in a per-class structure; that won't fly.

Also, using class variables instead of class instance variables ties the
notion of interface to inheritance, which I believe you wanted to avoid.

I would consider storing the conformance information in
ROBJECT(self)->klass directly; this will be either the real class or
the singleton class. In the first case, the "conformity table" is
shared by all objects of that class; in the second, it will be "owned"
by the object. The advantage is that this new table would be created
on demand, only when the object is extended/a singleton method is
defined and has_interface? is called. You wouldn't even need to capture
singleton_method_added this way.

···

On Wed, Jun 09, 2004 at 03:46:30AM +0900, Sean O'Dell wrote:

Ruby/Interface tries to solve this problem by ???tagging??? objects as
implementing an interface. The implementation is enforced by comparing an
object???s methods against the methods expected by the interface.

--
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

<miguel> any new sendmail hole I have to fix before going on vacations?
  -- Seen on #Linux

Sean O'Dell wrote:

Ruby/Interface 0.1-1

Very nice!

But wouldn't implements_interface(...) instead of declare_interface(...) be better, e.g "implements" is the Java terminology for implementing an interface? And maybe also implements_interface?(...) instead of has_interface?(...). Of aliasses for both.

Regards,

Peter

Nice. But the Hash example on the page should be more comprehensive.
It's ironic that this was driven by wanting to tell an array from a
hash, and you've defined a :hash interface with just and = !

Gavin

···

On Wednesday, June 9, 2004, 4:46:30 AM, Sean wrote:

Ruby/Interface 0.1-1

Homepage/Documentation: http://interface.rubyforge.org/

Changed the name of the library to celsoft.com/interface.

  Sean O'Dell

I decided the library wasn't "Ruby" enough and I got some ideas from Daniel
Berger's interface library, so I made a lot of changes.

Homepage: http://interface.rubyforge.org/
Rubyforge Project Homepage: http://rubyforge.org/projects/interface/
Download: http://rubyforge.org/frs/?group_id=266&release_id=529

Code Snippet:

  require "celsoft.com/interface"

  HashInterface = Interface.new

  HashInterface.add_class_patterns(Hash, :[], :[]=)
  HashInterface.add_pattern(:each_pair, proc{||})

  class Hash
    implements HashInterface
  end

  class<<ENV
    implements HashInterface
  end

  p Hash.new.implements?(HashInterface) == true
  p ENV.implements?(HashInterface) == true

  HashInterface.add_pattern(:each_pair, proc{|a|}) # method param changed

  p ENV.implements?(HashInterface) == false

How It Works

Method patterns are stored with each Interface object and are used to check
against the methods of any given object. The name and number of parameters
are important. If an object never claims to implement an interface, it will
always report that it does not implement a given interface, even if it may be
capable of it. When an object claims to implement an interface, a thorough
check is performed to verify that it actually does. This check is only
performed when necessary for efficiency, when implements? is called. The
result of the check is cached and returned quickly on subsequent calls to
implements?. If either the interface definition changes, or methods are
changed in the class of the object, the cache is dumped and the thorough
check is performed the next time implements? is called, and the answer is
then again cached.

I have to ponder this a bit. I admit I am not entirely clear how Ruby works
internally, and singletons are something I don't quite get. Let me digest
the suggestions.

  Sean O'Dell

···

On Tuesday 08 June 2004 13:23, Mauricio Fernández wrote:

On Wed, Jun 09, 2004 at 03:46:30AM +0900, Sean O'Dell wrote:
> Ruby/Interface tries to solve this problem by ???tagging??? objects as
> implementing an interface. The implementation is enforced by comparing an
> object???s methods against the methods expected by the interface.

Some comments:

I see you're using per-class tagging, hence being unable to track
changes in the singleton class. I'd point you again to the last sections
of http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/102555
which I believe you didn't read.
There's an inconsistency in the code: you're checking the methods of the
object (including its singleton methods) with Object#method but storing
the result in a per-class structure; that won't fly.

Also, using class variables instead of class instance variables ties the
notion of interface to inheritance, which I believe you wanted to avoid.

I would consider storing the conformance information in
ROBJECT(self)->klass directly; this will be either the real class or
the singleton class. In the first case, the "conformity table" is
shared by all objects of that class; in the second, it will be "owned"
by the object. The advantage is that this new table would be created
on demand, only when the object is extended/a singleton method is
defined and has_interface? is called. You wouldn't even need to capture
singleton_method_added this way.

Probably right. I'll change it. Seems I'm going to be fiddling it with it
some more today anyway, may as well take of that now!

  Sean O'Dell

···

On Tuesday 08 June 2004 13:28, Peter C. Verhage wrote:

Sean O'Dell wrote:
> Ruby/Interface 0.1-1

Very nice!

But wouldn't implements_interface(...) instead of declare_interface(...)
be better, e.g "implements" is the Java terminology for implementing an
interface? And maybe also implements_interface?(...) instead of
has_interface?(...). Of aliasses for both.

> Ruby/Interface tries to solve this problem by ???tagging??? objects as
> implementing an interface. The implementation is enforced by comparing an
> object???s methods against the methods expected by the interface.

Some comments:

I see you're using per-class tagging, hence being unable to track
changes in the singleton class. I'd point you again to the last sections
of http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/102555
which I believe you didn't read.

Per-class tagging is what I intended. There are three tag states: not tagged,
tagged but unverified, and tagged and verified. Tagging initially only means
"objects of this class may implement this interface." A check of the object
itself later will determine if the object really does implement the
interface.

There's an inconsistency in the code: you're checking the methods of the
object (including its singleton methods) with Object#method but storing
the result in a per-class structure; that won't fly.

I see what you mean. I need to move the storage of the checks themselves to
the object, absolutely correct. I think what I was thinking when I did it
this way was, since I get no "module_added" messages when methods are added
to objects with <<, there was no sense in storing the information with the
objects themselves. But I see that there's an inconsistency that way. I'll
change that.

Also, using class variables instead of class instance variables ties the
notion of interface to inheritance, which I believe you wanted to avoid.

No, not necessarily. Classes derived from other classes should inherit the
interface. The initial tag only means "may implement the interface" not
"does implement it." The final test is a comparison of the methods of the
individual object methods and their arity.

I would consider storing the conformance information in
ROBJECT(self)->klass directly; this will be either the real class or
the singleton class. In the first case, the "conformity table" is
shared by all objects of that class; in the second, it will be "owned"
by the object. The advantage is that this new table would be created
on demand, only when the object is extended/a singleton method is
defined and has_interface? is called. You wouldn't even need to capture
singleton_method_added this way.

Ah, I see...thanks for pointing me at this. I was never really aware of klass
before. I just read about how it relates to Singleton's, and it gave a whole
heap of ideas on how I could do this better.

  Sean O'Dell

  Sean O'Dell

···

On Tuesday 08 June 2004 13:23, Mauricio Fernández wrote:

On Wed, Jun 09, 2004 at 03:46:30AM +0900, Sean O'Dell wrote:

Moving things around to work out of klass helped a lot, Mauricio, thanks for
the advice. Got a new version up and running already. It handles Singletons
nicely now.

  Sean O'Dell

···

On Tuesday 08 June 2004 13:23, Mauricio Fernández wrote:

On Wed, Jun 09, 2004 at 03:46:30AM +0900, Sean O'Dell wrote:
> Ruby/Interface tries to solve this problem by ???tagging??? objects as
> implementing an interface. The implementation is enforced by comparing an
> object???s methods against the methods expected by the interface.

Some comments:

I see you're using per-class tagging, hence being unable to track
changes in the singleton class. I'd point you again to the last sections
of http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/102555
which I believe you didn't read.
There's an inconsistency in the code: you're checking the methods of the
object (including its singleton methods) with Object#method but storing
the result in a per-class structure; that won't fly.

Also, using class variables instead of class instance variables ties the
notion of interface to inheritance, which I believe you wanted to avoid.

I would consider storing the conformance information in
ROBJECT(self)->klass directly; this will be either the real class or
the singleton class. In the first case, the "conformity table" is
shared by all objects of that class; in the second, it will be "owned"
by the object. The advantage is that this new table would be created
on demand, only when the object is extended/a singleton method is
defined and has_interface? is called. You wouldn't even need to capture
singleton_method_added this way.

The name :hash tells you the purpose of the interface, not the method list.
The method list is just to aid enforcement (to ensure they exist for the
interface, and to ensure they have the same parameters so you can always
depend on them being there as you expect them).

.has_interface?(:hash) => false

Make sense?

  Sean O'Dell

···

On Tuesday 08 June 2004 16:40, Gavin Sinclair wrote:

On Wednesday, June 9, 2004, 4:46:30 AM, Sean wrote:
> Ruby/Interface 0.1-1
>
> Homepage/Documentation: http://interface.rubyforge.org/

Nice. But the Hash example on the page should be more comprehensive.
It's ironic that this was driven by wanting to tell an array from a
hash, and you've defined a :hash interface with just and = !

Hi --

···

On Wed, 9 Jun 2004, Sean O'Dell wrote:

Changed the name of the library to celsoft.com/interface.

Ignore previous message (similar idea in spirit though
flipped the other way) -- hadn't seen this one.

David

--
David A. Black
dblack@wobblini.net

You might want to capture Module#remove_method and
Module#undef_method...

···

On Thu, Jun 10, 2004 at 04:55:34AM +0900, Sean O'Dell wrote:

If either the interface definition changes, or methods are
changed in the class of the object, the cache is dumped and the thorough
check is performed the next time implements? is called, and the answer is
then again cached.

--
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Linux is not user-friendly.

It _is_ user-friendly. It is not ignorant-friendly and idiot-friendly.
  -- Seen somewhere on the net

I decided the library wasn't "Ruby" enough and I got some ideas from Daniel
Berger's interface library, so I made a lot of changes.

Homepage: http://interface.rubyforge.org/
Rubyforge Project Homepage: http://rubyforge.org/projects/interface/
Download: http://rubyforge.org/frs/?group_id=266&release_id=529

Code Snippet:

       require "celsoft.com/interface"

       HashInterface = Interface.new

       HashInterface.add_class_patterns(Hash, :, :=)
       HashInterface.add_pattern(:each_pair, proc{||})

       class Hash
         implements HashInterface
       end

       class<<ENV
         implements HashInterface
       end

       p Hash.new.implements?(HashInterface) == true
       p ENV.implements?(HashInterface) == true

Should be implement?() vs implements?() I believe.
See include?() for example.
That is a nice Ruby idiom.

Also: You are using both "declarative" (like "implements")
and "imperative" styles (like "add_pattern). I feel like
"declarative" style would be appropriate in both cases.

OTOH that might be a matter of style/taste.

Yours,

JeanHuguesRobert
EOM

···

At 04:55 10/06/2004 +0900, you wrote:

       HashInterface.add_pattern(:each_pair, proc{|a|}) # method param changed

       p ENV.implements?(HashInterface) == false

How It Works

Method patterns are stored with each Interface object and are used to check
against the methods of any given object. The name and number of parameters
are important. If an object never claims to implement an interface, it will
always report that it does not implement a given interface, even if it may be
capable of it. When an object claims to implement an interface, a thorough
check is performed to verify that it actually does. This check is only
performed when necessary for efficiency, when implements? is called. The
result of the check is cached and returned quickly on subsequent calls to
implements?. If either the interface definition changes, or methods are
changed in the class of the object, the cache is dumped and the thorough
check is performed the next time implements? is called, and the answer is
then again cached.

-------------------------------------------------------------------------
Web: @jhr is virteal, virtually real
Phone: +33 (0) 4 92 27 74 17

Should be implement?() vs implements?() I believe.
See include?() for example.
That is a nice Ruby idiom.

It's not proper English, but perhaps implement? is more "Ruby."

Also: You are using both "declarative" (like "implements")
and "imperative" styles (like "add_pattern). I feel like
"declarative" style would be appropriate in both cases.

On this I totally agree, except for one thing. Since I can't do:

interface IHash
    pattern (key)
end

...to create the interface (like class or module) I don't feel like
declarative is the way to go. I have no choice but to build interface
procedurally, so I felt that more action-oriented methods were better.

I think if Matz ever gives developers the ability to do what I did above
there, I would switch to something more declarative.

  Sean O'Dell

···

On Thursday 10 June 2004 01:28, Jean-Hugues ROBERT wrote:

At 04:55 10/06/2004 +0900, you wrote:

After being annoyed with what looked like improper English in
all those #foo? methods, I noticed that you could read them as, say, "does foo include?" or "does foo respond_to?" etc... They make sense that way, and are proper English, inasmuch as any programming construct is proper English :slight_smile:

cheers,
Mark

···

On Jun 10, 2004, at 8:33 AM, Sean O'Dell wrote:

On Thursday 10 June 2004 01:28, Jean-Hugues ROBERT wrote:

At 04:55 10/06/2004 +0900, you wrote:

Should be implement?() vs implements?() I believe.
See include?() for example.
That is a nice Ruby idiom.

It's not proper English, but perhaps implement? is more "Ruby."

Interesting way to look at it.

Running my eyes over a lot of Ruby methods, I can see that implement? is more
correct. However, I noticed that some methods are has_ or is_ prepended to
them. I guess when does_ makes more sense, the rule is to eliminate it?
Either way, I see the pattern and I prefer the library behave the way people
"expect" so I'll change the method names.

Just a side-note: I've added pre-defined interfaces for IArray, IHash,
IString, IDate and ITime so far. 0.6 will have ITime and IDate and whatever
others I add. 0.5 already has those others.

  Sean O'Dell

···

On Thursday 10 June 2004 10:57, Mark Hubbart wrote:

On Jun 10, 2004, at 8:33 AM, Sean O'Dell wrote:
> On Thursday 10 June 2004 01:28, Jean-Hugues ROBERT wrote:
>> At 04:55 10/06/2004 +0900, you wrote:
>>
>> Should be implement?() vs implements?() I believe.
>> See include?() for example.
>> That is a nice Ruby idiom.
>
> It's not proper English, but perhaps implement? is more "Ruby."

After being annoyed with what looked like improper English in
all those #foo? methods, I noticed that you could read them as, say,
"does foo include?" or "does foo respond_to?" etc... They make sense
that way, and are proper English, inasmuch as any programming construct
is proper English :slight_smile: