Why does Ruby have TrueClass and FalseClass instead of a single Boolean class?

RubyLanguage Design

Ruby Problem Overview


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.

Ruby Solutions


Solution 1 - Ruby

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][1]. 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][2] and [ported it over to my syntax][3] and it passes. [1]: http://JoergWMittag.GitHub.Com/b001e/ [2]: https://GitHub.Com/RubySpec/RubySpec/blob/master/language/if_spec.rb [3]: https://GitHub.Com/JoergWMittag/B001e/tree/master/spec/

Solution 2 - Ruby

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.

Solution 3 - Ruby

Quoting Matz on Ruby forum (2013):

> ...There's nothing true and false commonly share, thus no Boolean class. Besides that, in Ruby, everything behave as Boolean value....

Solution 4 - Ruby

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.

Solution 5 - Ruby

Since everything but false and nil evaluate to true in Ruby by default, you would only need to add parsing to String.

Something like this could work:

class Object
  
  ## Makes sure any other object that evaluates to false will work as intended,
  ##     and returns just an actual boolean (like it would in any context that expect a boolean value).
  def trueish?; !!self; end
  
end

class String
  
  ## Parses certain strings as true; everything else as false.
  def trueish?
    # check if it's a literal "true" string
    return true if self.strip.downcase == 'true'
    
    # check if the string contains a numerical zero
    [:Integer, :Float, :Rational, :Complex].each do |t|
      begin
        converted_number = Kernel.send(t, self)
        return false if converted_number == 0
      rescue ArgumentError
        # raises if the string could not be converted, in which case we'll continue on
      end
    end
    
    return false
  end
  
end

When used, this would give you:

puts false.trueish?   # => false
puts true.trueish?    # => true
puts 'false'.trueish? # => false
puts 'true'.trueish?  # => true
puts '0'.trueish?     # => false
puts '1'.trueish?     # => true
puts '0.0'.trueish?   # => false
puts '1.0'.trueish?   # => true

I believe part of the “big idea” behind Ruby is to just make the behavior you desire inherent to your program (e.g. boolean parsing), rather creating a fully encapsulated class that lives in it's own namespace world (e.g. BooleanParser).

Solution 6 - Ruby

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 thing and then branch. If you had Boolean class, you'd have to convert the evaluation of thing 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)...

Solution 7 - Ruby

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"

Solution 8 - Ruby

As others have said, you could "patch" Ruby. Create your own class. Here is something I came up with. The methods on the Boolean class are a bit silly, but they could be useful programmatically at some point.

class Boolean
  def self.new(bool)
    bool
  end

  def self.true
    true
  end

  def self.false
    false
  end
end

class FalseClass
  def is_a?(other)
    other == Boolean || super
  end

  def self.===(other)
    other == Boolean || super
  end
end

class TrueClass
  def is_a?(other)
    other == Boolean || super
  end

  def self.===(other)
    other == Boolean || super
  end
end

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionkikitoView Question on Stackoverflow
Solution 1 - RubyJörg W MittagView Answer on Stackoverflow
Solution 2 - RubykikitoView Answer on Stackoverflow
Solution 3 - RubysteenslagView Answer on Stackoverflow
Solution 4 - RubyAsherView Answer on Stackoverflow
Solution 5 - RubySlipp D. ThompsonView Answer on Stackoverflow
Solution 6 - RubyChuckyView Answer on Stackoverflow
Solution 7 - RubyMongus PongView Answer on Stackoverflow
Solution 8 - RubyVolteView Answer on Stackoverflow