How to reduce Ruby runtime error?

X

Xiangrong Fang

Hi my friends,

I am thinking of how can I reduce Ruby runtime error. Not like
languages such as C or Pascal, Ruby is typeless. This is a good thing,
especially in design time. However, I found that my application is not
stable due to this feature.

For example, a routine is expecting a string, and this string turned out
to be nil at runtime, an error occurred. In Delphi, for example, it is
impossible that a string becomes a nil, it will be an empty string.

How can I better utilize Ruby's typeless feature yet avoid such anonying
runtime problem?

Thanks!
 
H

Hal E. Fulton

----- Original Message -----
From: "Xiangrong Fang" <[email protected]>
To: "ruby-talk ML" <[email protected]>
Sent: Tuesday, July 15, 2003 11:05 AM
Subject: How to reduce Ruby runtime error?

Hi my friends,

I am thinking of how can I reduce Ruby runtime error. Not like
languages such as C or Pascal, Ruby is typeless. This is a good thing,
especially in design time. However, I found that my application is not
stable due to this feature.

For example, a routine is expecting a string, and this string turned out
to be nil at runtime, an error occurred. In Delphi, for example, it is
impossible that a string becomes a nil, it will be an empty string.

How can I better utilize Ruby's typeless feature yet avoid such anonying
runtime problem?

You can always check types explicitly if you
really want to:

raise "Expecting an array" if not x.is_a? Array

Or you could test for nil:

raise "Argument was nil" if x.nil?

You can test for methods available ("duck typing")
rather than actual class:

raise "Object has no 'push' method" if not x.respond_to? :push

You can check for the "magic" conversion methods such as
to_str and to_ary:

if not x.is_a? String
x = x.to_str if x.respond_to? :to_str
else
raise "Wanted a string"
end

Not recommended: You could convert a nil value on entry
into the method:

x = [] if x.nil?

Not recommended: You could modify the NilClass so that a
nil value acted the way you want:

class NilClass
def +(value)
value # nil + x always equals x
end
end

I'm sure there are other ways.

Hal
 
S

Simon Strandgaard

----- Original Message -----
From: "Xiangrong Fang" <[email protected]>
To: "ruby-talk ML" <[email protected]>
Sent: Tuesday, July 15, 2003 11:05 AM
Subject: How to reduce Ruby runtime error?

Hi my friends,

I am thinking of how can I reduce Ruby runtime error. Not like
languages such as C or Pascal, Ruby is typeless. This is a good thing,
especially in design time. However, I found that my application is not
stable due to this feature.

For example, a routine is expecting a string, and this string turned out
to be nil at runtime, an error occurred. In Delphi, for example, it is
impossible that a string becomes a nil, it will be an empty string.

How can I better utilize Ruby's typeless feature yet avoid such anonying
runtime problem?
[snip]
I'm sure there are other ways.


Unittesting can help you early-catching such situations.

My AEditor project uses unittesting, status at this moment:
336 tests, 1161 assertions, 0 failures, 0 errors
http://raa.ruby-lang.org/list.rhtml?name=aeditor

I am myself using Rubyunit. Instead I recommend Test::Unit.
 
X

Xiangrong Fang

Hi Hal,

Thank you for the suggestions. The last thing I want to do is to raise
exceptions! If I raised exception, the program is broken. I will have to
deal with that manually.

Let me give you an example. I have a input line:

line = "a b"

I used a=line.split(" "), it becomes a=["a", "b"], that's what I want.
However, in real environment, some time the input is:

line = "c"

then, a=["c"]. In this case, if my program write somethin like

if a[1].upcase == "GOOD" then...

a[1] is nil, and you can't do "upcase" with it!

It is too tiring to find out all such possible errors, and raise
exceptions.

Actually, your "Not Recommended" ideas is better for me:
Not recommended: You could convert a nil value on entry
into the method:

x = [] if x.nil?

Not recommended: You could modify the NilClass so that a
nil value acted the way you want:

class NilClass
def +(value)
value # nil + x always equals x
end
end

I want my program to be robust. So the above recommendation is a good
idea. I don't know why it is "not recommended"?

Is it possible to "overload" the Nil class so that it is polymophic? for
example,

if the method upcase is called, nil act like string, if the expression
a = 2 + nil is executed, nil act like zero? That will be fantastic.

Thanks for your help.

Shannon


----- Original Message -----
From: "Xiangrong Fang" <[email protected]>
To: "ruby-talk ML" <[email protected]>
Sent: Tuesday, July 15, 2003 11:05 AM
Subject: How to reduce Ruby runtime error?

Hi my friends,

I am thinking of how can I reduce Ruby runtime error. Not like
languages such as C or Pascal, Ruby is typeless. This is a good thing,
especially in design time. However, I found that my application is not
stable due to this feature.

For example, a routine is expecting a string, and this string turned out
to be nil at runtime, an error occurred. In Delphi, for example, it is
impossible that a string becomes a nil, it will be an empty string.

How can I better utilize Ruby's typeless feature yet avoid such anonying
runtime problem?

You can always check types explicitly if you
really want to:

raise "Expecting an array" if not x.is_a? Array

Or you could test for nil:

raise "Argument was nil" if x.nil?

You can test for methods available ("duck typing")
rather than actual class:

raise "Object has no 'push' method" if not x.respond_to? :push

You can check for the "magic" conversion methods such as
to_str and to_ary:

if not x.is_a? String
x = x.to_str if x.respond_to? :to_str
else
raise "Wanted a string"
end

Not recommended: You could convert a nil value on entry
into the method:

x = [] if x.nil?

Not recommended: You could modify the NilClass so that a
nil value acted the way you want:

class NilClass
def +(value)
value # nil + x always equals x
end
end

I'm sure there are other ways.

Hal
 
X

Xiangrong Fang

Hi John,

My last post seems not appear yet -- usually it appears very fast. So I
repeat myself briefly:

I want my program to be robust. I don't want to check it after my
program is running in production environment. I want it to be
self-curable, i.e., tolerate these errors.

Is it possible to "overload" the Nil class so that it is polymophic? for
example,

if the method upcase is called, nil act like string, if the expression
a = 2 + nil is executed, nil act like zero? That will be fantastic.

Thanks for your help.

Shannon


Actually I consider it better not to check the type. You should get used to
looking at the stack trace to see what is wrong.

Generally you'll get some kind of method missing error. Then you puts the
type of the object being passed in, run your tests again, and check the
type.

I think about it this way: if the object passed in implements the correct
interface then it should be allowed to be passed in, regardless of whether
it is an instance_of? or kind_of? a certain object.

If you need to prevent a certain kind of class from being passed in (such as
the Nil class in your example) check for it at the beginning of your method
like so:

raise "cannot accept nil argument" if object.nil?

I would consider it preferable to exclude based on type rather than include
based on type.

--
John Long


-----Original Message-----
From: Hal E. Fulton [mailto:[email protected]]
Sent: Tuesday, July 15, 2003 11:21 AM
To: ruby-talk ML
Subject: Re: How to reduce Ruby runtime error?


----- Original Message -----
From: "Xiangrong Fang" <[email protected]>
To: "ruby-talk ML" <[email protected]>
Sent: Tuesday, July 15, 2003 11:05 AM
Subject: How to reduce Ruby runtime error?

Hi my friends,

I am thinking of how can I reduce Ruby runtime error. Not like
languages such as C or Pascal, Ruby is typeless. This is a good thing,
especially in design time. However, I found that my application is not
stable due to this feature.

For example, a routine is expecting a string, and this string turned out
to be nil at runtime, an error occurred. In Delphi, for example, it is
impossible that a string becomes a nil, it will be an empty string.

How can I better utilize Ruby's typeless feature yet avoid such anonying
runtime problem?

You can always check types explicitly if you
really want to:

raise "Expecting an array" if not x.is_a? Array

Or you could test for nil:

raise "Argument was nil" if x.nil?

You can test for methods available ("duck typing")
rather than actual class:

raise "Object has no 'push' method" if not x.respond_to? :push

You can check for the "magic" conversion methods such as
to_str and to_ary:

if not x.is_a? String
x = x.to_str if x.respond_to? :to_str
else
raise "Wanted a string"
end

Not recommended: You could convert a nil value on entry
into the method:

x = [] if x.nil?

Not recommended: You could modify the NilClass so that a
nil value acted the way you want:

class NilClass
def +(value)
value # nil + x always equals x
end
end

I'm sure there are other ways.

Hal
 
B

Ben Giddings

if a[1].upcase == "GOOD" then...

Then maybe you should say "if a[1].kind_of?(String) and "GOOD" == a[1].upcase"
a[1] is nil, and you can't do "upcase" with it!

It is too tiring to find out all such possible errors, and raise
exceptions.

Yes, but having your code blow an exception when you don't check the types is
probably a much easier type of thing to debug and fix than the subtle bugs
you can get when you change the behaviour of built-in classes.
I want my program to be robust. So the above recommendation is a good
idea. I don't know why it is "not recommended"?

If you do this, then you begin to lose the distinction between nil and "" or
0. The handy thing with nil is that you can use it to provide an
out-of-bounds value for certain operations. Say for example you have a
server and you want to know how what the change in the number of users is:

def getUsersDelta
server.num_users - @previous_user_count
end

So you write code that uses this number, doing math on it, etc. Then later,
you realize that unless the server is alive, the "num_users" accessor is
meaningless so you change the function to do this:

def getUsersDelta
if server.isAlive
server.num_users - @previous_user_count
else
nil
end
end

If you modify nil to act like zero, then you have no way of knowing whether or
not you have no change in the number of users, or whether the server has
died.

The other main reason not to change the definition of NilClass though is for
ease of future maintenance. If somebody else, or you 5 years down the road,
look at the code, you might forget that you modified NilClass in another
file of the project. You may look at how you're dealing with values and
assume that an exception will be thrown in a certain case but because you
modified NilClass the error sneaks by.

Ruby gives you more than enough rope to hang yourself, but keeps us suicide
hotline people busy. :)

Ben
 
X

Xavier Noria

My last post seems not appear yet -- usually it appears very fast. So
I repeat myself briefly:

I want my program to be robust. I don't want to check it after my
program is running in production environment. I want it to be
self-curable, i.e., tolerate these errors.

Is it possible to "overload" the Nil class so that it is polymophic?
for example,

if the method upcase is called, nil act like string, if the
expression a = 2 + nil is executed, nil act like zero? That will be
fantastic.

This happens in Java as well. Let me use its jargon.

One documents whether a returned object can be null or not. For instance
the persistence layer's read_user_by_id(id) may decide to indicate that
the requested user does not exist returning a null object. And in that
case the contract has to say so.

In that case, if the caller invokes user.name() without checking whether
the readed user object was null or not, that's his fault.

Otherwise, a method that returns nulls in a non well specified way might
need revision. If a method should return always a string and does not
do it, we've got a bug.

And as a last resort, you can wrap your application in a big try/catch
to avoid aborts in production.

-- fxn
 
B

Brian Candler

Hi Hal,

Thank you for the suggestions. The last thing I want to do is to raise
exceptions! If I raised exception, the program is broken. I will have to
deal with that manually.

Let me give you an example. I have a input line:

line = "a b"

I used a=line.split(" "), it becomes a=["a", "b"], that's what I want.
However, in real environment, some time the input is:

line = "c"

then, a=["c"]. In this case, if my program write somethin like

if a[1].upcase == "GOOD" then...

a[1] is nil, and you can't do "upcase" with it!

If you know in advance that a[1] might not exist, then you can write your
code as

if a[1].to_s.upcase == "GOOD" then ...

This also works if a[1] is some object other than a string (e.g. a number).
You will get some sort of string from it.

But Ruby is trying to tell you that it doesn't make sense to compare "no
object" with a string. So perhaps your code can be cleaned up: e.g.

next unless a[1] # skip this loop iteration unless we have an item

if a[1] and a[1].upcase == "GOOD" then...
Actually, your "Not Recommended" ideas is better for me:
Not recommended: You could convert a nil value on entry
into the method:

x = [] if x.nil?

Or even simpler:

x ||= "foo"

This is a very common Ruby idiom for "set a default value"
I want my program to be robust. So the above recommendation is a good
idea. I don't know why it is "not recommended"?

Well, it wouldn't work in your case, because you have not defined a
'downcase' method in NilClass.
Is it possible to "overload" the Nil class so that it is polymophic? for
example,

if the method upcase is called, nil act like string, if the expression
a = 2 + nil is executed, nil act like zero? That will be fantastic.

You can, but you have to write it yourself by tediously adding all the
appropriate methods to NilClass, Fixnum, String etc. This is because
operators belong to classes: a+b calls a.+(b), so 2+nil calls Fixnum#+,
whereas nil+2 calls NilClass#+.

You'll then have code which pollutes the core Ruby classes, which is
probably not a good thing (especially if you end up running your code on a
shared interpreter, e.g. under mod_ruby)

Regards,

Brian.
 
X

Xiangrong Fang

The other main reason not to change the definition of NilClass though is for
ease of future maintenance. If somebody else, or you 5 years down the road,
look at the code, you might forget that you modified NilClass in another
file of the project. You may look at how you're dealing with values and
assume that an exception will be thrown in a certain case but because you
modified NilClass the error sneaks by.

Ruby gives you more than enough rope to hang yourself, but keeps us suicide
hotline people busy. :)
I already realized that I am suiciding, that's why I asked you people
for help. What I am asking in essence is that since ruby is typeless, it
should act like truely typeless. An array of string can return nil, then
nil should act like string. I just ask if the ruby language itself has
such mechanism.

I repeat: Thanks for all the suggestion regarding unit test and
exceptions. Acutally I have already used if/then, or rescue to catch
these runtimes. What I am complaining is that if I have to always do
this in ruby programming, I might be losing all the productivity I
gained from the excellent language. Unfortunately, it is happening... I
am porting a piece of code from ruby to a dll writen in delphi.

I remembered Dave Thomas in his posting or the pickaxe said that Ruby is
a good lanugage for prototyping, is it really only prototyping? I hope
not.

Shannon
 
H

Hal E. Fulton

----- Original Message -----
From: "Xiangrong Fang" <[email protected]>
To: "ruby-talk ML" <[email protected]>
Sent: Tuesday, July 15, 2003 12:56 PM
Subject: Re: How to reduce Ruby runtime error?

I already realized that I am suiciding, that's why I asked you people
for help. What I am asking in essence is that since ruby is typeless, it
should act like truely typeless. An array of string can return nil, then
nil should act like string. I just ask if the ruby language itself has
such mechanism.

I repeat: Thanks for all the suggestion regarding unit test and
exceptions. Acutally I have already used if/then, or rescue to catch
these runtimes. What I am complaining is that if I have to always do
this in ruby programming, I might be losing all the productivity I
gained from the excellent language. Unfortunately, it is happening... I
am porting a piece of code from ruby to a dll writen in delphi.

I remembered Dave Thomas in his posting or the pickaxe said that Ruby is
a good lanugage for prototyping, is it really only prototyping? I hope
not.

Well, remember that if an invalid value is being
passed to a method, it's really the fault of the
code upstream.

An exception is a way of informing you that something
is wrong, rather than stumbling along with data that
are "probably" OK.

I don't think this is a bug of Ruby. I think it is
a feature.

If a core method that usually returns a string actually
returns a nil value, it is doing so for a reason -- it
is conveying information to you. It is better to act on
that information earlier rather than later.

John and Simon are correct in what they say. (Others,
too.)

Testing the type of an argument is a last-ditch effort.
Changing nil to behave like a string or integer is
probably even less desirable. For one thing, it defeats
the purpose of the nil value. If we wanted nil to act
like a null string, we could just use a null string
instead.

Unit testing is an excellent way of ensuring code
coverage and catching unusual conditions.

Cheers,
Hal
 
G

gabriele renzi

il Wed, 16 Jul 2003 01:05:20 +0900, Xiangrong Fang
How can I better utilize Ruby's typeless feature yet avoid such anonying
runtime problem?

lots of people already gave you thery ideas, I'll add my 2c even if
this is mayn not really be what you need.
using the StrongTyping module from raa you can do something like this

def foo(a, b)
expect(a, String, b, Numeric)
...
end

maybe this could help..
 
N

Nucleon

Hi John,

My last post seems not appear yet -- usually it appears very fast. So I
repeat myself briefly:

I want my program to be robust. I don't want to check it after my
program is running in production environment. I want it to be
self-curable, i.e., tolerate these errors.

Is it possible to "overload" the Nil class so that it is polymophic? for
example,

if the method upcase is called, nil act like string, if the expression
a = 2 + nil is executed, nil act like zero? That will be fantastic.

Thanks for your help.

Shannon

class Object
def method_missing(id, *args)
STDERR.puts "Missing method #{self.class}##{id}(#{args.collect{|i|i.inspect}.join(", ")}) called from #{caller[0]}"
return nil
end
end

Now, whenever you try to call a non-existant method, you'll get an
informative complaint, but the call will succeed and return nil. This is
evil, of course, and you really should fix the problem, but it will work.
For example:

nil.upcase
nil + 2

outputs:

Missing method NilClass#upcase() called from (irb):8:in `irb_binding'
Missing method NilClass#+(2) called from (irb):9:in `irb_binding'
 
S

Sascha Dördelmann

Xiangrong Fang said:
I repeat: Thanks for all the suggestion regarding unit test and
exceptions. Acutally I have already used if/then, or rescue to catch
these runtimes. What I am complaining is that if I have to always do
this in ruby programming, I might be losing all the productivity I
gained from the excellent language.

You won't lose your productivity.

The topic has been broadly discussed in comp.lang.*. Do a search in
groups.google.com on "Why dynamic typing" and "Newbie: argument
checking in Smalltalk"
http://groups.google.com/groups?q=Why+dynamic+typing
http://groups.google.com/groups?q=Newbie+argument+checking+Smalltalk

My own opinion is, that your own suggestion about nils behaving like
various other objects won't lead to a robust program. (Maybe I still
don't get what you meant by robust? ;-)

A robust program won't ignore errors. It will detect them and behave
in a deterministic way. Unit tests are the best way towards robust
programs I know of. The second best is to use some assertion concept
based on exceptions. Start with some basic exception handling and
focus on error detection instead of error handling. After reducing the
total amount of errors you will easily find the places where error
handling makes sense and where it does not.

Cheers
Sascha
 

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,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top