This article is also available in Chinese

In Python, zero and None, as well as empty lists, dictionaries, and strings, are falsy. If something is falsy, it means that it can be used in the condition of an if statement, and it’ll follow the else branch. For example, in Python:

if []:
	# will not be evaluated
else:
	# will be evaluated
	
if 0: 
	# will not be evaluated
else:
	# will be evaluated

In Swift, on the other hand, only true booleans can be used in an if statement. Using nil, an empty array, or really any other type in an if statement simply won’t compile:

if Array<String>() {
// error: type 'Array<String>' does not conform to protocol 'BooleanType'

Both Python and Swift are consistent, which is a great quality to have. In Swift, you know that nothing besides a boolean will work in an if statement. In Python, you know every collection type, integer, nil value (None), and boolean will work. Consistency seems easy, until you notice how bad other languages get it.

JavaScript, for example, is a real shitshow. false, null, and undefined are falsy, which is more or less fine. But 0 and "" are falsy as well, even though [] and {} are truthy. (And don’t even bother trying to describe JavaScript’s equality behavior.)

Objective-C, love it though I do, is also an example of an inconsistent language in this respect. nil, NO, false, and 0 are falsy, while @[], @{}, @0, and @NO are truthy. Ruby is mostly good, allowing only nil and false to be falsy. I prefer Swift’s absoluteness strictness to Ruby’s behavior.

Consistency is good, but even better is utility. Swift’s falsiness rules are good, but Python’s are useful.

I have two pieces of evidence for why Python’s rules are more useful than Swift’s (and pretty much every other language.)

The first piece of evidence is the present? methods in Rails’s ActiveSupport. I’ve written about present? here before, but, in brief, it’s a way to get around the fact that the only thing that isn’t mutable in the Ruby runtime is falsiness. Every object has a chance to describe if it’s #present?. nil is not present, as well as the empty array and string. You can override present? for your own objects as well, if you have custom collections or null objects. This lets you write code like:

if myObject.present? {
	
} else {

}

to override Ruby’s stubborn falsiness. ActiveSupport’s presence is just plain useful, which is why calls to .present? (and .blank?, its opposite) are littered all over Rails codebases.

The second piece of evidence I have is how awkward Swift is at dealing with optional values in conditionals. Pay attention to how much you’re checking if a thing is empty or nil (blank?, in Ruby parlance) in your code. It’s really frequent for me. For example, the text property on UITextField is optional. If you want to check if the text is present, you have to do this awkward dance:

if let text = textField.text where !text.isEmpty {
	// we have some text
} else {
	// the string is either empty or nil
}

If you think this code is not awkward, try reversing the conditional. I’ll wait. (Here’s a hint: you can’t just remove the not operator.)

Soon, you’re adding a method onto Optional specifically for strings:

protocol TextContaining {
    var isEmpty: Bool { get}
}

extension String: TextContaining { }

extension Optional where Wrapped: TextContaining {
    var isEmpty: Bool {
        switch self {
        case let .Some(value):
            return value.isEmpty
        case .None:
            return true
        }
    }
}

You don’t have to live like this! You deserve nice things! (Like Python’s falsiness.)

Swift, unlike all of the other languages I’ve mentioned here, is a highly dynamic language. It will allow you to add code to change the compiler’s falsiness behavior.

Here are the docs for BooleanType:

Types that conform to the BooleanType protocol can be used as the condition in control statements (if, while, C-style for) and other logical value contexts (e.g., case statement guards).

Yessssss.

Only three types provided by Swift, Bool, DarwinBoolean, and ObjCBool, conform to BooleanType. Expanding this set to include types that represent more than simple boolean values is discouraged.

Sorry, Chris Lattner, I’m going for it.

extension String: BooleanType {
    public var boolValue: Bool {
        return !self.isEmpty
    }
}

Done! Now, we can use strings in conditionals. This code compiles and works as expected:

if "" {
	// this code will not be executed
} else {
	// this code will be executed	
}

We can do the same for Dictionary and Array. For optional, we should check if the Wrapped type conforms to BooleanType:

extension Optional: BooleanType {
    public var boolValue: Bool {
        switch self {
        case .None:
            return false
        case .Some(let wrapped):
            if let booleanable = wrapped as? BooleanType {
                return booleanable.boolValue
            }
            return true
        }
    }
}

Now, if you have a Boolean wrapped in an Optional, it will do the thing you expect, instead of giving you a compiler error, and you no longer have to do the weird optionalBool ?? true workaround.

The big question is “Should I do this in production?”. The answer is…maybe? If it’s a library that goes into other people’s apps, definitely do not do this. If you do it in your app, it shouldn’t break any third party code: because it wouldn’t compile, they couldn’t use it anyway. And Swift is still strongly typed, so you can’t ever compile the code myArray == false.

I think it’s great that Swift is built on small composable pieces (like BooleanType) that the standard library uses to define itself within the language. (Types like ArrayLiteralConvertible follow a similar pattern.) It’s also surprising that none of the “dynamic” languages that we’re used to allow this kind of mutation of fundamental language structure. In the meantime, I have to decide if I want to try to use this anywhere.