Thread overview
Operator overloading question
Jan 21, 2013
Joseph Cassman
Jan 21, 2013
Nathan M. Swan
Jan 21, 2013
Joseph Cassman
Jan 21, 2013
bearophile
Jan 21, 2013
mist
Jan 21, 2013
Ali Çehreli
Jan 21, 2013
mist
Jan 21, 2013
Ali Çehreli
Jan 21, 2013
mist
Jan 22, 2013
Timon Gehr
January 21, 2013
Please refer to http://dpaste.dzfl.pl/edit/b73ef2cd

The code is contrived but is trying to focus on overloading the "+" and "*" operators for a struct.

Here is the output.

/home/c215/c527.d(36): Error: incompatible types for ((x) + (y)): 'Arithmetic!(int)' and 'Arithmetic!(int)'
/home/c215/c527.d(37): Error: 'x' is not of arithmetic type, it is a Arithmetic!(int)
/home/c215/c527.d(37): Error: 'y' is not of arithmetic type, it is a Arithmetic!(int)

I am trying to understand why there are errors at all.
Also why the error for line 36 is different from the error for line 37.

Thanks

Joseph
January 21, 2013
On Monday, 21 January 2013 at 06:19:47 UTC, Joseph Cassman wrote:
> Please refer to http://dpaste.dzfl.pl/edit/b73ef2cd
>
> The code is contrived but is trying to focus on overloading the "+" and "*" operators for a struct.
>
> Here is the output.
>
> /home/c215/c527.d(36): Error: incompatible types for ((x) + (y)): 'Arithmetic!(int)' and 'Arithmetic!(int)'
> /home/c215/c527.d(37): Error: 'x' is not of arithmetic type, it is a Arithmetic!(int)
> /home/c215/c527.d(37): Error: 'y' is not of arithmetic type, it is a Arithmetic!(int)
>
> I am trying to understand why there are errors at all.
> Also why the error for line 36 is different from the error for line 37.
>
> Thanks
>
> Joseph

The correct keyword is "opBinary", not "opbinary". Also, that's a link to editing the paste, viewing it is:

http://dpaste.1azy.net/b73ef2cd
January 21, 2013
On Monday, 21 January 2013 at 06:51:23 UTC, Nathan M. Swan wrote:
> On Monday, 21 January 2013 at 06:19:47 UTC, Joseph Cassman wrote:
>> Please refer to http://dpaste.dzfl.pl/edit/b73ef2cd
>>
>> The code is contrived but is trying to focus on overloading the "+" and "*" operators for a struct.
>>
>> Here is the output.
>>
>> /home/c215/c527.d(36): Error: incompatible types for ((x) + (y)): 'Arithmetic!(int)' and 'Arithmetic!(int)'
>> /home/c215/c527.d(37): Error: 'x' is not of arithmetic type, it is a Arithmetic!(int)
>> /home/c215/c527.d(37): Error: 'y' is not of arithmetic type, it is a Arithmetic!(int)
>>
>> I am trying to understand why there are errors at all.
>> Also why the error for line 36 is different from the error for line 37.
>>
>> Thanks
>>
>> Joseph
>
> The correct keyword is "opBinary", not "opbinary". Also, that's a link to editing the paste, viewing it is:
>
> http://dpaste.1azy.net/b73ef2cd

Nice when it's an easy fix.
Appreciate the pointer on dpaste too.

Joseph
January 21, 2013
Nathan M. Swan:

> The correct keyword is "opBinary", not "opbinary".

The compiler must give an error message easy to understand in similar wrong cases.


> http://dpaste.1azy.net/b73ef2cd

This is your code:

	Arithmetic opbinary(string op)(Arithmetic rhs)
	{
		static if(op == "+") return add(rhs);
		static if(op == "*") return mul(rhs);
		else static assert(0, "Operator "~op~" not implemented");
	}


I like to add template constraints, where possible:

Arithmetic opbinary(string op)(Arithmetic rhs)
const pure nothrow if (op == "+" || op == "*")

Bye,
bearophile
January 21, 2013
Do you want to discuss it? :) I have noticed that phobos style guidelines favor constraints heavily over static asserts but this exactly the example when I am uneasy about such choice: static assert can both provide more user-friendly error message here and remove some code duplication.

On Monday, 21 January 2013 at 12:27:35 UTC, bearophile wrote:
> Nathan M. Swan:
>
>> The correct keyword is "opBinary", not "opbinary".
>
> The compiler must give an error message easy to understand in similar wrong cases.
>
>
>> http://dpaste.1azy.net/b73ef2cd
>
> This is your code:
>
> 	Arithmetic opbinary(string op)(Arithmetic rhs)
> 	{
> 		static if(op == "+") return add(rhs);
> 		static if(op == "*") return mul(rhs);
> 		else static assert(0, "Operator "~op~" not implemented");
> 	}
>
>
> I like to add template constraints, where possible:
>
> Arithmetic opbinary(string op)(Arithmetic rhs)
> const pure nothrow if (op == "+" || op == "*")
>
> Bye,
> bearophile

January 21, 2013
On 01/21/2013 08:32 AM, mist wrote:

> phobos style
> guidelines favor constraints heavily over static asserts but this
> exactly the example when I am uneasy about such choice: static assert
> can both provide more user-friendly error message here and remove some
> code duplication.

Agreed but there is a problem with 'static assert' in the implementation: We don't know what user code has caused the issue. (It is a common problem in C++ templated libraries that a hard-to-understand compilation error is produced from inside a library function template.)

D's template constraints move the compilation error to the exact place of the user code.

void foo(string s)()
    if ((s == "hello") || (s == "goodbye"))
{
    // ...
}

void main()
{
    foo!"howdy"();  // <-- compilation error on this line
}

It is better:

Error: template instance foo!("howdy") foo!("howdy") does not match template declaration foo(string s)() if (s == "hello" || s == "goodbye")

To be honest, it is kind of obvious in this simple case but sometimes the cause of the error is still hard to understand even with template consraints.

Ali

January 21, 2013
Hm, but why can't static assert provide an instantiation trace? I can live without error message but duplicating same condition twice (one time being part of implementation) hurts my eyes :(

On Monday, 21 January 2013 at 17:16:22 UTC, Ali Çehreli wrote:
> On 01/21/2013 08:32 AM, mist wrote:
>
> > phobos style
> > guidelines favor constraints heavily over static asserts but
> this
> > exactly the example when I am uneasy about such choice:
> static assert
> > can both provide more user-friendly error message here and
> remove some
> > code duplication.
>
> Agreed but there is a problem with 'static assert' in the implementation: We don't know what user code has caused the issue. (It is a common problem in C++ templated libraries that a hard-to-understand compilation error is produced from inside a library function template.)
>
> D's template constraints move the compilation error to the exact place of the user code.
>
> void foo(string s)()
>     if ((s == "hello") || (s == "goodbye"))
> {
>     // ...
> }
>
> void main()
> {
>     foo!"howdy"();  // <-- compilation error on this line
> }
>
> It is better:
>
> Error: template instance foo!("howdy") foo!("howdy") does not match template declaration foo(string s)() if (s == "hello" || s == "goodbye")
>
> To be honest, it is kind of obvious in this simple case but sometimes the cause of the error is still hard to understand even with template consraints.
>
> Ali

January 21, 2013
On 01/21/2013 10:02 AM, mist wrote:
> Hm, but why can't static assert provide an instantiation trace? I can
> live without error message

I forgot to mention that template constraints take part in choosing the implementation as well:

void foo(string s)()
    if ((s == "hello") || (s == "goodbye"))
{
    // ...
}

void foo(string s)()
    if (s == "howdy")
{
    // ...
}

void main()
{
    foo!"howdy"();    // not an error: goes to special implementation
    foo!"merhaba"();  // error: no implementation at all
}

In order to achieve the same goal without template constraints we have two options:

1) Template specialization, which I don't think is not valid for non-type template parameters like string:

void foo(string s == "hello")() // ERROR; I don't think possible
{
    // ...
}

2) 'static if' in the implementation:

void foo(string s)()
{
    static if ((s == "hello") || (s == "goodbye")) {
        // ...

    } else static if (s == "howdy") {
        // ...

    } else {
        static assert(false, "Invalid string " ~ s);
    }
}

void main()
{
    foo!"howdy"();
    foo!"merhaba"();
}

You are right though, dmd does produce useful information for this simple case:

Error: static assert  "Invalid string merhaba"
       instantiated from here: foo!("merhaba")

The only shortcoming that I see in this case is that the implementations for different string values are inside the same function template.

> but duplicating same condition twice (one
> time being part of implementation) hurts my eyes :(

Can you elaborate. I don't see the duplication.

Ali

January 21, 2013
On Monday, 21 January 2013 at 18:53:08 UTC, Ali Çehreli wrote:
> ...
> Can you elaborate. I don't see the duplication.
>
> Ali

First we check that parameter is one of allowed ones in constraint. Then we do the very same check using the very same operator list to actually make a right dispatch. Looks wrong to me, we shouldn't need to.

I need to clarify though: I am perfectly aware that constraints are superior solution in many cases, especially when it comes to multiple overloads for library snippets. But exactly in given example static assert feels as better suited.
January 22, 2013
On 01/21/2013 07:53 PM, Ali Çehreli wrote:
> ...
>
> void foo(string s == "hello")() // ERROR; I don't think possible
> {
>      // ...
> }

void foo(string s : "hello")(){
    // ...
}