RCR 303: nil should accept missing methods and return nil

H

Hal Fulton

Joel said:
(Apologies to Eric Idle.)

Now there's a role model. I'd love to be like him,
just on the other side of the Atlantic. A sort of
American Idle.


Hal
 
A

Austin Ziegler

Ah, but _would_ it. On any other object I would agree with you.
But nil occupies a special place in the ruby world.

Not really.
nil means in effect, no thing is here.

And, if I'm expecting that some thing will be there, then I have an
error condition. Putting your nileater mechanism in place will
simply *mask* that I have a very real and very serious error. It
will also cause me to output incorrect information to my PDF files.
What happens if I tell no thing to do something.
No thing happens and I'm left with no thing.

You may, however, be left with incorrect output.

"\n#{@oid} 0 obj\n<</Type /Page /Parent #{@owner.oid} 0 R >>"

If @owner is nil, then using your proposal I will get:

234 0 obj
<</Type /Page /Parent 0 R >>

This is INCORRECT. This will, AT BEST, cause the page you're looking
at in a PDF reader to be partially or completely not rendered.
Equally likely, it will cause your PDF reader to crash. (Object
references are ALWAYS "id version R" in PDF.)

However, since I know that @owner should never be nil, when that
line blows up during execution, I can start tracing it from the blow
up. I can find out where and why @owner becomes nil and I can fix
it. Without the fatal error, I am left with a mysteriously crashing
PDF document -- and almost no debugging possible from a PDF reader.

This is real code, John. It's a bit simplified from the real case
(the real case is about 20 lines long and contains a lot more).
Indeed, I don't even need to go that far:

arr.empty?

This doesn't return +true+ if arr is +nil+. This means that
carefully crafted logic will, in fact, break.

(It also ignores the fact that nil *can* be a useful value.)
Is your assertion correct?

Yes, John, it's correct. I know this without taking your challenge.
Do you actually generate incorrect PDF? (In which case you are right)

Yes, I'm right. I know this without taking your challenge.
Does it just plain work? (In which case I am right)

No, it doesn't. Never has, never will. I know this without taking
your challenge.
Or does it blow up with an exception or error report somewhen
else? (I which case I'm still right.)

You're not right. I know this without taking your challenge.
I'm better a beer (to be collected in Christchurch New Zealand)
that practical software will find that...
a) Mostly it will just do the right thing.

This will be true so long as the program does not interact with
other programs that require strict conformance to output (or input!)
formats.
b) Otherwise it will do the right thing for now, and be caught later as an
Exception or error report. (I suspect by adding a few more things like
class NilClass
def respond_to?( sym)
true
end
end
may cure some of those...

Not in the case where you have to have strict conformance to data
formats. Not when you're dealing with numbers. What is 2 + nil? Is
it 2 or nil?
c) And very rarely, far more rarely than people suspect, will it
silently do the wrong thing.

Far more often.
Also as you go along, consider cases where you could just drop
conditionals. How many if branches would you be quietly dropping?

Well, strictly speaking, I *don't* have a branch now. I simply
assume that @owner is set and is set properly. If it isn't, I have a
serious error -- and no amount of pretending that this will work
will change that incorrect data will be output.
As for debugging, if you look at the RCR, the implementation I
give creates state on nil that records which method was missing
and in which calling context.

Still won't help with the case where the error is in the generated
code (that is quite large and hard to read) -- it's very
disconnected from the real error.

-austin
 
B

Bill Kelly

From: "John Carter said:
Prove me wrong, take the challenge.

Next time your pet program dies of a NoMethodError for NilClass add

class NilClass
def method_missing(sym,*args)
nil
end
end

...at the head of your program.

See empirically what happens. Does it hide bugs? Does it just work? Does
it still throw an exception or give an error message?

A CGI script I coded a couple weeks ago just got:

[Fri May 06 20:22:37 PDT 2005] Process 27695 encountered
#<NoMethodError: undefined method `[]' for nil:NilClass>
at /var/www/---------.net/www/quake/servers/list.cgi:133:in `top_client'

and indeed, when I added method_missing to NilClass the bug
was masked and the script executed without complaint.

Interesting . . .

The bug was something in an as-yet untested "else" case
in the code. Something a unit test should have caught,
if i'd written any for this quick hack..... which I didn't.

I would count myself in the "fail as early as possible"
camp. I still don't like that NilClass#method_missing
masks the bug... but it did prevent the error in this
case from the user's point of view.

If I were to consider adding NilClass#method_missing
permanently to my script(s), I would at least cause it
to write the problem and stack trace to a logfile, so
that I could periodically scan the logfile for any such
incidents.

Interesting challenge though, thanks ... :)


Regards,

Bill
 
R

Robert Klemme

Cyent said:
Think of an array with 42 elements. Access element 55. It returns nil. How
is this different?

It's not different. But it's not an argument in favor of your suggestion
either.
If you think through the default values for arithmetic operations on nil
properly. My guess is the results in most case will be right.(How often do
you initialize an array to 55? 99.9% of the time you expect it to be full
of 0 or maybe 1.

How about nil autoconverts to the identity element for all operations. ie.
0 for +, 1 for *, "" for string concat.

Not feasible since you would need a custom version of #coerce also and in
that method you don't know the operator (+ or * for example) - at least not
with standard means. And I would not like to see these perlish dirty tricks
in ruby.
In which case more code simplification, more code that just works.

If you would drop this "code that just works" maybe I could find something
useful in your suggestion. I'm sorry, but I get the impression that you
don't really seem to overlook the effects of your suggestion.
Is it?

I wwnt grepping through ruby 1.9 CVS (ok its a month or two old version)
looking for some code that will break.

I don't believe _anything_ will (currently) break.

But you should accept that others believe different.

robert
 
D

Dan Doel

I certainly don't use Ruby in large business applications (just my personal
web site and various programming assignments for my courses at university)...

However, I must say that any time I've gotten a NoMethodError on nil, it's
been an actual bug in my algorithm that I had to fix. Your proposal probably
would have made things significantly harder for me.

Perhaps my code isn't real-world enough, or I just write code differently than
you, but I doubt this would save me much time, and would probably cost some
in actuality. When I know that a method may be legitimately called with nil,
I can usually anticipate it and code around it, so these exceptions actually
signal real bugs in most of my code.

-- Dan Doel
 
R

Ryan Davis

If I prove wrong, I will gladly retract my RCR and apologise.

The burden of proof (that this is needed) must sit on you, not on us
(that this is not needed).

Either way, I still agree with Eric, there is no reason for this to
be an RCR since the whole thing is 5 simple lines of ruby that any
user can selectively use as they see fit. If someone wants to use it,
they require a very small file. If they don't want to use it, they
don't do anything special.
 
J

James Britt

Ryan said:
The burden of proof (that this is needed) must sit on you, not on us
(that this is not needed).

Either way, I still agree with Eric, there is no reason for this to be
an RCR since the whole thing is 5 simple lines of ruby that any user
can selectively use as they see fit. If someone wants to use it, they
require a very small file. If they don't want to use it, they don't do
anything special.

Yes, and the nil.blackhole switch is a very handy idea; one could turn
this on and off for blocks of code where the alternative behavior seemed
useful.

But the general idea of nil silently consuming all messages turns duck
typing into duck hunting.


James
 
D

David A. Black

Hi --

The burden of proof (that this is needed) must sit on you, not on us (that
this is not needed).

Either way, I still agree with Eric, there is no reason for this to be an RCR
since the whole thing is 5 simple lines of ruby that any user can selectively
use as they see fit. If someone wants to use it, they require a very small
file. If they don't want to use it, they don't do anything special.

I'm a little puzzled by this (the idea that changes that can be
implemented in Ruby should not be RCRs), which I hadn't heard before
Eric's post, but which seems to be taking Seattle by storm :)

I don't think implementability-in-Ruby correlates with RCR
appropriateness at all. There are lots of accepted RCRs that can be
implemented in Ruby, and since the RCR process flows into Matz's work
on the language -- and much of what Matz does can be implemented in
Ruby -- it seems like an artificial barrier.

Also, there are serious pitfalls to falling back on the solution of
just changing core behavior one program or library at a time. We've
heard from several people, for example, who would not be happy if nil
started eating messages, so presumably if they used a library that
made that change to nil, they wouldn't like it.

This situation may change if there are selector namespaces or
something along those lines in Ruby 2.0, but for the moment I would
say that the RCR process is for all proposed changes to Ruby,
including those that can be implemented in Ruby.


David
 
D

David A. Black

Hi --

Yes, and the nil.blackhole switch is a very handy idea; one could turn this
on and off for blocks of code where the alternative behavior seemed useful.

I don't think it would be thread-safe, would it? (I'm thinking back
to the wall I hit when working on Ruby Behaviors....)


David
 
J

James Britt

David said:
Hi --

On Sat, 7 May 2005, James Britt wrote: ...



I don't think it would be thread-safe, would it? (I'm thinking back
to the wall I hit when working on Ruby Behaviors....)

Good point.


James
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: RCRs that can be implemented in Ruby (was: Re: Prove me Wrong! Re: RCR 303: nil should accept missing methods)"

|> Either way, I still agree with Eric, there is no reason for this to be an RCR
|> since the whole thing is 5 simple lines of ruby that any user can selectively
|> use as they see fit. If someone wants to use it, they require a very small
|> file. If they don't want to use it, they don't do anything special.
|
|I'm a little puzzled by this (the idea that changes that can be
|implemented in Ruby should not be RCRs), which I hadn't heard before
|Eric's post, but which seems to be taking Seattle by storm :)

There's no such official rule for RCRs; actually there's no official
rule for RCRs at all. But if the idea can be easily implemented in
pure Ruby, one can release it to the public before submitting an RCR
to prove its usefulness by real experiences. Besides that, if
everyone submit every idea popped in his mind, whole RCR process would
overflow. Eric (and Ryan) might think of such case.

matz.
 
D

David A. Black

Hi --

Hi,

In message "Re: RCRs that can be implemented in Ruby (was: Re: Prove me Wrong! Re: RCR 303: nil should accept missing methods)"

|> Either way, I still agree with Eric, there is no reason for this to be an RCR
|> since the whole thing is 5 simple lines of ruby that any user can selectively
|> use as they see fit. If someone wants to use it, they require a very small
|> file. If they don't want to use it, they don't do anything special.
|
|I'm a little puzzled by this (the idea that changes that can be
|implemented in Ruby should not be RCRs), which I hadn't heard before
|Eric's post, but which seems to be taking Seattle by storm :)

There's no such official rule for RCRs; actually there's no official
rule for RCRs at all. But if the idea can be easily implemented in
pure Ruby, one can release it to the public before submitting an RCR
to prove its usefulness by real experiences.

Yes, although there are cases (core changes, mostly) where people
might be reluctant to use it in production code.
Besides that, if
everyone submit every idea popped in his mind, whole RCR process would
overflow. Eric (and Ryan) might think of such case.

Oh, believe me, I'm not in favor of that :) That's why I like
RCRchive: it discourages casual RCRs, and the "I've used Ruby for five
minutes and here's what should be changed" type, by insisting on full
explanation and rationale and, where possible, implementation. (At
least it *tries* to discourage them.... :)


David
 
R

Ryan Leavengood

Joel said:
$ ruby missing_obj.rb
#<NoMethodError: undefined method `lookup' for nil:NilClass>
#<MissingDatabase: missing_obj.rb:26: No user input>

I really like this. I think it is worthy of being put into a library
(like maybe as part of Facets.) For now I'll just add it to my code library.

Thanks,
Ryan
 
R

Ryan Leavengood

Ryan said:
The burden of proof (that this is needed) must sit on you, not on us
(that this is not needed).

I agree. In fact, to quote
http://www.adamsmith.org/logicalfallacies/000656.php

"Shifting the burden of proof is a specialized form of the argumentum ad
ignorantiam. It consists of putting forward an assertion without
justification, on the basis that the audience must disprove it if it is
to be rejected. Normally we take it that the new position must have
supporting evidence or reason adduced in its favour by the person who
introduces it. When we are required instead to produce arguments against
it, he commits the fallacy of shifting the burden of proof."

I feel this discussion has been useful, and the NullObject pattern is
interesting, but I'm in the camp that this will do more harm than good.
To shift the burden of proof to us only tends to deflate your own
argument through logical fallacy.

Most useful programs interact with other outside programs, or of course
with a user. Using this pattern would cause silent errors that would
frustrate users and possibly crash other applications (as in Austin
Ziegler's PDF::Writer example.) If a user of some Rails application asks
to see some important bit of information, and gets nothing instead, due
to nil eating the "show_me_the_important_stuff" method, the user will
not be happy. And the developer will have to spend time figuring out why
the information was blank (which could be for any number of reasons
besides the nil-monster.)

Ryan
 
R

Ryan Leavengood

Cyent said:
Anybody who is vaguely worried about bugs in their code whether they are
running perl or ruby should _always_ use the -w switch.

So your argument is that because this particular error appeared when
using the -w switch, it is just fine that the NullObject pattern
completely hid it?

Sorry, I don't buy it. In fact, this has shades of a Straw Man to me.

How about this:

class NilClass
def method_missing(*ignore) nil end
end

$counters = []

class Counter
def initialize(name)
@name = name
@count = 1
end

attr_reader :count, :name

def increase
@count += 1
end

def ==(other)
@name = other.name
end
end

def counter_factory(name)
c = Counter.new(name)
if not $counters.include? c
$counters << c
c
end
end

class DoSomething
def initialize
@name = 'DS'
@counter = counter_factory(@name)
end

def count_done
@counter.count
end

def do_it
@counter.increase
if @counter.count > 5
@counter = counter_factory(@name)
end
end
end

ds = DoSomething.new
10.times do
ds.do_it
end
puts "Have done it #{ds.count_done} times."
__END__

Contrived? Maybe. But it doesn't have warnings and it doesn't work like
it should, all because of a small (and I think common) Ruby error that
is hidden by the NullObject pattern.

Ryan
 
B

Bill Atkins

------=_Part_5373_5200519.1115498739991
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

object =3D get_some_object # get_some_object returns nil
object.perform_action_of_great_importance # action never gets performed - n=
o=20
error

=20


I, for one, would rather see it throwing an error as it is now. Dead=20 programs
don't tell lies.
=20
I would suggest next 5 times you have a nil class throws
NoMethodError crash, just add this at the head of your program...
=20
class NilClass
def method_missing( sym, *args)
nil
end
end
=20
And see what happens.
=20
If, your program then works correctly, then I am right.
=20
If, your still program thows an exception or reports an error, but perhap= s
a slightly different point, then I am still right, namely this change
doesn't hide bugs.
=20
If the program silent proceeds to do the wrong thing, then I am wrong.
=20
I ask members of this list to actually try this experiment the next coupl= e
of times they hit a nil throws NoMethodError and send the results to me.
=20
I will summarize to the list.
=20
If I prove wrong, I will gladly retract my RCR and apologise.
=20
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand
=20
This rock is covered by a very very thin layer of life. (Think 6000km
of rock followed by a meter or so of biomass.)
=20
=20[/QUOTE]


--=20
Bill Atkins

------=_Part_5373_5200519.1115498739991--
 
J

Jason Sweat


Interesting quote, however for an alternative point of view consider
the scientific method, where people postulate hypothesis which can
never be proved true (only in general agreement with observed data)
but one single contradictory fact can invalidate the entire
hypothesis. I think this is more in alignment with John's baiting,
i.e. he has postulated this is not a problem in real world
circumstances. He has tried to come up with the code which counters
this himself and has not found it, so he is requesting the same of a
broader audience.

My $0.02 :)

Regards,
Jason
http://blog.casey-sweat.us/
 
J

John Carter

You may, however, be left with incorrect output.

"\n#{@oid} 0 obj\n<</Type /Page /Parent #{@owner.oid} 0 R >>"

If @owner is nil, then using your proposal I will get:

234 0 obj
<</Type /Page /Parent 0 R >>

Hmm. Interesting.

Hard real, live, data.

I like it.

Let's look at it a bit more closely.

How did the nil get into @owner? Would it have been picked up by -w?
Was it placed there yourself in the meaning "uninitialised" or in the
meaning "no thing here"? By what route did it arrive?

Given my new proposal of having an uninitialised vs nothing
types of nil?

Could we design things in a way that would resolve your
issue?




John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand


It originates from just one of the 6 billion yet vastly outnumbered
humans.

I trust you will keep this perspective and context in mind when
reacting to this email.
 
E

ES

Le 8/5/2005 said:
Hmm. Interesting.

Hard real, live, data.

I like it.

Let's look at it a bit more closely.

How did the nil get into @owner? Would it have been picked up by -w?
Was it placed there yourself in the meaning "uninitialised" or in the
meaning "no thing here"? By what route did it arrive?

Given my new proposal of having an uninitialised vs nothing
types of nil?

Could we design things in a way that would resolve your
issue?

Well, one could redesign so that nil is never returned from
any method at all for any purpose. While a valid suggestion
as such, it is sort of moot as far as the merits or your
original proposal go.
John Carter

E
 
A

Austin Ziegler

Hmm. Interesting.
Hard real, live, data.
I like it.
Let's look at it a bit more closely.
How did the nil get into @owner? Would it have been picked up by -w?
Was it placed there yourself in the meaning "uninitialised" or in the
meaning "no thing here"? By what route did it arrive?

In this *particular* case, it is usually a case of forgotten
initialization -- but -w only helps so much. But my point was more
illustrative than anything else. This could have happened with another
line of code than I picked, perhaps from malformed user input that was
improperly guarded against. Without an exception on an attempt to call
a method on nil, it would be difficult to trace this.
Given my new proposal of having an uninitialised vs nothing
types of nil?

I think that the discussion in relation to relational databases is an
informative one here. Originally, Codd specified multiple types of
NULL -- for much the same reason you have. The SQL standard combined
these into a single NULL. In later life, though, Codd and his
successors have determined that NULL values in database tables are bad
and represent a bad logical table design.

We already have a problem where sometimes we need #nil_or_empty? I
don't want to add a case of #uninitialised_or_nil_or_empty?

-austin
 

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

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,163
Latest member
Sasha15427
Top