views:

285

answers:

5

I was working on serializing values when found out about this one. Ruby has a TrueClass class, and a FalseClass class, but it has no Boolean class. I'd like to know why is this.

I see some advantages in using a Boolean - for example, String parsing could be centralized on it.

Ruby developers are smarter than me, so there must be a lot of good reasons that I just don't see. But right now it looks to me like having OneClass and a TwoClass instead of Fixnum.

+2  A: 

In Ruby nil and false are false and everything else is true. Hence there is no need for a specific boolean class.

You can try it :

if 5
  puts "5 is true"
end

5 evaluates to true

if nil
    puts "nil is true"
else
    puts "nil is false"
end

Will print "nil is false"

Mongus Pong
@Mongus Pong: you forgot that `false` is false too :)
neutrino
True, nil and false is false. Everything else is true.
Mongus Pong
How are you deducing that no boolean class is needed from the fact that only nil and false are "false" ("falsy")? I can't see the logic here ( pun intended :) )
egarcia
@egarcia because it means that any type can be used to represent a Boolean. This coupled with rubys dynamic typing covers all the necessary scenarios.
Mongus Pong
@Mongus: `nil` and `false` are false, but `true` is not false. :)
Andrew Grimm
@Andrew, darn the english language! I meant to say "neutrino, it is True what you have stated, nil and false is false.
Mongus Pong
+1  A: 

true and false could be managed by a Boolean class that held multiple values, but then the class object would have to have internal values, and therefore have to be de-referenced with every use.

Instead, Ruby treats true and false as long values (0 and 1), each of which corresponds to a type of object class (FalseClass and TrueClass). By using two classes instead of a single Boolean class, each class does not require any values and therefore can be distinguished simply by its class identifier (0 or 1). I believe this translates to significant speed advantages internal to the Ruby engine, because internally Ruby can treat TrueClass and FalseClass as long values that require zero translation from their ID value, whereas a Boolean object would have to be de-referenced before it could be evaluated.

Asher
This doesn't make sense to me. You're saying that if we had a Boolean class with two values instead of TrueClass and FalseClass with one value each, true and false could no longer be immediate values. Why should this be the case? After all there's more than one instance of Fixnum (significantly more) and Fixnums are still immediate values. Also minor nitpick: true and false aren't represented as 0 and 1 internally, they're represented as 2 and 0 (in MRI at least).
sepp2k
Good point on 2 and 0 - I was sure about FalseClass but not TrueClass... In any case, my point was that using TrueClass and FalseClass permits the value to be evaluated simply by way of the class identifier, whereas an identifier that marked an object as a Boolean class would then have to be evaluated before the instance value was known. I take this to be the same sort of optimization that is done by treating all IDs (which are generally pointer addresses) as longs so that FixNums can be 1 bit offset and operated on directly rather than having to be dereferenced before evaluation.
Asher
@Asher: The value is not "evaluated simply by way of the class identifier". Ruby doesn't know that a value is false, because its class is FalseClass. It know it's false because the value is 0. Likewise it knows a value is true because it is 2. It doesn't even store the class information anywhere (there's not actually any object struct associated with true or false, so there'd be nowhere to store the class). In other words replacing TrueClass and FalseClass with Boolean would not make any performance difference.
sepp2k
But that was exactly my point. Ruby doesn't need to instantiate TrueClass or FalseClass because 0 and 2 already correspond to their values, which is also their class. If there was a Boolean class, instances and instance-evaluation would be necessary (via internal class resolution). As is, only the long value (0 or 2) needs to be evaluated as a class ID, at which point instance evaluation can stop. 0 and 2 are directly and already the class information, which is why nothing else has to be stored anywhere.
Asher
I dont see how instance evaluation would be necessary for a Boolean class. Make Boolean a class that has only two possible values, stored as constants: true and false. true is represented by the value 2, and false by 0. Boolean.new only returns one of these two constants. No instance-evaluation needed here.
Ragmaanir
Having made it a class requires evaluation of the class. What you've describe is basically how Ruby handles it- there are two constants that appear to function as a common class. The reality is that what you've described necessitates a third identifier as well, which is the class identifier.
Asher
+1  A: 

The main reason is simply the fact that this is far quicker and simpler to implement boolean expressions as it is currently than with a Boolean class which would imply a conversion.

As Mongus Pong told you, when you write "if ", you ask the interpretor to evaluate the and then branch. If you had Boolean class, you'd have to convert the evaluation of into a Boolean before branching (one more step).

Remember that such a ->Boolean conversion would be available as a Ruby method in the Boolean class. This method could then be changed dynamically as any other Ruby method, allowing developer to mess up things completely (which is not so serious indeed) but clearly, this wouldn't allow the interpretor to optimize tests as they should.

Do you realize that it would replace a few CPU instructions operation by a complete method call, which is costly in Ruby (remember the "send" method processing)...

Michel Nolard
I'm sorry, but I don't follow the efficiency reasoning. I imagine that there's some very minor gain on the fact that you don't have to invoke "self" on a method and return a value instead, but that isn't relevant when the whole class is implemented in C anyway, is it?
egarcia
Why would a Boolean class imply a conversion, but TrueClass and FalseClass do not? "If you had Boolean class, you'd have to convert the evaluation of `thing` into a Boolean before branching" What's the logic behind this statement?
sepp2k
+3  A: 

The purpose of a class is to group similar objects, or objects with similar behavior together. 1 and 2 are very similar, therefore it makes perfect sense for them to be in the same class. true and false however are not similar. In fact, their whole point is that they are exactly the opposite of each other and have opposite behavior. Therefore, they don't belong in the same class.

Can you give an example of what sort of common behavior you would implement in a Boolean class? I can't think of anything.

Let's just look at the behavior that TrueClass and FalseClass have: there's exactly four methods there. No more. And in every single case, the two methods do exactly the opposite. How and why would you put that in a single class?

Here's how you implement all those methods:

class TrueClass
  def &(other)
    other
  end

  def |(_)
    self
  end

  def ^(other)
    !other
  end

  def to_s
    'true'
  end
end

And now the other way around:

class FalseClass
  def &(_)
    self
  end

  def |(other)
    other
  end

  def ^(other)
    other
  end

  def to_s
    'false'
  end
end

Granted, in Ruby, there is a lot of "magic" that is going on behind the scenes and that is not actually handled by TrueClass and FalseClass but rather hardwired into the interpreter. Stuff like if, &&, || and !. However, in Smalltalk, from which Ruby borrowed a lot including the concept of FalseClass and TrueClass, all of these are implemented as methods as well, and you can do the same thing in Ruby:

class TrueClass
  def if
    yield
  end

  def ifelse(then_branch=->{}, _=nil)
    then_branch.()
  end

  def unless
  end

  def unlesselse(_=nil, else_branch=->{})
    ifelse(else_branch, _)
  end

  def and
    yield
  end

  def or
    self
  end

  def not
    false
  end
end

And again the other way around:

class FalseClass
  def if
  end

  def ifelse(_=nil, else_branch=->{})
    else_branch.()
  end

  def unless
    yield
  end

  def unlesselse(unless_branch=->{}, _=nil)
    ifelse(_, unless_branch)
  end

  def and
    self
  end

  def or
    yield
  end

  def not
    true
  end
end

A couple of years ago, I wrote the above just for fun and even published it. Apart from the fact that the syntax looks different because Ruby uses special operators while I use only methods, it behaves exactly like Ruby's builtin operators. In fact, I actually took the RubySpec conformance testsuite and ported it over to my syntax and it passes.

Jörg W Mittag
Thank you, your comment is very extensive. I've already given an example of common behaviour - string parsing, as in Boolean.parse("true") => true. You have a point that they have 4 methods and they do the opposite. And yet I can't admit that they are not similar; after all, `true.methods == false.methods`. Both walk and sound like ducks, method-wise.
egarcia
+2  A: 

It seems that Matz himself answered this question on a mailing list message in 2004.

Short version of his answer: "right now it works ok, adding a Boolean doesn't give any advantage".

Personally I don't agree with that; the aforementioned "string parsing" is one example. Another one is that when you are applying different treatment to a variable depending on its type, (i.e. a yml parser) having a "Boolean" class is handy - it removes one "if". It also looks more correct, but that's a personal opinion.

egarcia
I think it adheres to the ruby philosophy that if you miss a Boolean class and ruby doesn't offer it, to simply define it yourself!
jug
I don't see how I could. I know that classes are open but ... I'm not sure their hierarchy is. Right know TrueClass and FalseClass depend directly from Object. I'd have to insert my Boolean class in between. Don't think its possible, but would be glad if you proved me wrong.
egarcia