In one of my favorite plays, The Importance of Being Earnest, Oscar Wilde wrote, “The truth is rarely pure and never simple.” When I first encountered truthiness in Python, I was reminded of this quote:

>>> bool(None)
False
>>> bool(0)
False
>>> bool(1)
True
>>> bool([])
False
>>> bool([1])
True

The truth is definitely not simple.

Especially considering other languages, like Ruby, define truth values differently. Since Ruby doesn’t have a bool method, we’ll use !! to double negate and get the boolean values:

=> !!nil
false
=> !!0
true
=> !!1
true
=> !![]
true
=> !![1]
true

Python treats empty and zero values as False, while Ruby treats anything except nil and false as true. Indeed, from the python docs, we can confirm that nil (None), false (False), zeros of any numeric type, and empty sequences or collections all have a boolean evaluation of False.

What’s more, on any class, we can write our own __bool__(self) method to define the boolean value when bool(that_class) is evaluated. If __bool__() isn’t defined for a class, Python will fall back on the __len__(self) method and return False for anything with zero length, True for anything with a nonzero length. If neither is defined, the object is always evaluated as True.

Who cares?

Well, me, clearly. But seriously, what difference does it make?

In Ruby, I often find myself writing if some_list.empty? or if some_number.zero? whereas in Python this would simply be if some_list or if some_number. In Ruby, these two would only evaluate to false in the case that some_list or some_number was actually nil or not yet defined. This .zero? or .empty? check is unnecessary in Python.

Implementing Truthiness

As many refrigerator magnets will confirm, experience is the best teacher. So, in order to more fully understand truthiness, I implemented it in the interpreter I’m writing. I’ve been following Writing an Interpreter in Go (see this post also based on the book about Lexing Floats).

The interpreter defines an Object interface which is implemented by a variety of different types including Boolean, Integer, Float, String, Array and Hash.

To implement truthiness in the interpreter, I required each type which implemented the Object interface to also define its own Falsey value. (Aside: doing research for this, I noticed Falsy is usually spelled without an e, but I like it better with the e, so am keeping the slight misspelling in my interpreter.)

Functions we’ll need

As discussed above, truthiness is implemented in Python through the __bool__() method. But, in my interpreter, I’d like to implement it slightly differently. Instead of defining a Bool() function right on each object, I’d instead like to explicitly define the Falsey() values for a given object (so I can re-use this in other places). I can then combine it with an Equal() function on objects to produce a boolean value. Similarly, this Equal() function is helpful to have in the interpreter outside of the context of Truthiness. Let’s get to it!

Here are the two method signatures defined by the Object interface which we’ll need for truthiness:

type Object interface {
    ...
    Falsey() Object
    Equal(o Object) bool
}

Falsey()

Falsey() will allow us to define a Falsey() instance for each Object. For numbers, this will be the zero value; for collections, this will be the empty value. For example:

func (i *Integer) Falsey() Object { return &Integer{Value: 0} }
func (a *Array) Falsey() Object { return &Array{Elements: []Object{}} }

Equal()

Equal() is the other method we’ll need to fully implement truthiness. After all, we need to be able to check if an object is equivalent to its type’s Falsey(). For numerics and String, Equal() is straightforward:

func (f *Float) Equal(o Object) bool {
    comparison, ok := o.(*Float)
    return ok && comparison.Value == f.Value
}

func (s *String) Equal(o Object) bool {
    comparison, ok := o.(*String)
    return ok && comparison.Value == s.Value
}

For collections, it requires a little recursion:

func (a *Array) Equal(o Object) bool {
    comparison, ok := o.(*Array)
    if !ok || len(comparison.Elements) != len(a.Elements) {
        return false
    }

    // Iterate over all elements and compare them
    for i, el := range a.Elements {
        if !el.Equal(comparison.Elements[i]) {
            return false
        }
    }

    // If we haven't returned yet, all elements are equal
    return true
}

We have to iterate through all elements and check that they are equal to one another.

Bool()

But, the goal was to define a Bool() function on objects which will return an object’s Truthiness. And now, with both Falsey() and Equal() methods defined on Objects, we can write our Bool() function as follows:

func Bool(o Object) bool { return !o.Equal(o.Falsey()) }

and with a small addition to define it as a builtin function in the interpreter, we now have our own repl-checkable truthiness:

>> bool(nil)
false
>> bool(0)
false
>> bool(1)
true
>> bool([])
false
>> bool([1])
true

I’ll leave it to you to decide whether Wilde’s words apply here, or if we have defined a pure and simple truth for this interpreter.