on Custom Operators

hsoi blog, talk 0 Comments

One nifty feature of the Swift programming language is the ability to define your own custom operators.

Why would you want to do this? Because it can make reading and writing code better/easier. Just like factoring code into a function can help with maintenance, reusability, and readability, so can a custom operator. However, one must remember that the custom operator must enable maintenance, reusability, and readability — if it makes the code harder to work with, it’s not really an improvement.

And so with Swift being the new kid on the block, everyone wants to play with the new features. The thing is, custom operators are not some new thing in the grander world of programming languages. I spent a good number of years with custom operators in C++, and I can tell you that for the most part, custom operators don’t get and should not get used very often. It’s rare when they actually enhance code. But to that, I’d like to give one example.

Years ago I helped develop the then-leading C++ Mac application framework, PowerPlant. PowerPlant had a class, LSharable, which provided a way to implement reference counting (via a mixin class). Accompanying LSharable was a template class called TSharablePtr, which was a template class that facilitated the reference counting on some pointer-object. The typical use case was to make the TSharablePtr a class data member, thus reference counting of the managed pointer was “automatic” as the parent object came and went. The beauty in TSharablePtr was selective operator overloads that effectively enabled you to bypass the TSharablePtr and go right to the underlying object. That way in client code you didn’t have to worry about this TSharablePtr stuff that was really an implementation detail, and you just cut right to the object you cared about — and it looked exactly like you’d expect in code, with proper pointer dereferencing, etc.. The code read properly, neatly, and an implementation detail layer was removed. If you needed to know about it, it was there, but for the most part code was enhanced by these custom operators.

template<class T>
class TSharablePtr {
public:
      operator  T*() const     { return mSharablePtr; }
  T*  operator -> () const     { return mSharablePtr; }
};

This is just a small snippet of the TSharablePtr class. You can find LSharable and TSharablePtr with some Google searches.

But those custom operators allowed you to do things like:

class MyObject {
  TSharablePtr<Something>    theSomething;
};

Then in code:

   theSomething->DoSomething();

Which rolls very naturally. (Forgive me if my C++ is off; it’s been years since I’ve written any).

This is a case where custom operators made sense. They improved code readability and maintainability, without obscuring (too much) of the important bits. When we write code, we’re not writing it for the computer to understand, but for other humans to understand because code, once written, will have to be maintained. To be maintained, it must be understood, so we should strive to write code that is first understandable.

What does this mean for custom operators in Swift? I’d argue the same.

For instance, a recent discussion about if-let assignment strives to take this:

if let value = someOptionalValue as? String {
  self.value = value
}

to this:

self.value ?= someOptionalValue as? String

via:

infix operator ?= { associativity right precedence 90 }

func ?=<T>(inout left: T, right: T?) {
    if let value = right {
        left = value
    }
}

func ?=<T>(inout left: T?, right: T?) {
    if let value = right {
        left = value
    }
}

Do I think this is a huge gain? No, not really. But it does mark the first time I’ve seen someone offer a Swift custom operator that is actually useful and I’d consider using, because I do think it does offer potential to clean up the code, make it a little more readable. And the operator itself isn’t too difficult to figure out – if you know enough about the Swift language, you know what ? means, you know what compound assignment operators look like and do, and so this could be figured out (whereas the author’s original ||= is too obscure, IMHO). Yes it’s non-standard, so that’s a strike against it (whereas in the LSharable/TSharablePtr case, it was using existing operators in their well-defined way). But it has potential.

All in all, one must be careful about using new features simply because they’re novel. Custom operators ultimately are syntactic sugar and I’ve rarely found a time where they bring true improvement that couldn’t be brought about some other way; it may be more mundane, but code that gets too clever often tends to be difficult to understand and thus maintain. That said, they have their place. Just use them wisely.

Leave a Reply