[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Thinking About Java Interfaces In Ruby

James Gray

11/11/2004 3:19:00 PM

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!!!



17 Answers

Lothar Scholz

11/11/2004 3:32:00 PM

0

Hello James,

JEGI> You don't really just say, "Pass in an object that responds to X, Y,
JEGI> 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.ru...
CTO Scriptolutions Ruby, PHP, Python IDE 's




Francis Hwang

11/11/2004 3:41:00 PM

0


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?
>

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.



gabriele renzi

11/11/2004 4:07:00 PM

0

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/int...
(yeah ruby can become most anything :)

> 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 :)
I even wrote an rcr wich is related : http://www.rcrchive.net/rc...



> 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 :)
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? :)


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

+1

Dave Thomas

11/11/2004 4:32:00 PM

0


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

> Hello James,
>
> JEGI> You don't really just say, "Pass in an object that responds to
> X, Y,
> JEGI> 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.


Cheers

Dave



James Britt

11/11/2004 4:55:00 PM

0

Dave Thomas wrote:

>
> On Nov 11, 2004, at 9:32, Lothar Scholz wrote:
>
>> Hello James,
>>
>> JEGI> You don't really just say, "Pass in an object that responds to
>> X, Y,
>> JEGI> 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




Edwin Eyan Moragas

11/11/2004 5:48:00 PM

0

On Fri, 12 Nov 2004 01:54:35 +0900, James Britt
<jamesunderbarb@neurogami.com> wrote:
> > 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. :-)

now back to my day job...

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


Austin Ziegler

11/11/2004 6:00:00 PM

0

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?

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
--
Austin Ziegler * halostatue@gmail.com
* Alternate: austin@halostatue.ca


Francis Hwang

11/11/2004 6:07:00 PM

0


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.

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.



Jamis Buck

11/11/2004 6:07:00 PM

0

Austin Ziegler wrote:
> 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?
>
>
> 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.

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


Francis Hwang

11/11/2004 6:15:00 PM

0


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.

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.