81

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.

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
kikito
  • 51,734
  • 32
  • 149
  • 189

9 Answers9

64

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
  • 363,080
  • 75
  • 446
  • 653
  • 10
    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. – kikito Jul 07 '10 at 16:33
  • 16
    The purpose of a class is to group similar objects together (among other things). I agree --- and the fact that these two classes have the exact same four methods suggests to me that they might have the same type. That they do not share any implementation is a method inheritance issue, not a typing issue. – alexloh Jul 21 '11 at 00:24
  • 6
    @alexloh: I never said they don't have the same type. I agree with you: they *do* have the same type. But that doesn't have anything to do with their classes: Ruby is an object-oriented language. In an object-oriented language, the type(s) of an object is/are defined by its protocol(s), not its class, unlike ADT-oriented languages like Java, C# or C++ where classes *are* types, but that doesn't apply to Ruby because it is OO not ADT-oriented. In an object-oriented language, the purpose of a class is implementation sharing, but `true` and `false` *don't* share any implementation. – Jörg W Mittag Jul 21 '11 at 01:12
  • @kikito: Is better to do that with `eval` as something like `!!eval("true")`? I personally would prefer to have .to_bool for everything or even bool(), but its fine. – alanjds Jun 13 '12 at 22:43
  • @AlanJustinoDaSilva: I have no idea about the eval part. In ruby, everything has a to_bool - it's just that everything returns true except nil and false :) – kikito Jun 14 '12 at 06:42
  • This answer hits the bullseye. It is not that Matz has anything against George Boole, it is that Ruby builds on Smalltalk conventions, and separate `TrueClass` and `FalseClass` removes the need for branching in the instance methods, which do the exact opposite for _true_ and _false_. – Boris Stitnicky Aug 05 '14 at 16:40
  • The `TrueClass#&` method should return a `false` or `true`. This implementation could return something else. Similar problem with `FalseClass#^` – Mark Swardstrom Jun 08 '16 at 19:11
  • It's worth noting, many years later, that `true` and `false` both walk, but they don't walk like ducks. And they both make noises, but they don't quack like ducks. The interfaces are the same (so they have the same type) but the implementations are different (so they don't have the same class.) – Matty K Nov 03 '16 at 06:39
  • 1
    @JörgWMittag With respect, I don't see two classes that don't share an implementation; I see an obvious missed opportunity for abstraction. I also see legitimate use-cases for a common ancestor, as have plenty of others. I remain unconvinced. – aidan Aug 14 '17 at 07:06
  • _Can you give an example of what sort of common behavior you would implement in a Boolean class? I can't think of anything._ Everyone keeps mentioning String parsing. Look at `OptionsParser`. I can write `opts.on("flag=VALUE", "Do or do not!") {|value| flag = value}`, but if on the CLI I type `--flag=false`, it will convert `value` to the String `"false"` which in a condition evaluates to `True`. The logical thing to do would be to pass an argument to `on()` to let if know the value is a `Boolean`, but OH THAT'S RIGHT!!! There IS NO BOOLEAN TYPE! – Toddius Zho Sep 27 '17 at 15:09
  • _Can you give an example of what sort of common behavior you would implement in a Boolean class? I can't think of anything._ Sorry, I don't buy it. By that reasoning, every number should be its own type. Numbers don't have exactly opposite behaviour, just different behaviour based on their values. The only reason that Booleans have opposite behaviour is because there is only two states, and their different behaviour is based on that. Putting them into types doesn't negate that, it just hides it. Could say that it is convenient under certain circumstances, but that's all. – Adrian Jun 16 '21 at 14:43
  • I don't think this should be the top answer. True and False are similar in that they are the same datatype and should be consistant with the implementations in pretty much every other programming language. They are essentially base 1 binary numbers. Ruby treats types as objects so should use have a Boolean class. – Ben Jun 21 '22 at 11:27
27

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.

kikito
  • 51,734
  • 32
  • 149
  • 189
  • 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! – j.p. Jul 16 '10 at 18:05
  • 5
    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. – kikito Jul 16 '10 at 21:30
  • 2
    It seems to me that having TrueClass and FalseClass both be subclasses of Boolean would give you the best of both worlds: Distinct classes for their distinct behavior, but a common base class to which you can add common behavior/implementation. It would also let you do things like true.is_a? Boolean, which feel like they are currently missing. But like @kikito said, this would have to be changed at the language level; we can't change a class's superclass at runtime. – Tyler Rick Jun 19 '13 at 19:04
  • At the same time, I can see Matz's point: true is only a representative value of truth. "string" and 5 are also true. "In Ruby, everything behaves as a Boolean value" [http://www.ruby-forum.com/topic/4412411] – Tyler Rick Jun 19 '13 at 19:16
  • 3
    @kikito: I guess it could be done with modules. See http://www.ruby-forum.com/topic/60154#56346: module Boolean; end [true, false].each do |obj| obj.extend(Boolean) end – Tyler Rick Jun 19 '13 at 19:18
6

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....

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
steenslag
  • 79,051
  • 16
  • 138
  • 171
3

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
  • 1,195
  • 1
  • 11
  • 19
  • 4
    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 Jul 07 '10 at 18:58
  • 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 Jul 07 '10 at 19:06
  • 3
    @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 Jul 08 '10 at 18:40
  • 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 Jul 08 '10 at 23:20
  • 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 Jul 11 '10 at 19:36
  • 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 Jul 12 '10 at 04:53
3

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).

Slipp D. Thompson
  • 33,165
  • 3
  • 43
  • 43
  • 2
    I think that a bigger idea on ruby is making the frequent tasks simple. Take String - most of its methods could be implemented in terms of regular expressions, gsub and monkeypatching, but they are instead already "baked in" the class, which is just convenient. It is a bit strange that booleans aren't deemed worthy of the same treatment. Nevertheless, your post could be useful for someone else, so +1. – kikito Mar 22 '11 at 08:46
  • Isn't that the big idea behind programming in the first place? ;-D – Slipp D. Thompson Mar 22 '11 at 11:39
  • I see your point, but it still holds that if one really needs a Boolean class, they're free to make their own. In real-world usage, an actual Boolean class is rarely needed; only about as often as a TriBoolean class (false, true, indeterminate) or a QuadBoolean class (false, true, neither, both). If you need more-specific behavior, just make it yourself; Ruby provides just what's safe and sane for all. – Slipp D. Thompson Mar 22 '11 at 11:45
  • I can certainly program a Boolean class myself and create my own TRUE and FALSE constants as instances of it. However, I'm not able to "insert" Boolean in between Object and TrueClass and FalseClass; in other words, I can't make true and false instances of Boolean. – kikito Mar 22 '11 at 11:48
  • There's no need to insert it anywhere; you're over-thinking the problem. Just create a normal class (inheriting from Object) and use the 'true' and 'false' constants that already exist language-wide. – Slipp D. Thompson Mar 22 '11 at 12:35
  • I don't know what you mean by "using" them. I can't change their classes. – kikito Mar 22 '11 at 14:27
  • Use them via [composition](http://en.wikipedia.org/wiki/Object_composition) rather than [inheritance](http://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)). – Slipp D. Thompson Mar 23 '11 at 00:06
  • egarcia: If your question has shifted from “why isn't there a Boolean class” to “how would I create a custom Boolean class”, it wouldn't hurt to post a new question to SO. – Slipp D. Thompson Mar 23 '11 at 00:13
  • My question has not shifted. I was requesting clarifications on your comment, that is all. I didn't thought you were referring to composition when you said "make their own Boolean class". I don't like composition on this case, for a number of reasons - not enough space here. Feel free to open a question if you are interested :) – kikito Mar 23 '11 at 11:47
  • The more I think about it, the reason why this works the way it does is because Ruby is a blend of adding the necessary behavior to something else (classical functional) and constructing your own custom structures (classical OO). And it does a pretty good job of using functional philosophies to enhance OO constructs and vice versa. That said, Matz's answer (referenced in the reply by @kikito) does sum it up: There's no need for a Boolean class… yet. That really is the core of Ruby: if it's simple, do it functional. If you need more structure than that, it's easy to adapt to an OO solution. – Slipp D. Thompson Oct 26 '11 at 16:03
  • Another good example: Symbols vs. Strings. Symbols are immutable, persistent, and global (like `TrueClass` and `FalseClass`), while Strings are mutable and instance-oriented (like `Number`). Besides that, they both have roughly the same capabilities. They're just just tuned differently for different purposes. – Slipp D. Thompson Oct 26 '11 at 16:05
  • @SimonB. I'm not entirely sure what you're asking. `false` and `nil` equal false. Everything else equals true. – Slipp D. Thompson Jan 21 '16 at 15:55
  • 1
    @SlippD.Thompson I mean the string value when I write it enclosed in "double quote". But thanks; repasted and clarified: Is there any agreement on what equals false? Maybe also the string "nil" and the string "null" and the symbol :null and some other things? Silly, I know. Any pre-made boolean parser would force opinions on the user, and tempt to use the built-in parser and get bugs, instead of looking at the domain and taking responsibility for the parsing. – Simon B. Jan 22 '16 at 16:54
  • 1
    @SimonB. If you're looking for some kind of existing standard on truthy strings, your options are limited. JavaScript equates only `"1"` to true when using `==`; all other strings are false. PHP is nearly the opposite— `"0"` and `""` `==` false; all other strings true. Perl & Python (AFAIK) work like Ruby— all strings are true. In Java, `Boolean.parseBoolean()` will give you true for `"true"` and false for every other string. C#'s `Boolean.Parse()` will give you (after whitespace-stripping and downcasing) true for `"true"`, false for `"false"`, and throws on everything else. – Slipp D. Thompson Jan 22 '16 at 17:59
  • 1
    @SimonB. So your options for conformance are limited, primarily by design. I suggest either making your extension highly and obviously class- or app-specific, or giving it a funky method name (e.g. `trueish?`) that makes it clear that the assumptions made by using this method are loose and ad hoc, not based on any standard. – Slipp D. Thompson Jan 22 '16 at 18:02
1

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)...

Chucky
  • 545
  • 7
  • 14
  • 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? – kikito Jul 07 '10 at 16:40
  • 6
    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 Jul 07 '10 at 18:54
1

Joining TrueClass and FalseClass with a kind of Boolean super-class breaks the LSP (Liskov Substitution Principle, ) - then "any code using Boolean should be able use any it's descendant transparently". It is not achievable for TrueClass and FalseClass since they have opposite behavior.

Btw, LSP is the (L) of SOLID.

Looks like it is the good example of LSP in real life.

UPD#1: regarding does LSP broken or not

COMMON_EXPECTED_VALUE = x

def test_LSP(klass)
  assert klass.new.value == COMMON_EXPECTED_VALUE
end

test_LSP(FooClass)
test_LSP(BarClass)

Once you have got both green - you have chances to not break the LSP joining this classes with the common superclass, of course if you will get double green for all features of future superclass.

x'ES
  • 554
  • 5
  • 15
  • Sorry but I don't agree. The number 0 has "opposite behavior" to the rest of the numbers when multiplying, and adding. Yet we don't have a ZeroClass. The number 1 also behaves a bit "especially" when doing multiplication. You are treating Booleans as if the main thing they do is "activate (or not activate) a conditional". To me the main thing they do is "going inside a conditional". In that sense False and True do the same. LSP is respected. – kikito Aug 19 '22 at 22:10
  • While True/False is constants managed by some Boolean class - it's one case, similar to having the 0 (zero) constant for the Integer class. But in Ruby we have True & False not as constant, but as Class. This way some generic Boolean class my composite True/False classes inside, but can't be the superclass for them. Nesting True/False from Boolean in Ruby breaks LSP - it's my main point. But Boolean may be implemented in other way, excluding nesting. – x'ES Aug 21 '22 at 18:09
0

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
  • 11,337
  • 9
  • 44
  • 72
-2

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
Volte
  • 1,905
  • 18
  • 25