Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dis-ambiguate operator definitions between objects/classes in a programming language?

I'm designing my own programming language (called Lima, if you care its on 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?

like image 218
B T Avatar asked Mar 16 '11 01:03

B T


1 Answers

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 defines operator - in a certain way, and I do int x - A y, I don't want A's operator - 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 calls x.__sub__(y) to see if x knows how to subtract y. This can either produce a result, or return a special value NotImplemented. If Python finds that NotImplemented was returned, it then tries the other way. It calls y.__rsub__(x), which would have been programmed knowing that y was on the right hand side. If that also returns NotImplemented, then a TypeError is 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:

  • Give precedence to one side or the other (usually the one on the left). This prevents a class with a right-side overload from hijacking a class with a left-side overload, but not the other way around. (This works best in dynamic languages, as the methods can decide whether they can handle it, and dynamically defer to the other one.)
  • Make it an error (as @dave is suggesting in his answer). If there is ever more than one viable choice, it is a compiler error. (This works best in static languages, where you can catch this thing in advance.)
  • Only allow the left-most class to define operator overloads, as in C++. (Then your class B would be illegal.)

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.

like image 183
mgiuca Avatar answered Oct 20 '22 17:10

mgiuca