Boolean

The boolean Bool type in Swift underlies a lot of primitive functionality, making it an interesting demonstration of how to build a simple type. This post walks through the creation of a new MyBool type designed and implemented to be very similar to the Bool type built into Swift. We hope this walk through the design of a simple Swift type will help you better understand how the language works.

Let’s start with the basic definition. The MyBool type models two different cases, perfect for an enum:

enum MyBool {
	case myTrue, myFalse
}

To reduce confusion in this post, we’ve named the cases myTrue and myFalse. We want MyBool() to produce a false value, and can do so by providing an init method:

extension MyBool {
	init() { self = .myFalse }
}

Swift enum declarations implicitly scope their enumerators within their body, allowing us to refer to MyBool.myFalse and even .myFalse when a contextual type is available. However, we want our type to work with the primitive true and false literal keywords. To make this work, we can make MyBool conform to the BooleanLiteralConvertible protocol like this:

extension MyBool : BooleanLiteralConvertible {
	static func convertFromBooleanLiteral(value: Bool) -> MyBool {
		return value ? myTrue : myFalse
	}
}

// We can now assign 'true' and 'false' to MyBool.
var a : MyBool = true

With this set up, we have our basic type, but we still can’t do much with it. Booleans need to be testable within an if condition. Swift models this with the BooleanType protocol, which allows any type to be used as a logical condition:

extension MyBool : BooleanType {
	var boolValue: Bool {
		switch self {
		case .myTrue: return true
		case .myFalse: return false
		}
	}
}

// Can now test MyBool in 'if' and 'while' statement conditions.
if a {}

We also want anything that conforms to BooleanType to be castable to MyBool, so we add:

extension MyBool {
	// MyBool can be constructed from BooleanType
	init(_ v : BooleanType) {
		if v.boolValue {
			self = .myTrue
		} else {
			self = .myFalse
		}
	}
}

// Can now convert from other boolean-like types.
var basicBool : Bool = true
a = MyBool(basicBool)

Note that the use of _ in the initializer argument list disables the keyword argument, which allows the MyBool(x) syntax to be used instead of requiring MyBool(v: x).

Now that we have basic functionality, let’s define some operators to work with it, starting with the == operator. Simple enums that have no associated data (like MyBool) are automatically made Equatable by the compiler, so no additional code is required. However, you can make arbitrary types equatable by conforming to the Equatable protocol and implementing the == operator. If MyBool weren’t already Equatable, this would look like this:

extension MyBool : Equatable {
}

func ==(lhs: MyBool, rhs: MyBool) -> Bool {
	switch (lhs, rhs) {
	case (.myTrue,.myTrue), (.myFalse,.myFalse):
		return true
	default:
		return false
	}
}

// Can now compare with == and !=
if a == a {}
if a != a {}

Here we’re using some simple pattern matching in the switch statement to handle this. Since MyBool is now Equatable, we get a free implementation of the != operator. Lets add binary operations:

func &(lhs: MyBool, rhs: MyBool) -> MyBool {
	if lhs {
		return rhs
	}
	return false
}

func |(lhs: MyBool, rhs: MyBool) -> MyBool {
	if lhs {
		return true
	}
	return rhs
}

func ^(lhs: MyBool, rhs: MyBool) -> MyBool {
	return MyBool(lhs != rhs)
}

With the basic operators in place, we can implement a variety of helpful unary and compound assignment operators as well, for example:

prefix func !(a: MyBool) -> MyBool {
	return a ^ true
}

// Compound assignment (with bitwise and)
func &=(inout lhs: MyBool, rhs: MyBool) {
	lhs = lhs & rhs
}

The &= operator takes the left operand as inout because it reads and writes to it, and the effect must be visible to the user of the operator. Swift gives you complete control over mutability of operations on value types like enum and struct.

With this, the simple MyBool type has all of the basic operations and operators. Hopefully this post gives you a few tips that you can apply to your own code when defining higher-level types.

All Blog Posts