Thinking About Java Interfaces In Ruby

I'm currently reading "Holub on Patterns", an excellent volume on Object Oriented Programming Voodoo. The language of the book is Java and it makes extensive use of Java's Interfaces to avoid "The Fragile Base Class Problem".

This has me thinking a lot about proper Ruby coding techniques. What is Ruby's equivalent to a Java Interface?

module Interface
  def some_method; end
  def another_method; end
end

That doesn't really require you to provide those methods in a class that mixes them in. Perhaps:

module Interface
  def some_method
    raise NoMethodError, "Must be overriden!"
  end
  def another_method
    raise NoMethodError, "Must be overriden!"
  end
end

Of course, I guess the only point of something like this is type checking, which Java needs when it declares a variable, but Ruby does not. Given that, client code can easily check interface:

def my_method( interface )
  interface.respond_to?(:some_method) or raise ArgumentError, "Invalid interface."
  interface.respond_to?(:another_method) or raise ArgumentError, "Invalid interface."

  # ...
end

But that probably doesn't gain you much over just trying to call the methods and throwing exceptions that way, I guess. Which brings us full circle to doing nothing at all.

Does Ruby combat The Fragile Base Class Problem with philosophy alone? I'm referring to Duck Typing here, of course. Let them pass in what they want and if it responds to all the right messages, we don't care what it is. In this scenario, we're not supposed to check "kind_of?" at all, right? In that case, how do you document a method like this?

class DatabaseTable
  # ...

  def export( exporter )
    exporter.start_table
    exporter.store_metadata(@name, @width, @column_names)
    @rows.each { |row| exporter.store_row(row) }
    exporter.end_table
  end
end

You don't really just say, "Pass in an object that responds to X, Y, and Z." in the RDoc, do you?

The "exporter" argument should probably be a type, it seems to me. I guess in this case a module with empty methods is a fine choice. Then you can just override what you need. But wait, isn't that just a "Base Class"??? Might as well use a class then...

In case it isn't obvious, this message is mostly just me thinking out loud. However, I'm very interested in the opinions of others, which is why I'm thinking out loud on Ruby Talk.

I figure I'm probably just too much in Java mode right now (sorry, it's the day job) so that I can't see the Ruby answers. If you're reading my babble and thinking "You wouldn't do it like that in Ruby!", criticize away. You won't hurt my feelings.

James Edward Gray II

P.S. Book writers, are you listening?! I'm crying out for a Ruby OO Voodoo book!!!

Hello James,

You don't really just say, "Pass in an object that responds to X, Y,
and Z." in the RDoc, do you?

Unfortunately yes. And it is of course a huge source of errors.
It would be much better if ruby would support formally interfaces in
the same way as Smalltalk supports protocols. These interfaces are
notation only and are only used by the IDE to check if a method is missing.

You can't do something against passing a wrong object in a dynamically
typed language, but you can do something against missing methods
during implementation.

Remember that the problems grow if the interface changes and
you have old ruby code, since even if you write test cases you will
normally not catch all those errors, leaving this upto your customers.

This is why many people think that languages like ruby are not good
for programming in the large.

···

--
Best regards, emailto: scholz at scriptolutions dot com
Lothar Scholz http://www.ruby-ide.com
CTO Scriptolutions Ruby, PHP, Python IDE 's

In the RDoc, you usually say "Pass in an object of X class." RDoc comments ideally are a sort of loose contract, so when you say that you're implying "You can poke around with other classes, but there's no guarantee this library won't blow up when you do it, and there's no guarantee the internals won't change in the future so that a hack that works today will break tomorrow."

It's a philosophical difference, and it's fairly profound. Heavy OO theory seems to believe more or less in Platonic ideals: There are universal types that exist in the universe, and if you do a lot of domain research up front you'll discover them all and won't have to refactor much as you implement. (This is comparable, incidentally, to Islamic illustrators of antiquity, who were not allowed to make drawings that mimicked nature, so every time they drew, say, a horse, they would say they weren't drawing a particular horse in the world, but the essence of all horses.)

Lightweight OO theory -- expressed in the idioms of Ruby and other light OO languages like Smalltalk -- says that types are provisional, and to a certain extent, they're just convenient lies we tell ourselves to get our work done. (If you want, you can tie this into certain strains of Buddhism which argue that the self is an illusion we hold up to get through the day.) You define your classes as you need them, but the time may come when you discover that the class needs to be redefined or discarded entirely, so you do what is necessary for the moment and move on.

Note that most of this is untenable without rigorous unit testing. And in fact, the recent popularity of Ruby and *cough* Python owe more than a little to the bubble in XP and Agile methodologies a few years back. People started unit-testing in Java, then they realized that once they were writing the tests, all that static typing caused more harm then good.

On a level of expressiveness, I think one of the nice consequences of unit testing is the fact that you can encode a lot of subtle assumptions into those tests. As opposed to trying to write a class and interface, that's not going to encode nearly as much.

F.

···

On Nov 11, 2004, at 10:19 AM, James Edward Gray II wrote:

Does Ruby combat The Fragile Base Class Problem with philosophy alone? I'm referring to Duck Typing here, of course. Let them pass in what they want and if it responds to all the right messages, we don't care what it is. In this scenario, we're not supposed to check "kind_of?" at all, right? In that case, how do you document a method like this?

class DatabaseTable
  # ...

  def export( exporter )
    exporter.start_table
    exporter.store_metadata(@name, @width, @column_names)
    @rows.each { |row| exporter.store_row(row) }
    exporter.end_table
  end
end

You don't really just say, "Pass in an object that responds to X, Y, and Z." in the RDoc, do you?

James Edward Gray II ha scritto:

I'm currently reading "Holub on Patterns", an excellent volume on Object Oriented Programming Voodoo. The language of the book is Java and it makes extensive use of Java's Interfaces to avoid "The Fragile Base Class Problem".

This has me thinking a lot about proper Ruby coding techniques. What is Ruby's equivalent to a Java Interface?

module Interface
    def some_method; end
    def another_method; end
end

That doesn't really require you to provide those methods in a class that mixes them in. Perhaps:

module Interface
    def some_method
        raise NoMethodError, "Must be overriden!"
    end
    def another_method
        raise NoMethodError, "Must be overriden!"
    end
end

Of course, I guess the only point of something like this is type checking, which Java needs when it declares a variable, but Ruby does not.

notice that there is a Interface module that allows you to have interfaces a-la java. See here:
http://ruby-miscutils.sourceforge.net/interface.txt
(yeah ruby can become most anything :slight_smile:

Given that, client code can easily check interface:

def my_method( interface )
    interface.respond_to?(:some_method) or raise ArgumentError, "Invalid interface."
    interface.respond_to?(:another_method) or raise ArgumentError, "Invalid interface."

    # ...
end

But that probably doesn't gain you much over just trying to call the methods and throwing exceptions that way, I guess. Which brings us full circle to doing nothing at all.

Imo it gives you both a crash-early and a documentation hint, wich are valuable things on their own. I'd like to see optional type hints in ruby, even because I'd love to have generic methods or predicate dispatch.
The problem is that either they're checked at runtime, thus slowing down the code or they're handled with some complex partial evaluation at compile time (I think some scheme/lisp do this)

Does Ruby combat The Fragile Base Class Problem with philosophy alone? I'm referring to Duck Typing here, of course. Let them pass in what they want and if it responds to all the right messages, we don't care what it is. In this scenario, we're not supposed to check "kind_of?" at all, right? In that case, how do you document a method like this?

class DatabaseTable
    # ...

    def export( exporter )
        exporter.start_table
        exporter.store_metadata(@name, @width, @column_names)
        @rows.each { |row| exporter.store_row(row) }
        exporter.end_table
    end
end

You don't really just say, "Pass in an object that responds to X, Y, and Z." in the RDoc, do you?
The "exporter" argument should probably be a type, it seems to me. I guess in this case a module with empty methods is a fine choice. Then you can just override what you need. But wait, isn't that just a "Base Class"??? Might as well use a class then...

maybe you can define a module Exporter or an AbstractExporter class and document that, but it's not a real solution, I think.
But it does document things, and whenever a user of DatabaseTable look at it he can just figure out 'oh, well, I have to define this, no need to import.. '

In case it isn't obvious, this message is mostly just me thinking out loud. However, I'm very interested in the opinions of others, which is why I'm thinking out loud on Ruby Talk.

Everubody come to think of this sooner or later :slight_smile:
I even wrote an rcr wich is related : RCR 280: Unified type conversion framework

I figure I'm probably just too much in Java mode right now (sorry, it's the day job) so that I can't see the Ruby answers. If you're reading my babble and thinking "You wouldn't do it like that in Ruby!", criticize away. You won't hurt my feelings.

I hope not :slight_smile:
anyway, I think problems such "I will break stuff whenever I modify something and don't have a clear documented interface!" is mostly solved from Test::Unit. You're writing tests anyway, don't you? :slight_smile:

James Edward Gray II

P.S. Book writers, are you listening?! I'm crying out for a Ruby OO Voodoo book!!!

+1

Yes, indeed, I do. If I want something that responds to #<<, I'll
say that.

The only reason I really see for having interface hinting is
automatic cross-language interface discovery (e.g., SOAP, CORBA,
etc.). This way, you can simply say "this class is available for
SOAP manipulation" and SOAP can discover what it expects.

I haven't missed interfaces in the way that they are required for
less dynamic languages.

-austin

···

On Fri, 12 Nov 2004 00:19:28 +0900, James Edward Gray II <james@grayproductions.net> wrote:

You don't really just say, "Pass in an object that responds to X,
Y, and Z." in the RDoc, do you?

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

module Interface
    def some_method
        raise NoMethodError, "Must be overriden!"
    end
    def another_method
        raise NoMethodError, "Must be overriden!"
    end
end

Another way, which I personally feel is neatier (if nothing else because the error message comes a lot earlier) is to check in the constructor:

Class base_class
    def initalize
       raise 'Method required: MethodA' unless (respond_to? :MethodA)
       raise 'Method required: MethodB' unless (respond_to? :MethodB)
    end
end

It will break if the constructor is overridden without calling the one of the superclass though.

Strange - that's not my experience.

Cheers

Dave

···

On Nov 11, 2004, at 9:32, Lothar Scholz wrote:

Hello James,

> You don't really just say, "Pass in an object that responds to X, Y,
> and Z." in the RDoc, do you?

Unfortunately yes. And it is of course a huge source of errors.

Austin Ziegler wrote:

You don't really just say, "Pass in an object that responds to X,
Y, and Z." in the RDoc, do you?

Yes, indeed, I do. If I want something that responds to #<<, I'll
say that.

The only reason I really see for having interface hinting is
automatic cross-language interface discovery (e.g., SOAP, CORBA,
etc.). This way, you can simply say "this class is available for
SOAP manipulation" and SOAP can discover what it expects.

I haven't missed interfaces in the way that they are required for
less dynamic languages.

-austin

I'm completely with Austin on this. I do the same thing: "this method requires a method that responds to #foo". It has served me well enough.

The *only* time I've thought interfaces would be nice is when I considered finding a way to implement type 1 inversion of control. This is where a service just says "I need another service that implements this interface" and the container goes and finds one that matches.

Even then, I was only thinking it would be neat to implement for the "gee-whiz" value, not because I actually needed that feature.

···

On Fri, 12 Nov 2004 00:19:28 +0900, James Edward Gray II > <james@grayproductions.net> wrote:

--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck.org/jamis

Dave Thomas wrote:

Hello James,

> You don't really just say, "Pass in an object that responds to X, Y,
> and Z." in the RDoc, do you?

Unfortunately yes. And it is of course a huge source of errors.

Strange - that's not my experience.

Nor mine. But there are some number of people who believe this to be true.

In some cases, they believe this because they heard it from a source they trust. But for those who actually experience this, I wonder if there is something about their coding/design process that differs from those who find duck typing largely trouble-free.

Put another way, some find duck typing "a huge source of errors", others do not. I do not think anyone is lying, so what accounts for the difference? Class/method design? Unit testing? LOC?

James

···

On Nov 11, 2004, at 9:32, Lothar Scholz wrote:

If you want to be really far out, you can write code that infers this, by looking to see if various methods are defined. I've done this in extremely simple cases, and I wonder if there's a use for the idea in broader contexts, but that sort of feels like PhD stuff.

F.

···

On Nov 11, 2004, at 1:07 PM, Jamis Buck wrote:

The *only* time I've thought interfaces would be nice is when I considered finding a way to implement type 1 inversion of control. This is where a service just says "I need another service that implements this interface" and the container goes and finds one that matches.

Even then, I was only thinking it would be neat to implement for the "gee-whiz" value, not because I actually needed that feature.

Jamis Buck ha scritto:

I'm completely with Austin on this. I do the same thing: "this method requires a method that responds to #foo". It has served me well enough.

well, but once you write it up as documentation would'nt you be happy if actually there was a check for this and the doc can be extrapolated automatically?
(ok, ok automagic interface inference is impossible but it sounds so cool :slight_smile:

> Strange - that's not my experience.

Nor mine. But there are some number of people who believe this to be true.

nor mine. so far.

Put another way, some find duck typing "a huge source of errors", others
do not. I do not think anyone is lying, so what accounts for the
difference? Class/method design? Unit testing? LOC?

i think the source of "huge errors" would be in the overall
programming experience.
when i was young and full of hope, coding in c and perl was such a
pain primarily because
i didn't know how to write tests then.

shifting from php/perl to java when i grew a bit older in the code
gave me a really good
feeling because my IDE can check syntax even without running things i wrote.

then i as grew even older in the code, java just couldn't hack it
anymore and i found
ruby. but this time, i have learned how to write tests and i never felt an ounce
of unease just because i can't check syntax while writing.

so there. IMHO, duck typing is good because i don't have to go thru hoops just
to get my new class accepted. but the insurance comes from good tests.
another great
benefit is the lesser number of lines of code to do equivalent tasks.

ask paul graham might say "succinctness is power". you might want to
read his thoughts on java too. :slight_smile:

now back to my day job...

···

On Fri, 12 Nov 2004 01:54:35 +0900, James Britt <jamesunderbarb@neurogami.com> wrote:

--
i'm not flying. i'm falling... in style.

Yep, Weak-typing isn't a source of errors for me either.

Much of the java development cycle is spent casting and boilerplating,
IMHO, and for me, that *is* a source of errors -- but I do not find
instances in Perl or Ruby where I have an array of gerbils and a
giraffee accidentally sneaks in. That just doesn't happen very
often, and when it does, it's so rare I'm ok with the runtime
exception.

Up front checking on some things may be nice, but there are certaintly
a reason I am coding in a weakly typed language -- namely, they are
still fun to program in and there is much less boilerplate.

···

On Fri, 12 Nov 2004 01:54:35 +0900, James Britt <jamesunderbarb@neurogami.com> wrote:

Dave Thomas wrote:

>
> On Nov 11, 2004, at 9:32, Lothar Scholz wrote:
>
>> Hello James,
>>
>> > You don't really just say, "Pass in an object that responds to
>> X, Y,
>> > and Z." in the RDoc, do you?
>>
>> Unfortunately yes. And it is of course a huge source of errors.
>
>
> Strange - that's not my experience.

Nor mine. But there are some number of people who believe this to be true.

In some cases, they believe this because they heard it from a source
they trust. But for those who actually experience this, I wonder if
there is something about their coding/design process that differs from
those who find duck typing largely trouble-free.

Put another way, some find duck typing "a huge source of errors", others
do not. I do not think anyone is lying, so what accounts for the
difference? Class/method design? Unit testing? LOC?

James

Francis Hwang wrote:

The *only* time I've thought interfaces would be nice is when I considered finding a way to implement type 1 inversion of control. This is where a service just says "I need another service that implements this interface" and the container goes and finds one that matches.

Even then, I was only thinking it would be neat to implement for the "gee-whiz" value, not because I actually needed that feature.

If you want to be really far out, you can write code that infers this, by looking to see if various methods are defined. I've done this in extremely simple cases, and I wonder if there's a use for the idea in broader contexts, but that sort of feels like PhD stuff.

True, and I considered that (the approach, not the PhD :-P). However, most of the time, the interface that is desired is only a small subset of the methods published by the object...

Also, things are further complicated by the fact that you can't just look at a service definition and determine its interface by reflection. This is because the service definition (in Needle) is what is returned by executing a block, and I'd like to be able to get the interface information without having to execute the block (and thereby actually instatiate the object).

I've considered having service declarations explicitly name the methods they export, but that's where things just become too much work. Besides, "interface injection" just doesn't feel very Rubyish, especially since interfaces in Ruby can be changed on the fly, and may even differ from one object to the next, even though they share the same instantiating class.

- Jamis

···

On Nov 11, 2004, at 1:07 PM, Jamis Buck wrote:

--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck.org/jamis

I suppose one of the things that becomes evident in a learning process like this is that there are two sorts of correctness: Syntactic (does it compile?) and Semantic (does it do what it's supposed to do?) It's trivial to write code that is syntactically correct but semantically gibberish. It's the second you have to worry about, and a compiler can't help you with that stuff.

F.

···

On Nov 11, 2004, at 12:48 PM, Edwin Eyan Moragas wrote:

shifting from php/perl to java when i grew a bit older in the code
gave me a really good
feeling because my IDE can check syntax even without running things i wrote.

then i as grew even older in the code, java just couldn't hack it
anymore and i found
ruby. but this time, i have learned how to write tests and i never felt an ounce
of unease just because i can't check syntax while writing.

Hello Michael,

Yep, Weak-typing isn't a source of errors for me either.

Most of my errors came from the time when i had to work on dynamically
typed source code (Python in this case) that came from other persons.
Its a real pain to find out what types are expected if you haven't
written the code yourself. Maybe on the next project i have some time
to find out more by using Humphrey Watts "Personal Software Process".

···

--
Best regards, emailto: scholz at scriptolutions dot com
Lothar Scholz http://www.ruby-ide.com
CTO Scriptolutions Ruby, PHP, Python IDE 's

I assume you don't want to instantiate the service because instantiation is expensive? How about just instantiating it once, getting the methods, and then saving them for later? (what's below is not tested, I'm just thinking in code ...)

service_class = SomeReallyExpensiveService
service = service_class.new
my_methods = service.methods
File.open( "~/.needle/#{ service_class.name }.methods.yaml", "w" ) { |f|
   YAML.dump( my_methods, f )
}

Then whenever Needle is wrapping that service, you can look for the YAML file to see if you've got the methods pre-processed. It's like post-compilation!

Holy crap, do I love reflectivity.

Anyway, it's true that this all falls apart if you're adding per-instance methods to a service. Personally, I really don't like adding per-instance methods: For all my loosey-goosy typeless talk, I'm sort of a chicken about that stuff and I like all my objects of one class to respond to the exact same set of methods. But that's just me.

F.

···

On Nov 11, 2004, at 1:28 PM, Jamis Buck wrote:

True, and I considered that (the approach, not the PhD :-P). However, most of the time, the interface that is desired is only a small subset of the methods published by the object...

Also, things are further complicated by the fact that you can't just look at a service definition and determine its interface by reflection. This is because the service definition (in Needle) is what is returned by executing a block, and I'd like to be able to get the interface information without having to execute the block (and thereby actually instatiate the object).

I've considered having service declarations explicitly name the methods they export, but that's where things just become too much work. Besides, "interface injection" just doesn't feel very Rubyish, especially since interfaces in Ruby can be changed on the fly, and may even differ from one object to the next, even though they share the same instantiating class.

right on.

···

On Fri, 12 Nov 2004 03:06:54 +0900, Francis Hwang <sera@fhwang.net> wrote:

I suppose one of the things that becomes evident in a learning process
like this is that there are two sorts of correctness: Syntactic (does
it compile?) and Semantic (does it do what it's supposed to do?) It's
trivial to write code that is syntactically correct but semantically
gibberish. It's the second you have to worry about, and a compiler
can't help you with that stuff.

--
i'm not flying. i'm falling... in style.