---
title: Booleans don't exist in Ruby
teaser: 'An investigation into Ruby''s C, stdlib, interfaces, libraries, tooling,
  and ecosystem into what, exactly, Ruby thinks a Boolean is.

  '
tags: ruby
author: Mike Burns
published_on: 2022-07-15
---

Ruby has the literals `true` and `false`, which are the only constructors for
`TrueClass` and `FalseClass`, respectively. It's common to think of those as
Booleans. It can also make sense to think of them as bits, depending on the
need.

But to quote [Stafford Beer][], the purpose of a system is what it does. What
does `TrueClass` and `FalseClass` do?

[Stafford Beer]: https://www.informs.org/content/view/full/272741

## Conditionals

At a high level, `if` works on "truthy" values: the "then" clause is evaluated
unless the conditional is `false` or `nil`. The inverse is true for `unless`.

```ruby
if 0
  puts "this is run"
end
```

```ruby
if ""
  puts "this is also run"
end
```

```ruby
if []
  puts "you guessed it: run this line"
end
```

```ruby
if nil
  puts "never run"
end
```

```ruby
if false
  puts "also never run"
end
```

This is encoded in [the `RB_TEST` function][] in Ruby itself:

```c
static inline bool
RB_TEST(VALUE obj)
{
    return obj & ~RUBY_Qnil;
}
```

[the `RB_TEST` function]: https://github.com/ruby/ruby/blob/cb4e2cb55a59833fc4f1f6db2f3082d1ffcafc80/include/ruby/internal/special_consts.h#L123-L149

So we have our first definition: a Boolean in Ruby is one of two things:
`false` or `nil`, or anything else.

What the heck is that `obj & ~RUBY_Qnil` thing in C? Is that ...

### Object Identifiers

Every object in Ruby has a number assigned to it. The interpreter or virtual
machine sometimes uses that number for optimizations. You can see this number
with the `#object_id` method.

All instances of `Integer` -- that is, actual numbers -- have an odd object ID,
and all other constants have an even one.

```
irb(main):001:0> 1.object_id
=> 3
irb(main):002:0> 2.object_id
=> 5
irb(main):003:0> true.object_id
=> 20
irb(main):004:0> false.object_id
=> 0
irb(main):005:0> nil.object_id
=> 8
```

So we can see that true values are 20 and false values are 0. `nil`, which `if`
thinks is just as untrue as `false`, is 8.

```
irb(main):001:0> true.object_id.to_s(2)
=> "10100"
irb(main):002:0> false.object_id.to_s(2)
=> "0"
irb(main):003:0> nil.object_id.to_s(2)
=> "1000"
```

So for example:

```
irb(main):001:0> [].object_id & ~(nil.object_id)
=> 192
irb(main):002:0> nil.object_id & ~(nil.object_id)
=> 0
irb(main):003:0> false.object_id & ~(nil.object_id)
=> 0
irb(main):004:0> 1.object_id & ~(nil.object_id)
=> 3
irb(main):005:0> true.object_id & ~(nil.object_id)
=> 20
```

### OK but the C return type is `bool` ...

That's right, `RB_TEST` returns a `bool`, but the `&` operator produces numbers
like 192, 0, 3, 20, etc.

In C, Booleans are numbers: `0` is false and anything else is true. This is
[represented][], in C99, in the `_Bool` type (aliased to `bool`, as seen in
Ruby's C code):

> When any scalar value is converted to `_Bool`, the result is `0` if the value
> compares equal to `0`; otherwise, the result is `1`.

[represented]: https://www.enseignement.polytechnique.fr/informatique/INF478/docs/Cpp/en/c/language/boolean_type.html

That's right: Ruby's `if` and `unless` are truthy because C is truthy.

(You can also see this in the `RBOOL` macro, in case you're curious about
Ruby's C implementation. `RBOOL` will pop up again later!)

## Interfaces

OK sure, but that's some C. We're here to talk about Ruby, the duck-typed
programming language we love. What _interface_ do `TrueClass` and `FalseClass`
share?

```
irb(main):001:0> true.methods - false.methods
=> []
irb(main):002:0> true.methods - Object.methods
=> [:^, :&, :|]
```

They share the `#^`, `#&`, and `#|` methods, which look like little faces when
they're symbols. Now we're getting somewhere!

A Boolean in Ruby is anything that implements `#^`, `#&`, and `#|`. Case closed.

```
irb(main):003:0> nil.methods - Object.methods
=> [:to_h, :rationalize, :&, :to_f, :to_i, :to_a, :to_r, :to_c, :|, :^]
```

Oh hello, `nil`.

## Standard library

Ruby methods that end in `?` are typically predicate methods -- they are
expected to be used in conditional context such as `if` and `unless`. How does
the Ruby standard library define these methods?

I ran a quick Ripgrep over the Ruby stdlib:

```
~/ruby% rg -A5 'def .*\?' lib/
```

and skimmed through the 4600 lines of results. That's 713 method definitions,
but `-A5` shows the five lines after so I can see what they return.

And what I learned is that most of them are defined in terms of `&&`, `==`,
`||`, `#any?`, `#all?`, or other methods. We have to go back to C.

Some examples are `Proc#lambda?`:

```c
VALUE
rb_proc_lambda_p(VALUE procval)
{
    rb_proc_t *proc;
    GetProcPtr(procval, proc);

    return RBOOL(proc->is_lambda);
}
```

and `Complex#eql?`:

```c
static VALUE
nucomp_eql_p(VALUE self, VALUE other)
{
    if (RB_TYPE_P(other, T_COMPLEX)) {
	get_dat2(self, other);

	return RBOOL((CLASS_OF(adat->real) == CLASS_OF(bdat->real)) &&
			  (CLASS_OF(adat->imag) == CLASS_OF(bdat->imag)) &&
			  f_eqeq_p(self, other));

    }
    return Qfalse;
}
```

and `Symbol#casecmp?`:

```c
static VALUE
sym_casecmp_p(VALUE sym, VALUE other)
{
    if (!SYMBOL_P(other)) {
	return Qnil;
    }
    return str_casecmp_p(rb_sym2str(sym), rb_sym2str(other));
}
```

We see our friend `RBOOL` (`0` is false, anything else is true), an explicit
`Qfalse`, and even a `Qnil` (`str_casecmp_p` has additional return values of
`Qnil`).

From this we can learn that `false` and `nil` are falsy and anything else is
truthy. But is that really ... true?

## Third party libraries

Some major players are involved in the Boolean game. YARD needs to care about
return types and argument types. Sorbet and RBS are static analysis tools. And
Rails maps Ruby values to the database and back. What do they think?

YARD, Sorbet, and RBS each introduce the name "Boolean". [YARD defines][] it as
`true or false`; [Sorbet defines][] `Types::Boolean` as `OrType(trueClass,
falseClass)`. [RBS offers][] `Bool` as a "base type", like class, nil,
void, top, and bottom; this `Bool` [only matches][] on `TrueClass` or
`FalseClass`.

[YARD defines]: https://github.com/lsegal/yard/blob/359006641260eef1fe6d28f5c43c7c98d40f257d/lib/yard/parser/c/comment_parser.rb#L88
[Sorbet defines]: https://github.com/sorbet/sorbet/blob/c7524c1ad7aa512c766953f86d8091265f987f92/core/types/types.cc#L73-L76
[RBS offers]: https://github.com/ruby/rbs/blob/89f9403a1783f2b9277f0c07ea2ccea11cbfcd92/ext/rbs_extension/parser.c#L855-L856
[only matches]: https://github.com/ruby/rbs/blob/e5c69016df8974ec42131132c719f0dab8ef0fd0/lib/rbs/test/type_check.rb#L231-L232

Rails also introduces Booleans, in order to convert from HTML user agents or
databases into Ruby. [The `ActiveModel::Type::Boolean` class][], for example, can
produce three different values: `nil`, if the initial value is the empty
string; `false`, if the initial value is `false`, `"0"`, `"f"`, `"false"`, or
`"off"`; and `true` otherwise. Similar fun exists for the ActiveRecord
connection adapters, where a MySQL `tinyint(1)` gets parsed into either `true`
or `false`.

[The `ActiveModel::Type::Boolean` class]: https://github.com/rails/rails/blob/75a9e1be75769ae633a938d81d51e06852a69ea3/activemodel/lib/active_model/type/boolean.rb#L13-L22

This is the first time we encounter a traditional Boolean in Ruby, an
enumerable that can be exactly `true` or `false`. Rails, RBS, and Sorbet act as
a sort of translation project, mapping the expectations of the Language Server
Protocol, database, or user agent onto Ruby. All four of them attempt to map
how we are trained to think about software onto Ruby. You think there's a
Boolean? Well we can pretend!

## Booleans exist in the mind

And perhaps we get to the actual answer: Ruby has Booleans as much as you
believe they do.

Early Ruby programmers often came [from Perl][], where we have five things that
are treated as false (but, notably, no actual representation of a false or true
value itself). We were told that truthiness was more of a feeling and --
speaking for myself -- I found it constraining to imagine a world with fewer
than five false values. Empty list isn't false?!

[from Perl]: https://perldoc.perl.org/5.26.3/perlsyn#Truth-and-Falsehood

Further along we see Ruby devs playing with Haskell and, later, Rust. To make
their static analysis rigorous those languages support exactly two values in
their conditionals. This is when Rubyists starting adding `!!` before return
values to _force_ the value to be exactly one of `true` or `false`.

The concept of a Boolean is taught in computer science classes and programming
bootcamps. They exist as common terms shared between programmers; of course
we're going to express them in Ruby. But to what extent Ruby itself has
Booleans ebbs and flows, which truly is the beauty of Ruby itself. It lets us
express Boolean ideas when we want them, and steps out of the way when we
aren't interested.

Does Ruby have Booleans? That's a question for you and Ruby to decide together.
