java guilt

G

Giles Bowkett

I just solved a problem by adding an instance method to String.

It makes for very elegant code elsewhere within the application.

Yet I can't shake the feeling I've done something dirty.

I think there needs to be a group like "Recovering Java Programmers
Anonymous" or somehting.
 
D

David A. Black

Hi --

I just solved a problem by adding an instance method to String.

It makes for very elegant code elsewhere within the application.

Yet I can't shake the feeling I've done something dirty.

I think there needs to be a group like "Recovering Java Programmers
Anonymous" or somehting.

Feeling squeamish about changing the core language isn't the sole
prerogative of recovering Java programmers :) There's still no good
answer to the question of how to do it safely and in a way that does
not risk conflicts, though there are various "better practices". My
favorite is "extend", which lets you confine changes to particular
objects. That's not always very efficient, though.


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
G

Giles Bowkett

Hi --



Feeling squeamish about changing the core language isn't the sole
prerogative of recovering Java programmers :) There's still no good
answer to the question of how to do it safely and in a way that does
not risk conflicts, though there are various "better practices". My
favorite is "extend", which lets you confine changes to particular
objects. That's not always very efficient, though.

Actually that's my core concern with this kind of thing. Ruby isn't
the fastest language out there. Patching base classes could be pretty
bad for performance in some cases, although in practice it seems to
work out pretty well.

I just tried something:

Fixnum.class_eval do
def +(number)
self - number
end
end

It blew up my irb.
 
R

Raj Sahae

Giles said:
I just solved a problem by adding an instance method to String.

It makes for very elegant code elsewhere within the application.

Yet I can't shake the feeling I've done something dirty.

I think there needs to be a group like "Recovering Java Programmers
Anonymous" or somehting.
I've added instance methods to both String and Array. I didn't realize
that I should feel dirty about it.

Raj
 
C

Chad Perrin

I've added instance methods to both String and Array. I didn't realize
that I should feel dirty about it.

I guess you weren't raised Catholic^H^H^H^H^H^H^H^Hon Java.
 
R

Rob Sanheim

I just solved a problem by adding an instance method to String.

It makes for very elegant code elsewhere within the application.

Yet I can't shake the feeling I've done something dirty.

I think there needs to be a group like "Recovering Java Programmers
Anonymous" or somehting.

I worked in Java for 5+ years, and am coming up on my one year
anniversary on working in Ruby professionally. I *still* think adding
methods to core classes should be done very selectively, and only in
cases where that method is truly useful in a wide variety of contexts.
So if adding "foo" to String just makes my code cleaner in one spot
where its called, but I know I probably won't use "foo" anywhere else,
then I'll go with the less elegant solution of a helper method instead
of extending a builtin.

Note that I"m speaking of a larger Rails project with multiple
distributed developers - if its just you and a small side project then
hack up the core classes all you want. =)

- Rob
 
J

James Edward Gray II

I worked in Java for 5+ years, and am coming up on my one year
anniversary on working in Ruby professionally. I *still* think adding
methods to core classes should be done very selectively, and only in
cases where that method is truly useful in a wide variety of contexts.
...and...

Note that I"m speaking of a larger Rails project with multiple
distributed developers - if its just you and a small side project then
hack up the core classes all you want. =)

There's quite a bit of irony here my friend. Rails ads a good size
number of methods to the core Ruby classes. ;)

James Edward Gray II
 
D

David A. Black

Hi --

I've added instance methods to both String and Array. I didn't realize
that I should feel dirty about it.

You shouldn't -- you should just be aware of the potential problems.
Mainly it's the danger of naming conflicts, if we all start
distributing code that adds methods to the core. It's purely a
technical issue, though it's often characterized in quasi-moral terms
("great responsibilty") or stigmatized ("monkey-patching"). My
observation is that these characterizations are usually a prelude to
doing it anyway, even though they don't address the technical problems
directly.

As soon as someone figures out a way to make it work, all will be well
:) Actually the libraries that allow block-scoped core changes have
never been widely used, as far as I can tell.


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
R

Rob Sanheim

There's quite a bit of irony here my friend. Rails ads a good size
number of methods to the core Ruby classes. ;)

James Edward Gray II

True - and some of the things ActiveSupport adds are things I wouldn't
add myself, had I built Rails :). I think most of the extensions make
sense, given the type of applications Rails are targeting.

- Rob
 
J

James Edward Gray II

True - and some of the things ActiveSupport adds are things I wouldn't
add myself, had I built Rails :). I think most of the extensions make
sense, given the type of applications Rails are targeting.

Exactly. So you are more in favor of the process than you thought. ;)

James Edward Gray II
 
J

Jos Backus

Hi David,

On Thu, Mar 15, 2007 at 11:27:43AM +0900, David A. Black wrote:
[snip]
As soon as someone figures out a way to make it work, all will be well
:) Actually the libraries that allow block-scoped core changes have
never been widely used, as far as I can tell.

Are you referring to libraries like `scope-in-state'? AfaIac, these don't do
what I want them to do, which is the ability to restrict changes made to some
class while executing code in some other class. This is probably expensive as
hell and non-trivial to implement. But it sure would be great to have as it
would make Ruby a "safer" language.

Imagine if you could add String#foo to be visible within class Bar only
without having to subclass String and use it inside of Bar instead of String
(arguably the cleanest way to make this work otherwise).

Cheers,
 
M

Michael Schuerig

As soon as someone figures out a way to make it work, all will be
well

:)  Actually the libraries that allow block-scoped core changes have

never been widely used, as far as I can tell.

I seem to remember behavio(u)rs and fluid-let...

Michael
 
R

Rick DeNatale

I just tried something:

Fixnum.class_eval do
def +(number)
self - number
end
end

It blew up my irb.

With great power comes great responsibility!

It also might be the case in some future implementations that such a
patch might be ineffective, or intermittently effective.

For example, some Smalltalk implementations, would handle a + b under
the covers as an integer add of the two object pointers and a little
adjustment, followed by a quick check to see if the result were a
fixnum, and only send the :+ message if it wasn't. This check can be
done relatively inexpensively due to the way in which references to
FixNums (or SmallIntegers as Smalltalk called them) are encoded.

If a number x is in FixNum range then it's encoded as a 'pointer' with
the binary value x*2+1 This means that, if the low-order bit set it's
a Fixnum otherwise it isn't. So if we have two object pointers xp and
yp, referring to the objects x and y respectively we can implement x +
y as something like:

result = xp + yp - 1;
if (arithmetic overflow || !(result && 1) ) {
result = send(xp,:+,yp)
}

Note that, if xp and yp refer to Fixnums:

xp + yp - 1
= (x*2+1)+(y*2+1) - 1
= x*2 + y*2 + 1
= (x+y)*2 + 1

which is the correct representation for the FixNum x+y

I don't know whether YARV already does this or might in the future.
This is one of the things which VM implementers tend to look for, they
'cheat' and try not to get caught in the interest of performance.

Now Matz and his team might reject some of these tricks since the
dynamic nature of ruby might make it harder not to be caught, but in
some of these edge cases, I think that case can be made that it's
probably OK since it's unlikely that anyone will actually redefine
basic arithmetic operations on core classes such as FixNum and live to
tell about it without blowing up irb or worse.
 
G

Giles Bowkett

With great power comes great responsibility!

Two quick things about that: first, I tried it in another irb, and it
worked. This gave me great pleasure, although it was definitely a
guilty pleasure. For instance:
=> -1

What blew it up wasn't irb itself, but Wirble, which I use for syntax
coloring, tab completion, etc.

The other thing is, the great power/great responsibility thing, I
blogged about that a little while back, the "Ruby Is Spider-Man"
theory:

http://gilesbowkett.blogspot.com/2007/03/what-superhero-is-your-programming.html

Really though I'm growing more comfortable with the "patch everything"
philosophy. I see more problems coming from standard OO design issues
than I do from the "extra" power in Ruby.
 
L

Luciano Ramalho

Really though I'm growing more comfortable with the "patch everything"
philosophy.

I'll borrow a quote from Alan Runyan (one of the creators of Plone),
which applies to Python and but even more so to Ruby:

"Ruby is a language for consenting adults."

[ ]s
Luciano
 
T

Trans

Hi David,

On Thu, Mar 15, 2007 at 11:27:43AM +0900, David A. Black wrote:

[snip]
As soon as someone figures out a way to make it work, all will be well
:) Actually the libraries that allow block-scoped core changes have
never been widely used, as far as I can tell.

Are you referring to libraries like `scope-in-state'? AfaIac, these don't do
what I want them to do, which is the ability to restrict changes made to some
class while executing code in some other class. This is probably expensive as
hell and non-trivial to implement. But it sure would be great to have as it
would make Ruby a "safer" language.

Imagine if you could add String#foo to be visible within class Bar only
without having to subclass String and use it inside of Bar instead of String
(arguably the cleanest way to make this work otherwise).

I wrote an experimental lib a while back that let you sub in your
class for another within the scope of another module/or class. I think
it's a pretty easy way to work with this concept. Far-sight better
than the clock based approaches, IMO. The central idea is:

class InBlanket

# This would have a even nicer DSL.

String = Class.new:):String) do
def to_s; "(" + super + ")"
end

def tryme
puts "three pigs"
end

end

InBlanket.new.tryme #=> "(three pigs)

This could actually be made to work fairly easily *if* literals used
the ::new methods. Unfortunately they don't. So one would have to do
back and use String.new instead of "". I consider this a design flaw
in Ruby current implementation, though I'm sure there are
justifications for it (probably execution speed related).

In any case, I think the above is about the most elegant approach
possible for selector namespaces. Every other notation I've seen
deters me due to the spaghetti it makes of the code.

T.
 
P

Pit Capitain

Jos said:
Are you referring to libraries like `scope-in-state'? AfaIac, these don't do
what I want them to do, which is the ability to restrict changes made to some
class while executing code in some other class. This is probably expensive as
hell and non-trivial to implement. But it sure would be great to have as it
would make Ruby a "safer" language.

Jos, we've talked about this two months ago on ruby-core. Maybe it's
only the syntax of libraries like "import-module" which you don't like,
but I think they can do what you want. See below.
Imagine if you could add String#foo to be visible within class Bar only
without having to subclass String and use it inside of Bar instead of String
(arguably the cleanest way to make this work otherwise).

Changing the interface of import-module to something like Tom's example,
here's your use case:

require "import-module-extended"

class Bar

extending(String) do
def foo
"the foo version of #{self.inspect}"
end
end

def initialize(name)
@name = name
end

def hello(name = nil)
name ||= @name
puts(name.foo)
end

end

b = Bar.new("Jos")
b.hello # => the foo version of "Jos"
b.hello("Pit") # => the foo version of "Pit"

b.hello(1) rescue puts "no Fixnum#foo" # => no Fixnum#foo
"Pit".foo rescue puts "no String#foo" # => no String#foo

You can see that #foo is added to String but is only visible within
class Bar. (The implementation is a quick and dirty hack, though.)

Regards,
Pit
 
P

Pit Capitain

Trans said:
I wrote an experimental lib a while back that let you sub in your
class for another within the scope of another module/or class. I think
it's a pretty easy way to work with this concept. Far-sight better
than the clock based approaches, IMO. The central idea is:

class InBlanket

# This would have a even nicer DSL.

String = Class.new:):String) do
def to_s; "(" + super + ")"
end

def tryme
puts "three pigs"
end

end

InBlanket.new.tryme #=> "(three pigs)

This could actually be made to work fairly easily *if* literals used
the ::new methods. Unfortunately they don't. So one would have to do
back and use String.new instead of "". I consider this a design flaw
in Ruby current implementation, though I'm sure there are
justifications for it (probably execution speed related).

Tom, what would happen in your implementation if I pass a string from
the outside to tryme? And what would happen if I use a string created in
InBlanket outside of the class?

Regards,
Pit
 
T

Trans

Jos, we've talked about this two months ago on ruby-core. Maybe it's
only the syntax of libraries like "import-module" which you don't like,
but I think they can do what you want. See below.


Changing the interface of import-module to something like Tom's example,
here's your use case:

require "import-module-extended"

class Bar

extending(String) do
def foo
"the foo version of #{self.inspect}"
end
end

def initialize(name)
@name = name
end

def hello(name = nil)
name ||= @name
puts(name.foo)
end

end

b = Bar.new("Jos")
b.hello # => the foo version of "Jos"
b.hello("Pit") # => the foo version of "Pit"

b.hello(1) rescue puts "no Fixnum#foo" # => no Fixnum#foo
"Pit".foo rescue puts "no String#foo" # => no String#foo

I stand corrected. This is a fine interface.

T.
 
T

Trans

Trans schrieb:









Tom, what would happen in your implementation if I pass a string from
the outside to tryme? And what would happen if I use a string created in
InBlanket outside of the class?

Good point, Pit. The String defined outside would not be effected.
Most data comes from the outside, so there's really no point to this
unless the incoming data is recast into the extended version
automatically (or manually?).

T.
 

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

Forum statistics

Threads
474,237
Messages
2,571,190
Members
47,827
Latest member
wyton

Latest Threads

Top