RCR 303: nil should accept missing methods and return nil

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 : john.carter@tait.co.nz
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.

John Carter wrote:

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.

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

···

--
Best regards,

Alexey Verkhovsky

Ruby Forum: http://ruby-forum.org (moderator)
RForum: http://rforum.andreas-s.net (co-author)
Instiki: http://instiki.org (maintainer)

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.

···

On 5/6/05, John Carter <john.carter@tait.co.nz> wrote:

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

--
spooq

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

···

On Friday 06 May 2005 04:27, John Carter wrote:

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.

"John Carter" <john.carter@tait.co.nz> schrieb im Newsbeitrag news:Pine.LNX.4.61.0505061350130.28698@parore.tait.co.nz...

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 smalltalkchronicles.net - This website is for sale! - smalltalkchronicles Resources and Information. 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

"John Carter" <john.carter@tait.co.nz> schrieb im Newsbeitrag
news:Pine.LNX.4.61.0505061350130.28698@parore.tait.co.nz...

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
smalltalkchronicles.net - This website is for sale! - smalltalkchronicles Resources and Information. 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

John Carter wrote:

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.

Great! I've so long waited for something like this. I have just written
a very reliable program, with this new feature:

class NilClass
  def method_missing(*ignore) nil end
end

class Counter
  def initialize
    @count = 1
  end

  attr_reader :count

  def increase
    @count += 1
  end
end

class DoSomething
  def initialize
    @coutner = Counter.new
  end

  def count_done
    @counter.count
  end

  def do_it
    @counter.increase
  end
end

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

···

--
Florian Frank

As far as I understand the whole discussion some wish this behaviour to be
able to do things like

obj.meth1().meth2().meth3()

which should not break if meth2 for some cause returns nil.

On the other hand many people claim this will hide errors (I agree here).
It seems there are two kinds of nils: expected and unexpected ones.

So why dont we deal with the expected ones seperately:

···

John Carter <john.carter@tait.co.nz> wrote:

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.

==========================================================
class Object
  def nil_friendly
    self
  end
end

class NilClass
  def nil_friendly
    Blackhole.instance
  end
end

class Blackhole
   include Singleton
   def method_missing( sym, *args)
     nil
   end
end

## Now this works:

obj.meth1().meth2().nil_friendly.meth3()

So by inserting .nil_friendly (choose a better/shorter name for this) you
_explicitly_ tell that you don't care if nil ist returned.

This will not break any existing code, will not hide errors, but gives
shorter code without explicit ifs.

Comments?

Klaus Stein
--

The Answer is 42. And I am the Answer. Now I am looking for the Question.

Luke Graham wrote:

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.

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

···

On 5/6/05, John Carter <john.carter@tait.co.nz> wrote:

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 : john.carter@tait.co.nz
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.)

···

On Fri, 6 May 2005, Alexey Verkhovsky wrote:

John Carter wrote:

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.

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

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 : john.carter@tait.co.nz
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.

···

On Fri, 6 May 2005, Luke Graham wrote:

On 5/6/05, John Carter <john.carter@tait.co.nz> wrote:

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

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.

···

On 5/6/05, Stefan Lang <langstefan@gmx.at> wrote:

On Friday 06 May 2005 04:27, John Carter wrote:
> 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.

--
spooq

I presume the typo on @coutner was deliberate for illustrative purposes...

Unfortunately rubies run time switches are bug compatible with perl's.

If you read "man perl" and scroll down to the BUGS sections it says...

BUGS
       The -w switch is not mandatory.

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

ruby -w frank.rb
frank.rb:29: warning: instance variable @counter not initialized
frank.rb:29: warning: instance variable @counter not initialized
frank.rb:29: warning: instance variable @counter not initialized
frank.rb:29: warning: instance variable @counter not initialized
frank.rb:29: warning: instance variable @counter not initialized
frank.rb:29: warning: instance variable @counter not initialized
frank.rb:29: warning: instance variable @counter not initialized
frank.rb:29: warning: instance variable @counter not initialized
frank.rb:29: warning: instance variable @counter not initialized
frank.rb:29: warning: instance variable @counter not initialized
frank.rb:25: warning: instance variable @counter not initialized
Have done it times.

···

On Sat, 07 May 2005 06:54:27 +0900, Florian Frank wrote:

John Carter wrote:

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.

Great! I've so long waited for something like this. I have just written
a very reliable program, with this new feature:

--
John Carter

The Cybernetic Entomologist - cyent@xtra.co.nz

http://geocities.yahoo.com/cy_ent

I'm becoming less and less convinced of humans as rational beings.
I suspect we are merely meme collectors, and the reason meme is only
kept on to help count our change.

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.

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

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.

In which case more code simplification, more code that just works.

The article at
smalltalkchronicles.net - This website is for sale! - smalltalkchronicles Resources and Information. 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.

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.

···

On Fri, 06 May 2005 12:12:35 +0200, Robert Klemme wrote:

--
John Carter

The Cybernetic Entomologist - cyent@xtra.co.nz

http://geocities.yahoo.com/cy_ent

I'm becoming less and less convinced of humans as rational beings.
I suspect we are merely meme collectors, and the reason meme is only
kept on to help count our change.

Klaus Stein wrote:

···

John Carter <john.carter@tait.co.nz> wrote:

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.

As far as I understand the whole discussion some wish this behaviour
to be able to do things like

obj.meth1().meth2().meth3()

which should not break if meth2 for some cause returns nil.

On the other hand many people claim this will hide errors (I agree
here). It seems there are two kinds of nils: expected and unexpected
ones.

So why dont we deal with the expected ones seperately:

==========================================================
class Object
  def nil_friendly
    self
  end
end

class NilClass
  def nil_friendly
    Blackhole.instance
  end
end

class Blackhole
   include Singleton
   def method_missing( sym, *args)
     nil
   end
end

## Now this works:

obj.meth1().meth2().nil_friendly.meth3()

So by inserting .nil_friendly (choose a better/shorter name for this)
you _explicitly_ tell that you don't care if nil ist returned.

This will not break any existing code, will not hide errors, but gives
shorter code without explicit ifs.

Comments?

What about #nil?

    robert

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.

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.

···

On 05 May 2005, at 20:39, John Carter wrote:

On Fri, 6 May 2005, Alexey Verkhovsky wrote:

John Carter wrote:

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.

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

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

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 : john.carter@tait.co.nz
New Zealand

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

···

On Fri, 6 May 2005, Hal Fulton wrote:

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.

Um. I think that the point being made is that crashing, in this
case, *is* doing the right thing. If nil respondedd to all methods
silently, how would you ever know when your method got the *wrong*
data?

I said this on the RCR comment area, but PDF::Writer is *correct* to
blow up when it gets nil. Without this, there are things that I
could not *possibly* have debugged. With the silent eating of
messages, the generated PDF files would be incorrect -- invalid --
and there's not a damned thing I could do to tell where the problem
is.

I don't really know what to say to this -- this will turn a correct
behaviour into a completely invalid behaviour.

-austin

···

On 5/5/05, John Carter <john.carter@tait.co.nz> wrote:

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.

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

John Carter wrote:

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.

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

No. I can tell you from experience over the last four days that I
have fixed no fewer than five *real* bugs in PDF::Writer that would
have resulted in the incorrect generation of PDF output had I done
this. Probably even created a PDF that would crash any PDF reader
out there.

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

This will happen in only a few applications; I would argue it will
never happen in anything that has to communicate with the outside
world.

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.

Yes, it does, at least with PDF::Writer. The output for PDF has to
be correct. And in this case, PDF::Writer would simply do the wrong
thing.

-austin

···

On 5/5/05, John Carter <john.carter@tait.co.nz> wrote:

On Fri, 6 May 2005, Alexey Verkhovsky wrote:

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca