[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

#send and private methods

Brian Palmer

12/29/2004 9:22:00 PM

I apologize if this has been discussed before and I missed it...

What's the rationale for having #send bypass the private status of a
method? For instance:

irb(main):001:0> b = Object.new
=> #<Object:0x2ae06d8>
irb(main):002:0> b.gets
NoMethodError: private method `gets' called for #<Object:0x2ae06d8>
from (irb):2
irb(main):003:0> b.send("gets")
no!
=> "no!\n"
irb(main):004:0>

In my mind, #send should honor the visibility of a method and only allow
calls to private methods if called like

self.send("gets")

But maybe there's a good reason for this behavior that I'm simply missing.

- Brian Palmer


11 Answers

David Heinemeier Hansson

12/30/2004 1:36:00 AM

0

> What's the rationale for having #send bypass the private status of a
> method?

That it's endlessly useful to be able to mindfully sidestep the rules
when the pressures are right. I use this to much delight in various
places in Rails where I'm well aware of the fact that the call is to a
private, but I choose to anyway.

To me, that's the essence of Ruby. Guidance, not constraints. Make it
ugly instead of impossible.

Three cheers for a send that sidesteps visibility ;)!
--
David Heinemeier Hansson,
http://www.basec... -- Web-based Project Management
http://www.rubyon... -- Web-application framework for Ruby
http://macro... -- TextMate: Code and markup editor (OS X)
http://www.loudthi... -- Broadcasting Brain



James Britt

12/30/2004 1:42:00 AM

0

Brian Palmer wrote:
>
> In my mind, #send should honor the visibility of a method and only allow
> calls to private methods if called like
>
> self.send("gets")
>
> But maybe there's a good reason for this behavior that I'm simply missing.

Off the top of my head:

Ruby is (almost) pure OO if that is what you want. And it behaves that
way so long as you follow the OO conventions. And most Ruby developers do.

However, it provides numerous hooks and inlets such that you can pretty
much do as you like; the proverbial enough rope to shoot yourself in the
foot.

Why might this be? I would posit that programming languages exist to
aid the developer, not the computer, and so should be enabling, not
prohibitive.

OOP|A|D is/are a set of design and development guidelines, not the Ten
Commandments, so a language should not dictate, merely encourage. (Maybe
even strongly encourage.)

Any reasonably powerful language provides the wherewithal to write
difficult, ugly, unreadable, unmaintainable, dangerous code. What stops
most people from doing so is typically not the constraints of a
language, but common sense and self-preservation.

Using something like 'send' on private methods is similar to off-road
driving. You can deliberately choose to step outside the boundaries, but
you have to be responsible for your actions.

Ruby treats you like an adult.


James


Marc Merlin

12/30/2004 4:25:00 AM

0

I find that my sympathies lie with the original poster (Brain Palmer) with
regard to Object#send bypassing conventional access controls, although I do
agree that the rationales presented by the other responders are more than
likely historically accurate.

What I mean is this. Private access designation, although an artifact of OO
methodology, serves a much broader role. It is a mechanism for allowing a
programmer to organize his or her implementation while explicitly noting
that certain details of the implementation are subject to change. It is
way of saying that such details should not be relied upon by clients of the
implementation. Why? To put it simply, it is a way of saying "these
things may behave differently or be entirely absent in future releases of
this component."

Although I agree that programming languages should be "enabling" in that
they should promote creative problem-solving and although I agree that
"adult" programmers accept the responsibility of bypassing the constraints
imposed upon them by the structure of the programming languages that they
use, the fact of the matter is that it is not necessarily the creative and
mature programmer who suffers the consequences of having taken such
liberties with access control. Sadly, other programmers - and ultimately
end users - who rely on the integrity of complex software systems to
continue to function properly in spite of ongoing revision are the ones
likely to be injured when access controls are disregarded.

In other words, information hiding is a way of making software systems
resilient in the face of change. It is not simply a way "nanny"
programming languages try to keep "top-gun" programmers from getting their
job done. Of course, for small projects with a restricted user base these
constraints hardly matter. If Ruby were intended to be only a prototyping
tool, then bypassing access controls would be no big deal. But Ruby
apparently is on the way to being adopted as a bona fide
progamming-language-in-the-large, as evidenced by such trailblazing
projects as the Rails framework. (I certainly hope so!) Consequently I
think that the adherence to a rigorous informtation hiding schema should be
taken seriously and mechanisms to bypass it should be brought into
question.

- Marc Merlin

Glenn Parker

12/30/2004 5:36:00 AM

0

Marc Merlin wrote:
> In other words, information hiding is a way of making software systems
> resilient in the face of change. It is not simply a way "nanny"
> programming languages try to keep "top-gun" programmers from getting their
> job done.

Except that, if a private function is irrevocably private, it actually
*does* occasionally keep top-gun programmers from getting their job
done. Information hiding is a good thing, but absolute enforcement of
information hiding is not a good thing, at least in Ruby. This probably
precludes Ruby from certain applications.

The normal Ruby syntax enforces information hiding as you would expect,
while using "send" is ugly and it stands out clearly. People that use
"send" to subvert information hiding are fully aware that they are
breaking the rules and introducing an ugly dependency on a hidden API
into their system. Sometimes that is the right tradeoff for a good
engineer to make.

Why should the creator of the "private" API be the one to decide, once
and for all, that this is unacceptable? The creator cannot anticipate
every situation that will occur in the future, they can only designate
areas of relative instability using "private" and "protected".

--
Glenn Parker | glenn.parker-AT-comcast.net | <http://www.tetrafoi...


James Britt

12/30/2004 5:44:00 AM

0

Marc Merlin wrote:
> ...
> In other words, information hiding is a way of making software systems
> resilient in the face of change. It is not simply a way "nanny"
> programming languages try to keep "top-gun" programmers from getting their
> job done. Of course, for small projects with a restricted user base these
> constraints hardly matter. If Ruby were intended to be only a prototyping
> tool, then bypassing access controls would be no big deal. But Ruby
> apparently is on the way to being adopted as a bona fide
> progamming-language-in-the-large, as evidenced by such trailblazing
> projects as the Rails framework. (I certainly hope so!) Consequently I
> think that the adherence to a rigorous informtation hiding schema should be
> taken seriously and mechanisms to bypass it should be brought into
> question.

I agree; any mechanism for by-passing orderly program control should be
viewed with suspicion. But such things need to be there, as the
programmer must always be the ultimate arbiter.

Every modern language has such mechanisms; they are either explicit or
buried, but they are there. Better they be made clear and well
documented so all involved can make informed choices.

Even if strict data hiding was enforced there would be countless other
ways to write poor code; I (sadly) have too much have proof of this.
The end-user must always rely on the judgment of the developer, in any
language.

I haven't looked at all that much of the Rails source code, so I don't
know the they answer, but here's something to ponder: Were strict data
+ method access enforced, would Rails be (as) feasible?

Further, C is certainly well accepted as a mainstream language, and it
allows for far more easier tomfoolery than does Ruby.

I don't view OO and data hiding as a way to hamstring advanced
developers, but I do believe some languages gain traction precisely
because their built-in constraints serve as an effective form of mob
control for projects involving large numbers of inexperienced (and less
expensive) developers who might otherwise run amok.

I believe, too, that it can be argued that these constraints lead to
overly complex code that ages poorly compared to applications built
using agile languages. While some might have a warm fuzzy feeling
during production, you end up with code less resilient to change.


James

P.S.

I suspect concerns over malleable object boundaries are nearly identical
to concerns over dynamic typing. Proper use of unit testing will do a
lot more for ensuring robust code than either strict encapsulation or
typing.


Brian Palmer

12/30/2004 5:53:00 AM

0

I certainly see what you mean--I can think of cases where it would be
useful to bypass the private status of a method, as well. I was curious
how others felt, though. The reason I ran across the #send behavior is
this--I've been toying with different ways of writing a Infocom-style
text adventure interpreter in Ruby, and one idea I had was a 'command'
object or module that would contain a bunch of methods named after
verbs, such as 'look' and 'west'. Then I would just have the
interpreter do a #send with whatever the user typed in, and let
#method_missing handle the "I don't understand what you mean" type
messages. I was just a bit shocked when I discovered that typing 'gets'
into my interpreter actually called the private method 'gets' (and I
started thinking about what the adventurous adventurer could do with a
well-designed #instance_eval command :)

But of course, this is easily avoided by checking whether #send would
call a public method, or using the awesome "evil ruby" to make a class
without Object and Kernel methods. Not to mention that, while an
interesting idea, it's not necessarily the most elegant way to do things
anyway.

Thanks for the feedback!

- Brian Palmer

David Heinemeier Hansson wrote:

>> What's the rationale for having #send bypass the private status of a
>> method?
>
>
> That it's endlessly useful to be able to mindfully sidestep the rules
> when the pressures are right. I use this to much delight in various
> places in Rails where I'm well aware of the fact that the call is to a
> private, but I choose to anyway.
>
> To me, that's the essence of Ruby. Guidance, not constraints. Make it
> ugly instead of impossible.
>
> Three cheers for a send that sidesteps visibility ;)!
> --
> David Heinemeier Hansson,
> http://www.basec... -- Web-based Project Management
> http://www.rubyon... -- Web-application framework for Ruby
> http://macro... -- TextMate: Code and markup editor (OS X)
> http://www.loudthi... -- Broadcasting Brain
>
>
>
>
>


Marc Merlin

12/30/2004 6:50:00 AM

0

I agree with Glenn Parker that denying access to private methods will
unquestionably prevent programmers from accomplishing tasks that they
otherwise could, and I agree that Ruby programmers for the most part are
aware that they are breaking the rules by bypassing information hiding.
(The same could be said of denying access to object attributes, as well.)
I also agree that the creator of an implementation cannot anticipate all its
possible future uses.

By designating a method as a private the implementor is saying that its
appearance in the code is in some way incidental. To some extent that is
his or her call to make, although admittedly it may be wrong. I'm not
saying that this is the implementor's right in any proprietary way, I'm
saying that as a matter of fact the implementor has decided that some
behavior for the component that will be exhibited publically - the type, so
to speak - and the rest of its functioning will be hidden.

The motivation for hiding this functioning is actually an admission not of
omniscience but of uncertainty on the part of the implementor - uncertainty
whether the private methods will be retained in future versions of the
component or whether they will function identically if they are retained.
The same is true with any successor implementors who strive to preserve the
type of the software component.

Now I think it's all well and good if a Ruby programmer aware of the risks
undertakes to violate access restrictions in order to accomplish an
otherwise impossible (or exceedingly difficult) task. The rules of
informed consent would seem to apply and the chips would seemingly fall
where they may.

The problem occurs when the aware access-control dodger in turn creates an
software artifact used by other people. They themselves are unaware of the
fragility that has been introduced into software system upon which they are
relying. A "private" change in a component invisible to them - a change
not affecting the proper functioning of that component in the least - could
blow them out of the water.

[Perhaps this problem could be addressed by introducing another dimension of
"tainting" to indicate other forms of software risk. That's a topic for
another thread.]

In summary, implementors publish types. They and their successors retain
the perogative to change their implementations consistent with such types,
otherwise they have simply introduced a new type. The people placed at
risk by the subversion of the "type boundary" are not necessarily the
first-order clients of the software component, but others, maybe several
layers removed, who incorporate the subverting components into their
software creations.

I should be clear. Object#send isn't a big deal for me. Someone raised an
issue and I wanted to share my concerns. I have the utmost respect for the
Ruby community and am sympathetic to points on both sides of this question.

Cheers,
Marc Merlin




Eric Hodel

12/30/2004 8:18:00 AM

0


On 29 Dec 2004, at 20:26, Marc Merlin wrote:

> Consequently I
> think that the adherence to a rigorous informtation hiding schema
> should be
> taken seriously and mechanisms to bypass it should be brought into
> question.

Ruby is too dynamic to do this:

$ ruby
class X; private; def a; puts 5; end; end
i = X.new
begin i.a; rescue Exception => e then puts e.class end
class X; public :a; end
i.a
NoMethodError
5

But then, there are ways to exploit it to not allow these things to
happen, like redefining send and __send__ and method and running with
$SAFE = 4

--
Eric Hodel - drbrain@segment7.net - http://se...
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

Michael Neumann

12/30/2004 11:58:00 AM

0

Marc Merlin wrote:
> [...]
> In other words, information hiding is a way of making software systems
> resilient in the face of change. It is not simply a way "nanny"
> programming languages try to keep "top-gun" programmers from getting their
> job done. Of course, for small projects with a restricted user base these
> constraints hardly matter. If Ruby were intended to be only a prototyping
> tool, then bypassing access controls would be no big deal. But Ruby
> apparently is on the way to being adopted as a bona fide
> progamming-language-in-the-large, as evidenced by such trailblazing
> projects as the Rails framework. (I certainly hope so!) Consequently I
> think that the adherence to a rigorous informtation hiding schema should be
> taken seriously and mechanisms to bypass it should be brought into
> question.

I think #send should not be able to call private methods (from a proper
OO perspective, not neccessarily mine ;-), but it should be able to call
protected methods. Nevertheless, I think deciding whether a method
should be declared as protected or private is not always easy, and I
would not matter if there were only "protected" methods in Ruby (which
means that they have to be called with "self" as receiver, regardless of
the class they were defined). Of course there are for sure many
situations where you'd want to have private methods.


What you want is probably this:

class Object
def send(id, *args, &block)
if private_methods.include?(id.to_s)
raise "private method `#{ id } called for #{ self }"
end
__send__(id, *args, &block)
end
end

That's the good thing with Ruby. Without the current behaviour of
__send__ (or send), you could not implement what currently is possible
(call private methods).

Regards,

Michael


T. Onoma

12/30/2004 3:09:00 PM

0

It goes both ways, you need a solid foundation to build on, agreed, but you
also need flexibility across the board. What separates private? Is it a
road-block or a sign-post? What's a sign-post going to do? Well, it will
_deter_. If that is a goal then perhaps a 'deter' directive is in order. On
the otherhand, if there are things necessarily private, does it indicate a
flaw in design? Is it rightful for Classes to have local namespace?

Or you could just say screw it and just reduce them to what they are, a Scope.
A Scope is just a callable container, a scope can be public, private,
protected, or deterred (and passive vs. active, see aop). Any one up for a

public class String
end

%String{This is a new string}

or,

String("This is a new string")

or Classicaly,

String.new("This is a new string")

I believe it is things like this that Rails is teaching, but David has better
words... http://rubyo...

T.

P.S.
Hmm... I wonder how fast Ruby method lookup would be if it was in a Database.
Then I could access through a marginally distinct map of nomenclature.