Subtle Bug Reveals Major Design Flaw

Discussion in 'Ruby' started by rantingrickjohnson, Dec 24, 2012.

  1. 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!"
     
    rantingrickjohnson, Dec 24, 2012
    #1
    1. Advertisements

  2. 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"...
    Hold your horses. That's way out of proportion.
    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.
    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.
    There is no reinitialization going on: nil is simply the default value
    for _all_ uninitialized variables.
    It's not broken. (see above)
    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.
    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.
    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.
    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
     
    Robert Klemme, Dec 24, 2012
    #2
    1. Advertisements

  3. rantingrickjohnson

    Rick Johnson Guest

    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.
    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.
    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!
    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!
    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.
     
    Rick Johnson, Dec 25, 2012
    #3
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.