How it works...

Once we have defined the operator, we can write top-level functions that implement the behavior for each pair of types: one on the left-hand side (LHS) and one on the right-hand side (RHS). Method parameter overloading allows us to specify the operator implementation for multiple-type pairings.

We can also implement it for our own custom types. Let's create Task and TaskList, which might benefit from using the operator:

struct Task { 
let name: String
}

class TaskList: CustomStringConvertible {
private var tasks: [Task] = []
func append(task: Task) {
tasks.append(task)
}
var description: String {
return tasks.map { $0.name }.joined(separator: "\n")
}
}

We'll add CustomStringConvertible conformance so that we can easily print out the result.

An alternative to implementing the use of an operator as a top-level function is to declare it within the relevant type as a static function. We'll declare it within an extension on our TaskList object, but we can just as easily declare it within the main TaskList class declaration:

extension TaskList { 
static func >>> (lhs: Task, rhs: TaskList) {
rhs.append(task: lhs)
}
}

Implementing this within a type has a few advantages: The implementation code is right next to the type itself, making it easier to find, and taking advantage of any values or types that might have a private, or otherwise restricted, access control, which will prevent them from being visible to a top-level function.

Now we can use our >>> operator to append Task to a TaskList:

let shoppingList = TaskList() 
print(shoppingList)
Task(name: "get milk") >>> shoppingList
print(shoppingList)
Task(name: "get teabags") >>> shoppingList
print(shoppingList)