Hacker News new | ask | show | jobs
by derefr 5332 days ago
Isn't this a flaw in Ruby, though? It also means that you can't create a delegate/decorator/proxy object for a false object and have it be false as well, which goes against the general "everything is determined by sending messages" vibe Ruby has going on.

I know the reasoning (it's much faster to do math on object IDs than it is to call a method), but there are workarounds for this (e.g. only allowing frozen objects to be boolean-false, and having the object's truth-ness/false-ness represented by the return value of its #false? method at the time of freezing—thus allowing the interpreter to locate it in a semantically-meaningful part of object-ID space that can later be masked for in a TEST instruction.)

2 comments

With infinite spare time, what I'd try would be to make NilClass usable as a base class. The way that would work would be to have all instances of classes inheriting from NilClass live in the 0bXXX...XXX100 object ID space. Nil is currently 0b100, which is consistent, and this doesn't clash with any of the other immediates - it just translates to a different memory alignment rule in MRI.
The ruby people will tell you that ruby's x, where x is power, flexibility, etc., comes from the ability to shoot yourself in the foot so just don't shoot yourself in the foot.
Speaking as a Ruby user, I think nil and the entire behaviour around truthyness and falseness are wrong. I should be able to create my own nil objects, and I especially would love to be able to create false objects that carry more information.

For example (and there are code smells in this, but it gets the idea across):

    if account = Account.create(params[:account])
      ...
    else
      complainAbout account.errors
    end
Truthiness should not be reserved for whether an object exists, it could also be used for whether it is valid, complete, or anything else.

My particular example might not be a great one, but I think framework and library developers ought to be able to work a consistent use for truthiness and falseness into their creations, e.g. a true object has been saved, a true object is valid, a true object is complete, a true object represents the current state of the world instead of the past or future or a wrong state, and so forth.

This is the next step for my #blank? proposal over here: http://redmine.ruby-lang.org/issues/5372 - to make #blank? or #null? objects evaluate as false in boolean expressions.
To me this isn't a problem with nil. This is a problem with the create method. create either returns you the object or something ambiguous (nil). it COULD return you the object or something useful, like an error class, or an error code, or whatever....

If you care enough about the state of what's being returned then it would be trivial to test if the returned object was of the class you were expecting (Account) and if not handle what it did return (some error indication) as appropriate.

I'm ok with that. BUT if that's how we want to go, why not ditch truthiness for non-nil objects altogether?My complaint is that truthiness is baked into the language in such an inflexible way.
I'm not sure how I feel about that. It just seems so useful to have it, BUT I think there's definitely a strong argument to be made for your proposition.

Alternately, why not just switch to a functional language where, it seems, ambiguity and other related problems rarely make it past the bouncer at the front door.

I think this is a design issue that is orthogonal to functional languages or static typing. We could easily create a language where writing "if foo" causes an error when foo doesn't resolve to an instance of Boolean. If we like, we could add coercion to boolean through the #to_b" method (although I have issues with implicit coercion).

At a deep level, I wonder if my issue is with if stamens and boolean operators being magic outside of the object system. Smalltalk gets this right. Scheme gets this right. if "if" is defined in the standard library rather than being magic syntactic construct, we can write our own control primitives:

    provided account = Account.create(params[:account])
      ...
    end
:-)
I think truthiness and validity/completeness are different things. Overloading truthiness sounds to me like a recipe for confusion and less expressiveness.

That said, I'm all for experimentation and would love to see this idea tried.