I’m designing my own programming language (called Lima, if you care its on http://www.btetrud.com), and I’m trying to wrap my head around how to implement operator overloading. I’m deciding to bind operators on specific objects (its a prototype based language). (Its also a dynamic language, where ‘var’ is like ‘var’ in javascript – a variable that can hold any type of value).
For example, this would be an object with a redefined + operator:
x =
{ int member
operator +
self int[b]:
ret b+self
int[a] self:
ret member+a
}
I hope its fairly obvious what that does. The operator is defined when x is both the right and left operand (using self to denote this).
The problem is what to do when you have two objects that define an operator in an open-ended way like this. For example, what do you do in this scenario:
A =
{ int x
operator +
self var[b]:
ret x+b
}
B =
{ int x
operator +
var[a] self:
ret x+a
}
a+b ;; is a's or b's + operator used?
So an easy answer to this question is “well duh, don’t make ambiguous definitions”, but its not that simple. What if you include a module that has an A type of object, and then defined a B type of object.
How do you create a language that guards against other objects hijacking what you want to do with your operators?
C++ has operator overloading defined as “members” of classes. How does C++ deal with ambiguity like this?
Most languages will give precedence to the class on the left. C++, I believe, doesn’t let you overload operators on the right-hand side at all. When you define
operator+, you are defining addition for when this type is on the left, for anything on the right.In fact, it would not make sense if you allowed your
operator +to work for when the type is on the right-hand side. It works for +, but consider -. If type A definesoperator -in a certain way, and I do int x – A y, I don’t want A’soperator -to be called, because it will compute the subtraction in reverse!In Python, which has more extensive operator overloading rules, there is a separate method for the reverse direction. For example, there is a
__sub__method which overloads the – operator when this type is on the left, and a__rsub__which overloads the – operator when this type is on the right. This is similar to the capability, in your language, to allow the “self” to appear on the left or on the right, but it introduces ambiguity.Python gives precedence to the thing on the left — this works better in a dynamic language. If Python encounters
x - y, it first callsx.__sub__(y)to see ifxknows how to subtracty. This can either produce a result, or return a special valueNotImplemented. If Python finds thatNotImplementedwas returned, it then tries the other way. It callsy.__rsub__(x), which would have been programmed knowing thatywas on the right hand side. If that also returnsNotImplemented, then aTypeErroris raised, because the types were incompatible for that operation.I think this is the ideal operator overloading strategy for dynamic languages.
Edit: To give a bit of a summary, you have an ambiguous situation, so you really only three choices:
The only other option is to introduce a complex system of precedence to the operator overloads, but then you said you want to reduce the cognitive overhead.