RCR 303: nil should accept missing methods and return nil

J

John Carter

A very simple and generic way of improving the reliability of Ruby
programs is to implement the NullObject pattern by allowing nil to
accept all and every method instead of throwing a NoMethodError.

Not only does this simplify ruby programs considerably, it also
changes certain crash bugs silently into correct programs.

If you were to investigate most cases where an if statement checks to
see if a value is non-nil, you will find that the else clause is
empty. ie. Does nothing.

I expect if this is accepted to see a bunch of knock on RCR's, namely
nil+n == n
nil * 7 == 0
-nil == 0

We already have
nil.to_s == ""
nil.to_i == 0
nil.to_f == 0.0

Doing an informal subsampling of the standard libs, I can see no
instances where this change will cause a problem, several cases where
this change could substantially simplify code, and several cases where
this change would result in The Right Thing happening instead of a
crash if the routine was fed a nil.


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


Somewhere on the edge of a Galaxy, one of literally billions of such
galaxies, is a sun, one of literally billions of suns in that
galaxy.

Orbiting that sun is a small rock 330000 times smaller than that
sun.

This rock is covered by a very very thin scum of life. (Think 6000km
of rock followed by a meter or so of biomass.)

Amongst the millions of species in that scum are many hundreds of
thousands of types beetle and a mere handful of primates.

Surprisingly enough, this email does not originate from a beetle.

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

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

Luke Graham

I expect if this is accepted to see a bunch of knock on RCR's, namely
nil+n == n
nil * 7 == 0
-nil == 0

We already have
nil.to_s == ""
nil.to_i == 0
nil.to_f == 0.0

This would be a big change to behaviour and a lot of existing programs
could have problems with it. Because integers already have a to_i(),
its possible to do this...

x.to_i + 3

and have it do what you want.
 
H

Hal Fulton

Luke said:
This would be a big change to behaviour and a lot of existing programs
could have problems with it. Because integers already have a to_i(),
its possible to do this...

x.to_i + 3

and have it do what you want.

I'll comment also.

First of all, I sympathize on some level with this RCR. It has
been thought of before, by me and others.

In fact, I *think* that this was Ruby's default behavior once upon
a time; and I think it was changed for a good reason(s) I don't recall.

In other words, I think it falls under the category of "things that
were already in and have been taken out" (like Object#to_a, and like
the 'end' suffixes -- end if, end while, etc., which were taken out
when modifiers like 'x if y' were added).

FWIW, if x has a to_int, even this should work:

y = 3 + x

although

y = x + 3

wouldn't. But that is neither here nor there. And if something is
neither here nor there, where is it??


Hal
 
J

John Carter

I, for one, would rather see it throwing an error as it is now. Dead programs
don't tell lies.

I would suggest next 5 times you have a nil class throws
NoMethodError crash, just add this at the head of your program...

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

And see what happens.

If, your program then works correctly, then I am right.

If, your still program thows an exception or reports an error, but perhaps
a slightly different point, then I am still right, namely this change
doesn't hide bugs.

If the program silent proceeds to do the wrong thing, then I am wrong.

I ask members of this list to actually try this experiment the next couple
of times they hit a nil throws NoMethodError and send the results to me.

I will summarize to the list.

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

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


This rock is covered by a very very thin layer of life. (Think 6000km
of rock followed by a meter or so of biomass.)
 
J

John Carter

This would be a big change to behaviour and a lot of existing programs
could have problems with it. Because integers already have a to_i(),
its possible to do this...

If there were a lot of programs deliberately doing

begin
# some complex calculation
rescue NoMethodError => details
# Do some valid functionality, not just error handling
end

I would agree with out.

If they are not doing that, then the program would
just crash totally. My change would change the behaviour from crashing, to
Doing The Right thing.

But I don't believe that there are programs rescuing NoMethodErrors/

I bet you could search they whole RAA and find very very little that has
rescue NoMethodError.


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

This rock is covered by a very very thin scum of life. (Think 6000km
of rock followed by a meter or so of biomass.)

Amongst the millions of species in that scum are many hundreds of
thousands of types beetle and a mere handful of primates.
 
A

Aredridel

If they are not doing that, then the program would
just crash totally. My change would change the behaviour from crashing, to
Doing The Right thing.

Only if the right thing is "do not crash ever".

For me, if a bug like that is exposed, _it's a severe error_. Not
something to say is nil.
 
E

Eric Hodel

I would suggest next 5 times you have a nil class throws NoMethodError
crash, just add this at the head of your program...

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

I think that any RCR than can be implemented in pure ruby doesn't need
to go into the core. You can place a package on RAA and get a project
on RubyForge for these kinds of things handily.
 
J

John Carter

First of all, I sympathize on some level with this RCR. It has
been thought of before, by me and others.

In fact, I *think* that this was Ruby's default behavior once upon
a time; and I think it was changed for a good reason(s) I don't recall.

Be careful about exactly what is being asked for and what has been done
before. I once developed an OOP system were all objects would respond to
all undefined messages by doing nothing. I rapidly changed that to an
error condition as in that case the complaint that it hid bugs was valid.

What I'm asking for here is that just the nil object, not even the false
object, responds to all undefined methods by doing nothing and returning
nil.


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


Surprisingly enough, this email does not originate from a beetle.
 
L

Luke Graham

Only if the right thing is "do not crash ever".

Ive got to agree with this - sometimes crashing really is the best
thing to do. Thats the point of assertions, for example. It certainly
gets attention. Getting a nil back when you expect a real value is a
pretty good sign something is wrong and really shouldnt be treated
lightly.
 
J

John Carter

I think that any RCR than can be implemented in pure ruby doesn't need to go
into the core. You can place a package on RAA and get a project on RubyForge
for these kinds of things handily.

I agree with you in general, but not in this particular instance.

A scripting language like ruby is partly about cleanliness and brevity of
expression. By making it part of the core, it permits even oneliners to
use the simplicity it gives.

Since it does change NilClass, although I don't believe it breaks any
existing code, it then makes a firm declaration that you can rely on any
future system and RAA libraries not to break.


As I say, I'm willing to be proven wrong.


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


Somewhere on the edge of a Galaxy, one of literally billions of such
galaxies, is a sun, one of literally billions of suns in that
galaxy.

Orbiting that sun is a small rock 330000 times smaller than that
sun.

This rock is covered by a very very thin scum of life. (Think 6000km
of rock followed by a meter or so of biomass.)

Amongst the millions of species in that scum are many hundreds of
thousands of types beetle and a mere handful of primates.

Surprisingly enough, this email does not originate from a beetle.

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

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

Austin Ziegler

Be careful about exactly what is being asked for and what has been
done before. I once developed an OOP system were all objects would
respond to all undefined messages by doing nothing. I rapidly
changed that to an error condition as in that case the complaint
that it hid bugs was valid.

Right. But why should I have to make that change for just about any
application that I end up writing? There are a lot of cases where
nil can arise -- and if it does arise, it may be incorrect. Do you
*want* to give me RSI by forcing me to do:

foo.bar unless foo.nil?

Sorry, but the current behaviour is more correct.
What I'm asking for here is that just the nil object, not even
the false object, responds to all undefined methods by doing
nothing and returning nil.

But nil isn't an Anything object, it's nothing. It shouldn't respond
to things that it doesn't know about.

If you want nil as a null object, use a module to extend the
behaviour -- but don't be surprised if a lot of programs start dying
for unclear reasons.

-austin
 
J

John Carter

But nil isn't an Anything object, it's nothing. It shouldn't respond
to things that it doesn't know about.

Ah, but you're wrong, nil is "no thing, of any type, is here."
If you want nil as a null object, use a module to extend the
behaviour -- but don't be surprised if a lot of programs start dying
for unclear reasons.

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?

Try this next few occasions you get such a bug, each time email me the
results, I will summarize to the group.


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


Somewhere on the edge of a Galaxy, one of literally billions of such
galaxies, is a sun, one of literally billions of suns in that
galaxy.
 
J

John Carter

Only if the right thing is "do not crash ever".

For me, if a bug like that is exposed, _it's a severe error_. Not
something to say is nil.

Is it? Prove me wrong empirically...

Save this code as nillit.rb
class NilClass
def method_missing( symbol, *args)
nil
end
end


Next time your program crashes on a "undefined method `foo' for
nil:NilClass (NoMethodError)"

Rerun your code with....
ruby -rnillit myprog
and see what happens, tell me the result. I'm prepared to be proven wrong.

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


Amongst the millions of species are many hundreds of
thousands of types beetle and a mere handful of primates.

Surprisingly enough, this email does not originate from a beetle.
 
S

Stefan Lang

A very simple and generic way of improving the reliability of Ruby
programs is to implement the NullObject pattern by allowing nil to
accept all and every method instead of throwing a NoMethodError.

Not only does this simplify ruby programs considerably, it also
changes certain crash bugs silently into correct programs.

I don't like it. It would hide many bugs. I want Ruby to throw an
exception when I'm ending up with something like that:

some_var = some_method
# some_method *should* return an IO object, but this time
# it returned nil
some_var.puts "important data"

It would be horribly if Ruby didn't throw an exception.

Stefan
 
L

Luke Graham

I don't like it. It would hide many bugs. I want Ruby to throw an
exception when I'm ending up with something like that:

some_var = some_method
# some_method *should* return an IO object, but this time
# it returned nil
some_var.puts "important data"

It would be horribly if Ruby didn't throw an exception.

John wants to be proven wrong with real code. (Well, Im sure he doesnt
-want- to be proven wrong, but you know...). I can think of some
artificial code that proves the point, but Im not sure its fair to
post it.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: RCR 303: nil should accept missing methods and return nil"

|Be careful about exactly what is being asked for and what has been done
|before. I once developed an OOP system were all objects would respond to
|all undefined messages by doing nothing. I rapidly changed that to an
|error condition as in that case the complaint that it hid bugs was valid.

I know Objective-C's nil works like that. I once developed an OOP
system (which was an early version of Ruby) where nil would respond to
all undefined messages by doing nothing. In production code, it does
nothing bad, since any production code should not raise an exception.
Rather it introduces new scheme of error handling.

But during development, it can hide bugs. It is very difficult to
distinguish correct error handling by ignoring unknown message, and
uncontrolled unintentional misbehavior caused by bugs. It's against
the principle of "early death".

Your proposal worth something, I think. But it's not going to be seen
in the near future of Ruby.

matz.
 
R

Robert Klemme

John Carter said:
A very simple and generic way of improving the reliability of Ruby
programs is to implement the NullObject pattern by allowing nil to
accept all and every method instead of throwing a NoMethodError.

Not only does this simplify ruby programs considerably, it also
changes certain crash bugs silently into correct programs.

I'm sorry, is this some kind of hoax? A program will not magically be
correct simply because an error is hidden. Think of all the subtle bugs
this would introduce, for example: you create an array with a fixed number
of elements and forget to give the default value 55. Now math would work,
but the results would be wrong - and you might not even catch this.

The article at
http://www.smalltalkchronicles.net/edition2-1/null_object_pattern.htm that
you refer to in the RCR states, that it's a bad idea to introduce this
change in Smalltalk because it would break too much code. The situation in
Ruby is similar.
If you were to investigate most cases where an if statement checks to
see if a value is non-nil, you will find that the else clause is
empty. ie. Does nothing.

But that would only work in cases like

unless x.nil?
x.do_something
end

i.e. where the block invokes methods on nil. This could be safely
transferred to

x.do_something

with your approach. But other cases cannot.

Regards

robert
 
R

Robert Klemme

John Carter said:
A very simple and generic way of improving the reliability of Ruby
programs is to implement the NullObject pattern by allowing nil to
accept all and every method instead of throwing a NoMethodError.

Not only does this simplify ruby programs considerably, it also
changes certain crash bugs silently into correct programs.

I'm sorry, is this some kind of hoax? A program will not magically be
correct simply because an error is hidden. Think of all the subtle bugs
this would introduce, for example: you create an array with a fixed number
of elements and forget to give the default value 55. Now math would work,
but the results would be wrong - and you might not even catch this.

The article at
http://www.smalltalkchronicles.net/edition2-1/null_object_pattern.htm that
you refer to in the RCR states, that it's a bad idea to introduce this
change in Smalltalk because it would break too much code. The situation in
Ruby is similar.
If you were to investigate most cases where an if statement checks to
see if a value is non-nil, you will find that the else clause is
empty. ie. Does nothing.

But that would only work in cases like

unless x.nil?
x.do_something
end

i.e. where the block invokes methods on nil. This could be safely
transferred to

x.do_something

with your approach. But other cases cannot.

Regards

robert
 
D

David A. Black

Hi --

I think that any RCR than can be implemented in pure ruby doesn't need to go
into the core. You can place a package on RAA and get a project on RubyForge
for these kinds of things handily.

I disagree. For one thing, there are real issues with modifying core
classes and methods; it's not a safe fallback. Also, most of Ruby can
be implemented in pure Ruby -- and some of what's in Ruby started life
as RCRs, either formal or informal. So you'd be creating an odd
situation: Matz will make changes that can be implemented in pure
Ruby, but no one else should suggest or discuss such changes.

I don't think there's any sign that that's what Matz has in mind with
the RCR process.


David
 
J

Jim Weirich

Right. But why should I have to make that change for just about any
application that I end up writing? There are a lot of cases where
nil can arise -- and if it does arise, it may be incorrect. Do you
*want* to give me RSI by forcing me to do:

    foo.bar unless foo.nil?

Sorry, but the current behaviour is more correct.

Actually, it is the current behavior that forces you to write the "unless
foo.nil?", since sending the "bar" message to nil is an error. If nil was
changed to be a message eating variant, then

foo.bar
and
foo.bar unless foo.nil?

would be equivalent. In other words, the change would help alleviate your RSI
problems.

I will admit that my gut reaction is to fall in the "should throw an
exception" camp. But the idea of a universal Null Design Pattern with nil is
intriguing. John's suggested experiment is fairly painless ... I'm willing
to try it and see if I'm persuaded.
 

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
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top