Jump to page: 1 2
Thread overview
Virtual opBinary in interface
3 days ago
sfp
3 days ago
monkyyy
3 days ago
user1234
3 days ago
sfp
3 days ago
mzfhhhh
2 days ago
Ali Çehreli
2 days ago
Ali Çehreli
1 day ago
sfp
1 day ago
Andy Valencia
1 day ago
sfp
3 days ago

Subject lines says it all, I think... The choice to make binary operators implementable only via this opBinary template means it's unclear how to get virtual operators on an interface. E.g., this toy example does compile:

interface Scalar {
  Scalar opBinary(string op)(Scalar rhs); // wrong
}

class Int : Scalar {
  int i;
  this(int i) { this.i = i; }
  Int opBinary(string op)(Int rhs) if (op == "+") {
    return new Int(i + this.i);
  }
}

void main() {
  Scalar one = new Int(1);
  Scalar two = one + one;
}

but with linker errors, of course:

~[...] $ dmd scratch.d
/usr/bin/ld: scratch.o: in function `_Dmain':
scratch.d:(.text._Dmain[_Dmain]+0x2a): undefined reference to `_D7scratch6Scalar__T8opBinaryVAyaa1_2bZQtMFCQBqQBlZQi'
collect2: error: ld returned 1 exit status
Error: undefined reference to `scratch.Scalar scratch.Scalar.opBinary!("+").opBinary(scratch.Scalar)`
       referenced from `_Dmain`
       perhaps `.d` files need to be added on the command line, or use `-i` to compile imports
Error: linker exited with status 1
       cc scratch.o -o scratch -m64 -Xlinker --export-dynamic -L/usr/lib64 -Xlinker -Bstatic -lphobos2 -Xlinker -Bdynamic -lpthread -lm -lrt -ldl

Could someone set me straight here? I just want to be able to define an interface that has some binary ops and then overload them as needed.

I am very new to D, and my goal is to learn how to solve this problem using classic, runtime, dynamic polymorphism in D (so, please don't suggest that I solve a different problem, suggest that I use a particular library, a different technique, etc.). Thanks in advance.

3 days ago

On Thursday, 19 December 2024 at 18:49:28 UTC, sfp wrote:

>
   perhaps `.d` files need to be added on the command line, or use `-i` to compile imports

always try -i in response to any linker error

3 days ago

On Thursday, 19 December 2024 at 18:49:28 UTC, sfp wrote:

>

Subject lines says it all, I think... The choice to make binary operators implementable only via this opBinary template means it's unclear how to get virtual operators on an interface. E.g., this toy example does compile:

interface Scalar {
  Scalar opBinary(string op)(Scalar rhs); // wrong
}

[...]

Function templates declared in interfaces are not virtual, see https://dlang.org/spec/interface.html#method-bodies (§17.1.1.2), so as Scalar.opBinary has no body the linker cannot find the matching function.

3 days ago

On Thursday, 19 December 2024 at 18:49:28 UTC, sfp wrote:

>

Subject lines says it all, I think... The choice to make binary operators implementable only via this opBinary template means it's unclear how to get virtual operators on an interface. E.g., this toy example does compile:

interface Scalar {
  Scalar opBinary(string op)(Scalar rhs); // wrong
}

class Int : Scalar {
  int i;
  this(int i) { this.i = i; }
  Int opBinary(string op)(Int rhs) if (op == "+") {
    return new Int(i + this.i);
  }
}

void main() {
  Scalar one = new Int(1);
  Scalar two = one + one;
}

The template methods in the interface need to be implemented within the interface.

https://dlang.org/spec/interface.html#method-bodies

interface Scalar {
    Scalar opBinary(string op)(Scalar rhs) if (op == "+") {
        return add(rhs);
    }

    Scalar add(Scalar rhs);
}


class Int : Scalar {
    int i;
    this(int i) { this.i = i; }

    Scalar add(Scalar rhs) {
    return new Int((cast(Int)rhs).i + this.i);
    }
}

void main() {
    Scalar one = new Int(1);
    Scalar two = one + one;
}
3 days ago

On Friday, 20 December 2024 at 01:29:32 UTC, user1234 wrote:

>

On Thursday, 19 December 2024 at 18:49:28 UTC, sfp wrote:

>

Subject lines says it all, I think... The choice to make binary operators implementable only via this opBinary template means it's unclear how to get virtual operators on an interface. E.g., this toy example does compile:

interface Scalar {
  Scalar opBinary(string op)(Scalar rhs); // wrong
}

[...]

Function templates declared in interfaces are not virtual, see https://dlang.org/spec/interface.html#method-bodies (§17.1.1.2), so as Scalar.opBinary has no body the linker cannot find the matching function.

Thanks. Yes, I surmised as much. I'm wondering if there is an idiomatic way to accomplish "virtual binary operators in an interface". The opBinary template gets in the way of this in a way that e.g. C++'s operator+ and friends do not.

2 days ago
On 12/19/24 10:49 AM, sfp wrote:
> Subject lines says it all

Although you clearly have a need for, virtual operators haven't been common in my experience. I always felt they could cause semantic issues.

For example, the two subclasses of an interface may not have the binary relation that the interface prescribes. I can think of the Animal hierarchy where Cat and Dog may have a certain relationship that may not make sense between Alligator and Mouse. Forcing such a binary function at the Interface level may not be right.

Having said that, and to contradict myself, this discussion reminded me of a fun DConf presentation by our friend Jean-Louis Leroy that included examples of such virtual functionality where it made sense:

"Open Methods for D (The Expression Problem - solved)"

  https://www.youtube.com/watch?v=MpwHeE2Vvfw&t=395s

But it uses his magical implementation (enabled by D) of open methods (and multi-methods).

Ali

2 days ago
On 12/20/24 10:40 AM, Ali Çehreli wrote:
> I always felt they could cause semantic issues.

I remembered one such case. What should happen if both Cat and Dog defined the "+" operator? Should we expect 'cat + dog' behave the same as 'dog + cat'?

Unfortunately, virtual functions are picked by the object that they are called on. The following example demonstrates this confusion with a function named mingleWith(). Different functions are called depending on the object.

import std.stdio;

interface Animal {
    void mingleWith(Animal);
}

class Dog : Animal {
    void mingleWith(Animal) {
        writeln("Dog with an Animal");
    }
}

class Cat : Animal {
    void mingleWith(Animal) {
        writeln("Cat with an Animal");
    }
}

void use(Animal a, Animal b) {
    a.mingleWith(b);
    b.mingleWith(a);      // <-- DIFFERENT BEHAVIOR
}

void main() {
    auto c = new Cat();
    auto d = new Dog();

    use(c, d);
}

This is too much complication for engineering, program correctness, and life. :)

Ali

1 day ago

On Thursday, 19 December 2024 at 18:49:28 UTC, sfp wrote:

>

Subject lines says it all, I think... The choice to make binary operators implementable only via this opBinary template means it's unclear how to get virtual operators on an interface.

>

I am very new to D, and my goal is to learn how to solve this problem using classic, runtime, dynamic polymorphism in D (so, please don't suggest that I solve a different problem, suggest that I use a particular library, a different technique, etc.). Thanks in advance.

As said virtual functions cannot be templates. However you can use alias to essentially define what you want your operators to be called.

I wrote a blog post on how to use a single mixin to forward all operators to the D1 style overloads. You might find it useful or inspiring.

https://www.schveiguy.com/blog/2022/06/how-to-keep-using-d1-operator-overloads/

-Steve

1 day ago

On Saturday, 21 December 2024 at 07:02:07 UTC, Steven Schveighoffer wrote:

>

I wrote a blog post on how to use a single mixin to forward all operators to the D1 style overloads. You might find it useful or inspiring.

https://www.schveiguy.com/blog/2022/06/how-to-keep-using-d1-operator-overloads/

Thank you for that remarkably informative article! I picked up quite a number of new techniques from it.

Andy

1 day ago
On Friday, 20 December 2024 at 18:40:17 UTC, Ali Çehreli wrote:
> On 12/19/24 10:49 AM, sfp wrote:
> > Subject lines says it all
>
> Although you clearly have a need for, virtual operators haven't been common in my experience. I always felt they could cause semantic issues.
>
> ...
>
> But it uses his magical implementation (enabled by D) of open methods (and multi-methods).
>
> Ali

Right, the semantics can be a little odd, but there are definitely use cases for it which are very natural. Modeling how different kinds of animals mingle, I'm not sure... :-)

"Virtual binary operators" are quite useful in computational science (my field...). For instance, with a numerical linear algebra library, you might have sparse matrices, dense matrices, diagonal matrices, Toeplitz matrices, matrices which are only accessible indirectly via their action (e.g. you multiply with a discrete Fourier transform matrix by applying the FFT and do not store the matrix itself), *block* matrices (comprised of other matrices), etc, etc.

It is pretty common to mix and match these heterogeneously. MATLAB and Python have dense and sparse matrices, potentially other user-defined matrices/operators, and mixing and matching them is straightforward. Duck-typing is especially nice here, because there is no need to return a super type... multiply a dense matrix with a diagonal matrix and return a dense matrix (rather than a matrix supertype), no problem...

I actually saw this multimethod implementation in another forum post when searching around, but not the talk. Thanks for posting it. I will definitely take a look, but unless I'm mistaken, D doesn't have free binary operators?
« First   ‹ Prev
1 2