Subtle Bug Reveals Major Design Flaw

R

rantingrickjohnson

Hello folks.

I was hacking away today when i made a stupid mistake that revealed a subtle bug in ruby. Here is a test suite that will expose the problem. (be sure to supply a valid folder path on your machine)

## START CODE ##
def bug
path = 'USE A VALID FOLDER PATH HERE!'
d = Dir.new(path)
files = []
folders = []
d.entries.each do |x|
$stdout.write("--x=#{x}\n")
if x == '.' or x == '..'
next
xpath = File.join(path, x) #<-- ERROR!
elsif File.ftype(xpath)=="directory"
folders.push(xpath)
elsif File.ftype(xpath)=="file"
files.push(xpath)
end
end
$stdout.write("files=#{files}\n")
$stdout.write("folders=#{folders}\n")
end
## END CODE ##

## START RESULT ##
Error: #<TypeError: path/file.rb:14:in `ftype': can't convert nil into String>
## END RESULT ##

As you can see i accidentally pasted the line "xpath = File.join(path, x)" INSIDE the conditional. However, for some reason Ruby committed the following three crimes:

############################################################
# Warning #
############################################################
# In the following paragraphs the author has used #
# emotional language to describe his mental state whilst #
# suffering the affects from a subtle bug. Please read #
# this section objectively and do not get offended by #
# these words. The author does not intend to offend any #
# member of this fine community or the even the language #
# designer himself. The author simply wants to understand #
# what the heck is going on! Thank you. #
############################################################

1. Ruby did not throw a SyntaxError:
I guess some might argue that the programmer should be responsible breakingsyntax, and i agree, HOWEVER, syntax error can and will happen and catching this type of error at the syntactical level BEFORE it has time to metastasis is just good language design.

2. Ruby re-assigned the value of a variable, OR, failed to interpret the value correctly and simply defaulted to nil.
I am amazed that Ruby would overlook the broken control structure, okay, maybe this is a feature of the language that i was unaware of-- if so i can accept that--but why then did Ruby go on to add insult to injury by haphazardly assigning "nil" to my variable `xpath`. What possible good could come of that?

3. Ruby did not throw a NameError:
Finally, but worst of all, (and a direct result of re-assignment); Ruby didnot throw a NameError when i tried to access xpath. xpath should never have had existed. I could "somewhat" understand allowing the syntactical mistake and just ignoring xpath, then, throwing a NameError if i tried to access`xpath`. This would make sense at least. But what i uncovered makes no sense. It's madness.

Conclusion: Ruby had three chances to catch my mistake and notify me of that mistake but ruby failed all three times. Can someone please explain the philosophy behind this design?

"Errors should never pass silently!"
 
R

Robert Klemme

Hello folks.

I was hacking away today when i made a stupid mistake that revealed
a subtle bug in ruby. Here is a test suite that will expose the problem.
(be sure to supply a valid folder path on your machine)

It's a subtle bug in _your code_ - not in Ruby. I wonder why you call
it "major design flaw" in the subject and now it's a "subtle bug"...
## START CODE ##
def bug
path = 'USE A VALID FOLDER PATH HERE!'
d = Dir.new(path)
files = []
folders = []
d.entries.each do |x|
$stdout.write("--x=#{x}\n")
if x == '.' or x == '..'
next
xpath = File.join(path, x) #<-- ERROR!
elsif File.ftype(xpath)=="directory"
folders.push(xpath)
elsif File.ftype(xpath)=="file"
files.push(xpath)
end
end
$stdout.write("files=#{files}\n")
$stdout.write("folders=#{folders}\n")
end
## END CODE ##

## START RESULT ##
Error: #<TypeError: path/file.rb:14:in `ftype': can't convert nil into String>
## END RESULT ##

As you can see i accidentally pasted the line "xpath =
File.join(path, x)" INSIDE the conditional. However, for some reason Ruby committed the
following three crimes:

Hold your horses. That's way out of proportion.
############################################################
# Warning #
############################################################
# In the following paragraphs the author has used #
# emotional language to describe his mental state whilst #
# suffering the affects from a subtle bug. Please read #
# this section objectively and do not get offended by #
# these words. The author does not intend to offend any #
# member of this fine community or the even the language #
# designer himself. The author simply wants to understand #
# what the heck is going on! Thank you. #
############################################################

If you do not intend offense then why are you using offensive language?
Instead of writing this disclaimer you could have taken the time to
rid out the offensive bits since you were obviously aware of the effect
they could have.
1. Ruby did not throw a SyntaxError: I guess some might argue that
the programmer should be responsible breaking syntax, and i agree,
HOWEVER, syntax error can and will happen and catching this type of
error at the syntactical level BEFORE it has time to metastasis is
just good language design.

There is no syntax error. If you indent line with "ERROR" a bit
differently you'll probably see it yourself:

if x == '.' or x == '..'
next
xpath = File.join(path, x) #<-- ERROR!
elsif File.ftype(xpath)=="directory"
folders.push(xpath)
elsif File.ftype(xpath)=="file"
files.push(xpath)
end

As Christian has written, local variables are known within the scope
from the point they are defined (first assigned) on - even if that
statement is not executed:

$ ruby -e '2.times {|x| if false;a=2 else p a;a=2 end}'
nil
nil

Variable a lives inside the scope only which also means it's
reinitialized on every execution of the block.
2. Ruby re-assigned the value of a variable, OR, failed to interpret
the value correctly and simply defaulted to nil.

There is no reinitialization going on: nil is simply the default value
for _all_ uninitialized variables.
I am amazed that
Ruby would overlook the broken control structure,

It's not broken. (see above)
okay, maybe this is
a feature of the language that i was unaware of-- if so i can accept
that--but why then did Ruby go on to add insult to injury by
haphazardly assigning "nil" to my variable `xpath`. What possible
good could come of that?

The way the language is designed there has to be a default value. nil
is even more friendly than Java's null which will throw immediately on
every method invocation.
3. Ruby did not throw a NameError: Finally, but worst of all, (and a
direct result of re-assignment); Ruby did not throw a NameError when
i tried to access xpath. xpath should never have had existed. I could
"somewhat" understand allowing the syntactical mistake and just
ignoring xpath, then, throwing a NameError if i tried to access
`xpath`. This would make sense at least. But what i uncovered makes
no sense. It's madness.

People find it strange from time to time that local variables work in
Ruby the way they do. If that is madness for you then I suggest you
simply stop using the language and move on to another one.
Conclusion: Ruby had three chances to catch my mistake and notify me
of that mistake but ruby failed all three times. Can someone please
explain the philosophy behind this design?

Ruby interpreter cannot read your mind. You can write syntactically
correct programs which are nonsense nevertheless in every programming
language. You better get used to that fact. There are ways to shoot
yourself in the foot in every programming language.
"Errors should never pass silently!"

Well, the error did not pass silently, did it? You got a response from
File.ftype().

Plus, Ruby MRI is even smart enough to warn you about the dead code - if
you chose to use the tools provided (option -w in this case):

$ ruby -w x.rb
x.rb:9: warning: statement not reached
$ cat -n x.rb
1 def bug
2 path = 'USE A VALID FOLDER PATH HERE!'
3 d = Dir.new(path)
4 files = []
5 folders = []
6 d.entries.each do |x|
7 $stdout.write("--x=#{x}\n")
8 if x == '.' or x == '..'
9 next
10 xpath = File.join(path, x) #<-- ERROR!
11 elsif File.ftype(xpath)=="directory"
12 folders.push(xpath)
13 elsif File.ftype(xpath)=="file"
14 files.push(xpath)
15 end
16 end
17 $stdout.write("files=#{files}\n")
18 $stdout.write("folders=#{folders}\n")
19 end

Cheers

robert
 
R

Rick Johnson

Robert Klemme:
It's a subtle bug in _your code_ - not in Ruby. I wonder
why you call it "major design flaw" in the subject and now
it's a "subtle bug"...

You and i must interpret the definition of "subtle bug" differently.
Here is the definition of subtle:
############################################################
# Definition: Subtle #
############################################################
# So delicate or precise as to be difficult to analyze or #
# describe. Delicately complex and understated. #
############################################################
From that definition you should now understand why "subtle bugs" are
the wort kinds of bugs and they are usually the result of either
language design or exception messages that mislead or even lie to
you.
There is no syntax error. If you indent line with "ERROR"
a bit differently you'll probably see it yourself:

Hindsight is 20/20 my friend. This time i was lucky, the syntactical
error was in the same method, but what if it was in another file? How
would i know where to start looking when Ruby throws an obscure
sounding error about typecasting nil? nil has nothing to do with
improper syntactical structures. Ruby failed to stop the snowball time
and time again.
$ ruby -e '2.times {|x| if false;a=2 else p a;a=2 end}'
nil nil

Variable a lives inside the scope only which also means
it's reinitialized on every execution of the block.

And that is exactly the kind of design that will create "subtle bugs".
Ruby looked at the variable "a" and said: Hmm, 'a' has no value, so
instead of throwing a NameError i'll just be a good buddy and read my
programmers mind... hmmm, i'll bet he wanted 'a' to hold a nil value,
yep, i'm sure of it! *Alakazam*

THE ROAD TO HELL IS PAVED WITH GOOD INTENTIONS!
There is no reinitialization going on: nil is simply the
default value for _all_ uninitialized variables.

What IS an uninitialized variable exactly? I would consider it to be a
variable that is defined by the programmer (following strict rules
that create an identifier and a value) but has yet to be initialized
in the program. Because how could ANY language initialize ANY object
without some sort of definition?

"""Sarcastic Sam Wrote: Mind reading of course! """

`a` was never defined by you, because in order to define a variable
with the identifier `a` you must first assign a value to `a` -- AND
THAT NEVER HAPPENED! You could say `a` was referenced, but never
defined. My problem is justifying such an action. What possible good
could come of that? Show an example that showcases this feature.

My opinion is that NOTHING good could come from the interpreter
"inventing" variable values from referenced (non-existent)
identifiers. This is lunacy!
Ruby interpreter cannot read your mind.

Well then why does it invent variable for me? That sounds like
attempted mind reading to me. Maybe i wanted `a` to be a class object,
or a constant, or a puppy... this is nuts!

You see, there must be a strict contract between a programmer and his
interpreter/compiler. A contract that defines rock solid rules that
will avoid these instances of the "lost-in-translation" variety. My
interpreter should always insure that my syntax is legal and should
NEVER do anything i did not tell it to do. Syntax is the interface to
my interpreter. And through this syntax I define what WILL and what
WILL NOT happen. I expect my interpreter to execute my commands
EXACTLY, without pride or prejudice -- EVEN IF my commands result in
catastrophic failure. That's why we invented the exception message.
 

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,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top