Skip to content

Operator Overloading

Matt Basta edited this page Apr 24, 2015 · 3 revisions

Operator overloading is an extremely powerful feature in BType. Essentially, operator overloading extends the ability to do a number of things:

  • Replace the functionality of existing binary operators (allowing for new behavior, such as divide-by-zero logging or prevention)
  • Implement support for binary operators on custom types (allowing for functionality like custom numeric types)
  • Add external support for implicit type casting
  • etc.

The syntax for operator overloading is straightforward:

operator (type:left * type:right) type {
    # ...
}

type represents a type name. left and right are the parameter names for the left and right halves of the binary operator. * in this case is the operator that is being overloaded. The type outside the parens is the return type of the binary operator.

Operators that can be overloaded include the following:

  • +
  • -
  • *
  • /
  • %
  • &
  • |
  • ^
  • <<
  • >>
  • and
  • or
  • <
  • <=
  • >
  • >=
  • ==
  • !=

Constraints

Currently, operator overloading is global. This has benefits, notably that it is simple to add this functionality to the entire program without needing to re-import it in every file that requires the behavior. The downside is that any file--even third party files--can add potentially unexpected operator overloads.

Operator overloading is static. That is, operator overloads cannot be conditionally defined. They must be present in the root of the file (not within any other language constructs). They are implemented at compile time.

Operator overload definitions can use operator overloading. That is, binary operators within an operator declaration will use globally defined operator overloads. If a binary operator within the operator overload declaration matches the declaration's prototype, it will recursively call the operator.

Recursion Warning

Note that naive operator overloading can lead to application crashes. For example, consider the following:

func int:main() {
    return 123 + 456;
}

operator (int:x + int:y) int {
    return x * y + 2;
}

Irrelevant as to the motive of the above example, the code will never complete. That is because x * y + 2 will be compiled into a call to $foo(x * y, 2) where $foo is the operator overload. Because x * y is an int, it will happily recursively call the operator overload definition.

Nullability Warning

Because null is typed, it is possible for the left or right parameter passed to the operator overloading function to be null. Consider the following code:

object Foo {
  int:x;
}

operator (Foo:left + Foo:right) int {
    return left.x + right.x;
}

Foo:left = new Foo();
Foo:right = null;

debug.printint(left + right);

Be aware that right will contain null. It is the developer's responsibility to ensure that both the left and right expressions are valid before using them.

Future

In the future, operator overloading will support unary operators and other types of language constructs, such as subscripts. This is dependant on other language features landing, and as such, is not on any short-term roadmaps:

operator (MyCustomType:me[int:x]) int {
    return me.get(x);
}
operator (~MyCustomType:me) int {
    return me.complement(x);
}

Clone this wiki locally