After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 518604 - Implementing operators
Implementing operators
Status: RESOLVED OBSOLETE
Product: vala
Classification: Core
Component: Objects
unspecified
Other All
: Low enhancement
: 2.0
Assigned To: Vala maintainers
Vala maintainers
: 726205 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2008-02-25 12:44 UTC by Johannes Schmid
Modified: 2018-05-22 13:04 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Patch enabling operator overloading for arithmetic operators (7.31 KB, patch)
2014-03-13 07:30 UTC, Richard Wiedenhöft
none Details | Review
Testcase (1.29 KB, text/x-vala)
2014-03-13 07:31 UTC, Richard Wiedenhöft
  Details
Patch enabling operator overloading (10.73 KB, patch)
2014-03-13 10:21 UTC, Richard Wiedenhöft
none Details | Review
Testcase (2.12 KB, text/x-vala)
2014-03-13 10:22 UTC, Richard Wiedenhöft
  Details
Patch enabling operator overloading (17.05 KB, patch)
2014-03-17 08:36 UTC, Richard Wiedenhöft
none Details | Review
Patch enabling operator overloading (18.83 KB, patch)
2014-03-17 18:18 UTC, Richard Wiedenhöft
none Details | Review
Patch (16.63 KB, patch)
2014-03-25 14:06 UTC, Richard Wiedenhöft
none Details | Review
Patch (16.55 KB, patch)
2014-03-29 15:39 UTC, Richard Wiedenhöft
none Details | Review
Testcase for Gee.Comparable (514 bytes, text/x-vala)
2014-03-29 15:44 UTC, Richard Wiedenhöft
  Details

Description Johannes Schmid 2008-02-25 12:44:21 UTC
(This is more or less what Jürg, Raffaele and me discussed while taking a bus from FOSDEM to the hotel)

Sometimes it is really convenient to overwrite mathematical operators likes it is done in C++. Also the operators "=" and "==" can save quite a couple of code and make it more readable.

The idea is to create special interfaces that automaticly map certain method names on operators:

public interface Vala.Compare
{
  public abstract compare(Vala.Compare other);
}

which would then map to the operator "==" and of course both objects have to be of the same type to be compared.

Of course this is for Vala 2.x!
Comment 1 Raffaele Sandrini 2008-02-25 13:40:54 UTC
Confirming.

We should discuss how to do such a thing in detail. Jürg and I discussed the concept of static interfaces, which could help achieve this goal.
Comment 2 Jürg Billeter 2009-01-16 20:20:41 UTC
I don't think we really need to use interfaces for this. I've implemented support for `in' operators in custom types by just checking for a `contains' member method with an appropriate signature. Something similar is used to support `foreach' when the type provides an `iterator' method.

I think we should proceed in that direction by supporting at least `compare', `equal', and `hash' (not as operator but as implicit argument for hash table creation). However, I'm considering to support this only for structs to avoid a conflict between `equal' and reference-type equality checking. Operator support makes a lot more sense for value types than for reference types, anyway, and we want to limit possible abuse of custom operator implementations - and hence not providing full operator overloading.
Comment 3 Jürg Billeter 2009-01-17 12:36:45 UTC
2009-01-16  Jürg Billeter  <j@bitron.ch>

	* vala/valaassignment.vala:
	* vala/valaelementaccess.vala:
	* vala/valasemanticanalyzer.vala:
	* gobject/valaccodearraymodule.vala:
	* gobject/valaccodeassignmentmodule.vala:
	* gobject/valaccodebasemodule.vala:

	Do not require libgee to support element access in custom types
Comment 4 James "Doc" Livingston 2009-06-26 00:46:15 UTC
I think that using Attributes to specify which operator a method maps to might be a good way of doing this, for example:

class Complex {
  [Operator name="+"]
  Complex plus(Complex c)

  [Operator name="=="]
  bool equals(Complex c)

  ...
}

class MyStringClass {
  [Operator name="+"]
  MyStringClass concatenate(MyStringClass c)

  [Operator name="+"]
  MyStringClass concatenate_cstring(string c)

  ...
}


Doing that would make Vala operators purely syntactic sugar over the underlying C methods, and allow the methods to have different names where it makes sense (like "plus" and "concatenate" above).

It would also allow you to nicely wrap an external library which contains collection-like classes whose methods aren't named exactly the same as libgee's (which Vala treats specially for the "in" operator and foreach).
Comment 5 Lucian 2010-07-27 12:18:12 UTC
I'd just like to point out that the lack of operator overloading is a major issue in Java. BigDecimal is night-unusable because of this.

On the other hand, the solution in C++ or C# makes parsing much more difficult than it should be, so it's not ideal either.

Python has "protocols" instead of interfaces, so classes can implement __add__ if they want +, but they don't need to implement anything else. The closest thing to this in Vala would be James's example with Attributes.
Comment 6 Richard Wiedenhöft 2014-03-13 06:44:48 UTC
*** Bug 726205 has been marked as a duplicate of this bug. ***
Comment 7 Richard Wiedenhöft 2014-03-13 07:30:45 UTC
Created attachment 271687 [details] [review]
Patch enabling operator overloading for arithmetic operators

I implemented operator overloading in vala for the 4 arithmetic operations +, -, *, /. To declare, that a method overloads an operator is use an attribute like '[Semantics(overloads = "+")]' for example.

This works inside of classes and in interfaces (although a cast is required... see testcase).

To implement further operators slight changes to the check routines are required so that comparing operators may return bool.
Comment 8 Richard Wiedenhöft 2014-03-13 07:31:34 UTC
Created attachment 271688 [details]
Testcase
Comment 9 Richard Wiedenhöft 2014-03-13 10:21:56 UTC
Created attachment 271695 [details] [review]
Patch enabling operator overloading

Updated the patch: Supported operators are now +, -, *, /, ==, !=, <, >, <=, >=.
Comment 10 Richard Wiedenhöft 2014-03-13 10:22:35 UTC
Created attachment 271696 [details]
Testcase
Comment 11 Simon Werbeck 2014-03-13 11:45:51 UTC
The way I see it operator overloading makes only sense for some cases:
 - operations where only arithmetic types are involved (+, -, *, /). This should only apply to value types.
 - comparisons (<, >, !=, ..)
 - string/list concatenation using +.

My solution for the first and second case is to use python-like protocols. The first case would look something like this:

[ArithmeticType (other="real")]
struct Complex {
  Complex add (Complex c)
  Complex sub (Complex c)
  Complex mul (Complex c)
  Complex div (Complex c)

  Complex add_real (float real)
  Complex sub_real (float real)
  Complex rsub_real (float real)  // used when Complex is the right operand

  Complex neg ()  // unary minus
}

Then the expression "c + 1" would first try to match against 'add' and after that fails against 'add_real'.

In addition we can further restrict the protocols by allowing only arithmetic parameter types (IntegerType, FloatingType, ArithmeticType).
This solution would have some advantages over general operator overloading:
 - It makes it clear what the types intended use is
 - it's more similar to vala's other constructs (get, set, contains) 
 - it prevents 'creative' use of operators (think c++ streams)

Of course the are also some disadvantages:
 - If a struct is bound as compact class, it can't be used as arithmetic type. 
 - it enforces method names and thus complicates bindings. I think this is not a real problem. If overloading is desired [CCode (cname=...)] can be used to adjust the names. After that one would prefer the operators anyway.
Comment 12 Richard Wiedenhöft 2014-03-17 08:36:08 UTC
Created attachment 272122 [details] [review]
Patch enabling operator overloading

Added test and fixed some inheritance issues.
Comment 13 Richard Wiedenhöft 2014-03-17 18:18:46 UTC
Created attachment 272189 [details] [review]
Patch enabling operator overloading

In this updated version of the patch operator-overloading for Classes, Structs and Interfaces is supported. Two tests are included (one for structs and one for interfaces and classes).
Comment 14 Luca Bruno 2014-03-24 20:12:36 UTC
Thanks for your work. If I were to merge this request, I'd first point out a couple of things:
1) [Semantics (overloads)] seems a little too complex. I'd prefer a simpler [Operator (type = "+")] or such.
2) For all the comparison operators, I'd rather add a [Compare] on top of a cmp method which returns an int, much like strcmp. I bet objects that are comparable have a compare method. Then vala can simply call compare() < 0, > 0, == 0, etc.
Comment 15 Maciej (Matthew) Piechotka 2014-03-24 20:49:47 UTC
Sorry for late jumping in - would it be possible to make it work with Gee.Comparable somehow (http://www.valadoc.org/#!api=gee-0.8/Gee.Comparable)? Hashable (http://www.valadoc.org/#!api=gee-0.8/Gee.Hashable) also has a equal_to operator. It'd be better if we had a canonical way of comparation instead of separate for library(ies) and language.

> 2) For all the comparison operators, I'd rather add a [Compare] on top of a cmp
> method which returns an int, much like strcmp. I bet objects that are
> comparable have a compare method. Then vala can simply call compare() < 0, > 0,
> == 0, etc.

There are types where you can reasonably implement == method but there is no prescribed comparation. To name one - by POSIX standart pthread_t can be compared by pthread_equal but there is no possibility of 'sorting' them. While ==, < and > usually work as pthread_t is usually typedef of pointer there are platforms in the wild where it is not the case.

However I agree that there should be no need to have both != and ==, < and >= or > and <=.
Comment 16 Richard Wiedenhöft 2014-03-25 07:55:14 UTC
(In reply to comment #15)
> Sorry for late jumping in - would it be possible to make it work with
> Gee.Comparable somehow (http://www.valadoc.org/#!api=gee-0.8/Gee.Comparable)?
> Hashable (http://www.valadoc.org/#!api=gee-0.8/Gee.Hashable) also has a
> equal_to operator. It'd be better if we had a canonical way of comparation
> instead of separate for library(ies) and language.

This can be achieved by simply adding the corresponding attributes to the Comparable-Interface (in the case that this patch is merged obviously).

(In reply to comment #14)
> Thanks for your work. If I were to merge this request, I'd first point out a
> couple of things:
> 1) [Semantics (overloads)] seems a little too complex. I'd prefer a simpler
> [Operator (type = "+")] or such.
> 2) For all the comparison operators, I'd rather add a [Compare] on top of a cmp
> method which returns an int, much like strcmp. I bet objects that are
> comparable have a compare method. Then vala can simply call compare() < 0, > 0,
> == 0, etc.

Both good ideas. On it.

(In reply to comment #15)
> There are types where you can reasonably implement == method but there is no
> prescribed comparation. To name one - by POSIX standart pthread_t can be
> compared by pthread_equal but there is no possibility of 'sorting' them. While
> ==, < and > usually work as pthread_t is usually typedef of pointer there are
> platforms in the wild where it is not the case.

Maybe it makes sense to add both "[Compare]" which overloads all comparison-operators and "[Equals]" which overloads == and !=.

Apart from that. After merging this it might be a good idea to implement "===" and "!==" operators which check reference (in)equality.
Comment 17 Richard Wiedenhöft 2014-03-25 14:06:19 UTC
Created attachment 272853 [details] [review]
Patch

The patch is now more concise i think. Arithmetic overloading can now be done with [Operator (type = "+")] for example. I also added [Equals] and [Compare] attributes. A method marked with [Compare] should behave like strcmp and overloads all comparison operators. A method marked with [Equals] should return a bool and overloads only "==" and "!=".
Comment 18 Maciej (Matthew) Piechotka 2014-03-27 08:33:32 UTC
A few notes:
a) left.value_type.equals (right.value_type): this has an unobvious side-effect that if A has + overloaded and B derives from A and a and b have type B then a + b works, (A)a + (A)b works but (A)a + b and a + (A)b doesn't. It should be sufficient to check if both are compatible with arguments,

b) Possibly it would even make sense to have multiple operators:

Mat3x1 a = ...
Mat1x3 b = ...
a = a * 3.0;
Mat3x3 c = a * b;
double d = b * a;

This can be left for future (or just scrapped).

c) Are you sure that it'll be allowed to use Gee.Comparable? As far as I can tell Comparable<G> and G are not the same type.
Comment 19 Richard Wiedenhöft 2014-03-27 12:03:28 UTC
(In reply to comment #18)
> A few notes:
> a) left.value_type.equals (right.value_type): this has an unobvious side-effect
> that if A has + overloaded and B derives from A and a and b have type B then a
> + b works, (A)a + (A)b works but (A)a + b and a + (A)b doesn't. It should be
> sufficient to check if both are compatible with arguments,

I intentionally left this out, because something that works not at all is better than something that works incorrectly. The problem i am seeing is that when A overloads + and B overloads + in a different way (which is allowed) then a + b  would have a different result than b + a. This might be solvable, but it adds a truckload of extra complexity.

> b) Possibly it would even make sense to have multiple operators:
> 
> Mat3x1 a = ...
> Mat1x3 b = ...
> a = a * 3.0;
> Mat3x3 c = a * b;
> double d = b * a;
> 
> This can be left for future (or just scrapped).

This would certainly be nice.

> c) Are you sure that it'll be allowed to use Gee.Comparable? As far as I can
> tell Comparable<G> and G are not the same type.

You are right. It would not work. But when i see it correctly Gee.Comparable makes it possible to compare two objects of different types. This is not desirable for the equality-operator anyway.
Comment 20 Maciej (Matthew) Piechotka 2014-03-27 19:45:38 UTC
(In reply to comment #19)
> (In reply to comment #18)
> > A few notes:
> > a) left.value_type.equals (right.value_type): this has an unobvious side-effect
> > that if A has + overloaded and B derives from A and a and b have type B then a
> > + b works, (A)a + (A)b works but (A)a + b and a + (A)b doesn't. It should be
> > sufficient to check if both are compatible with arguments,
> (...)
> > c) Are you sure that it'll be allowed to use Gee.Comparable? As far as I can
> > tell Comparable<G> and G are not the same type.
> 
> You are right. It would not work. But when i see it correctly Gee.Comparable
> makes it possible to compare two objects of different types. This is not
> desirable for the equality-operator anyway.

The problem is that it is the only way you can do it for generics in Vala in a 'sane' way. In that way you can write:

class Test : Object, Comparable<Test> {
    public int compare_to (Test test) {
        // ...
    }
}

If argument was Comparable<Test> it would be:

class Test : Object, Comparable<Test> {
    public int compare_to (Comparable<Test> _test) {
        Test? test = _test as Test;
        if (test != null) {
            // ...
        } else {
            // ???
        }
    }
}

The proposed method couldn't be used by libgee as code is generic. So it comes back to original problem - you have two methods of writing equality - for language and library - which cannot be bridge even if libgee was to break API/ABI (which we were explicitly saying that we are not planning to do).
Comment 21 Richard Wiedenhöft 2014-03-29 15:39:05 UTC
Created attachment 273235 [details] [review]
Patch

I disabled the check and made this feature compatible to Gee.Comparable. The only thing left to do to make this work is adding [Compare] to Gee.Comparable.compare_to().

This copies some of the drawbacks of the interface approach. It is now possible to do this:

public class Foo {
   [Compare]
   public int compare (Bar bar) {
      return 0;
   }
}

public class Bar { }

However... trying to use it will result in a compiler error, as TypeSymbol.get_binary_over() still checks if the two types are equal.
Comment 22 Richard Wiedenhöft 2014-03-29 15:44:20 UTC
Created attachment 273236 [details]
Testcase for Gee.Comparable

Attached is a testcase that shows that when [Compare] is added to libgee the patch will be compatible to Gee.Comparable
Comment 23 GNOME Infrastructure Team 2018-05-22 13:04:01 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to GNOME's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/vala/issues/4.