March 09, 2017
On Thursday, 9 March 2017 at 12:07:54 UTC, Ola Fosheim Grøstad wrote:
> "template<typename T, typename = void_t<>>struct mystuff ..."
> "template<typename T> struct mystuff<T, void_t<A> > ..."
> "template<typename T> struct mystuff<T, void_t<B> > ..."

Gah, that made no sense, meant something like this:

"template<typename T, typename = std::void_t<>>struct mystuff ..."
"template<typename T> struct mystuff<T, std::void_t<typename T::Fish>> > ..."
"template<typename T> struct mystuff<T, std::void_t<typename T::Cat>> > ..."

March 09, 2017
On Tuesday, 7 March 2017 at 15:34:54 UTC, ketmar wrote:
> Jack Stouffer wrote:
>
>> On Tuesday, 7 March 2017 at 15:05:45 UTC, ketmar wrote:
>>> only for primitive types, sadly.
>>>
>>> void main () {
>>> Object a, b;
>>> a == b;
>>> }
>>>
>>> oops. no more error messages. yes, i know that this invokes `opEquals()`, and `opEquals()` can have side-effects. but what are the chances of writing such code *intentionally*?
>>
>> Hmm, I guess the compiler can (and should) output an error message if it knows that opEquals is `pure`.
>>
>> https://issues.dlang.org/show_bug.cgi?id=17245
>
> yet Object's `opEquals()` is not pure (and it cannot be, to allow non-pure overloads). still, the code i've written has no sense.

I'm curious. Where does it make sense for opEquals to be non-pure? Likewise opCmp, etc.

Maybe we want to support weird DSLs, where operators are reused with very different semantics? For example, the pyparsing parser generator allows you to write a grammar like [0] this:

  exp << Group(lparen + op + (number | exp) + (number | exp) + rparen)

[0] http://pyparsing.wikispaces.com/file/view/pycalc.py/480674428/pycalc.py
March 09, 2017
On Thursday, 9 March 2017 at 15:42:22 UTC, qznc wrote:
> I'm curious. Where does it make sense for opEquals to be non-pure? Likewise opCmp, etc.
>

When the object need some kind of normalization to be comparable and you don't want to do the normalization every single time.

March 09, 2017
On Thursday, 9 March 2017 at 15:55:07 UTC, deadalnix wrote:
> On Thursday, 9 March 2017 at 15:42:22 UTC, qznc wrote:
>> I'm curious. Where does it make sense for opEquals to be non-pure? Likewise opCmp, etc.
>>
>
> When the object need some kind of normalization to be comparable and you don't want to do the normalization every single time.

I wonder how stupid it would be to cast the implementation to pure in this case.
March 09, 2017
John Colvin wrote:

> On Thursday, 9 March 2017 at 15:55:07 UTC, deadalnix wrote:
>> On Thursday, 9 March 2017 at 15:42:22 UTC, qznc wrote:
>>> I'm curious. Where does it make sense for opEquals to be non-pure? Likewise opCmp, etc.
>>>
>>
>> When the object need some kind of normalization to be comparable and you don't want to do the normalization every single time.
>
> I wonder how stupid it would be to cast the implementation to pure in this case.

as any other lie. broken promise == UB. while current compiler *may* tolerate it, broken promise is a broken promise.
March 10, 2017
On Thursday, 9 March 2017 at 15:42:22 UTC, qznc wrote:
> I'm curious. Where does it make sense for opEquals to be non-pure? Likewise opCmp, etc.

An example would be tracking the number of comparisons made.  This sounds like debug information (and then, debug statement can be used to escape purity), but perhaps may be applied beyond simple debugging and profiling.

Ivan Kazmenko.

March 09, 2017
On Fri, Mar 10, 2017 at 03:45:37AM +0000, Ivan Kazmenko via Digitalmars-d wrote:
> On Thursday, 9 March 2017 at 15:42:22 UTC, qznc wrote:
> > I'm curious. Where does it make sense for opEquals to be non-pure? Likewise opCmp, etc.
> 
> An example would be tracking the number of comparisons made.  This sounds like debug information (and then, debug statement can be used to escape purity), but perhaps may be applied beyond simple debugging and profiling.
[...]

The main practical use case I can think of is caching, e.g., if computing the (in)equality is expensive and you want to cache the result if the comparison is frequently made.

But opEquals/opCmp being non-pure has been a big hindrance in the uptake of pure (and other attributes), because of the way the Object base class defines these methods, and since it was originally done before some of these attributes existed in the language, some derived classes may already depend on them being impure / throwing / etc., so changing it now would break code.

Besides, if Object declares opEquals as pure, then *all* classes would be required to declare it as pure, which may be an onerous limitation.


T

-- 
This is a tpyo.
March 10, 2017
On Thursday, 9 March 2017 at 15:42:22 UTC, qznc wrote:
> On Tuesday, 7 March 2017 at 15:34:54 UTC, ketmar wrote:
>> Jack Stouffer wrote:
>>
>>> On Tuesday, 7 March 2017 at 15:05:45 UTC, ketmar wrote:
>>>> only for primitive types, sadly.
>>>>
>>>> void main () {
>>>> Object a, b;
>>>> a == b;
>>>> }
>>>>
>>>> oops. no more error messages. yes, i know that this invokes `opEquals()`, and `opEquals()` can have side-effects. but what are the chances of writing such code *intentionally*?
>>>
>>> Hmm, I guess the compiler can (and should) output an error message if it knows that opEquals is `pure`.
>>>
>>> https://issues.dlang.org/show_bug.cgi?id=17245
>>
>> yet Object's `opEquals()` is not pure (and it cannot be, to allow non-pure overloads). still, the code i've written has no sense.
>
> I'm curious. Where does it make sense for opEquals to be non-pure? Likewise opCmp, etc.

I my experience it is a bad idea to require such restrictive contracts at lower inheritance levels when others outside a library are supposed to inherit your class. My case was a calculation object in C++, a calculation is a mathematical formula, so in the base class I made the method const, and the simple implementation inside had no problems with that. But a big project plugged into the rest of calculations in the library, while overriding this calculation with its own FEM engine, which is expensive to run, and should be cached. I had to redesign the library and remove that const. So result caching is indeed an example, like HS Teoh said.

Going back to the original question above, if you really want the compiler to complain about a==b statements for custom types, rather than forcing opEquals to be pure, it would make more sense for the compiler to define which operator overloads are allowed in this way (++, --, etc) and which aren't (==, +, -, etc).

> Maybe we want to support weird DSLs, where operators are reused with very different semantics? For example, the pyparsing parser generator allows you to write a grammar like [0] this:

Maybe... I usually hate that stuff a-la-C++ iostream but, inverting the question, do you want to disallow it?
March 10, 2017
On Fri, Mar 10, 2017 at 07:47:43AM +0000, XavierAP via Digitalmars-d wrote:
> On Thursday, 9 March 2017 at 15:42:22 UTC, qznc wrote:
[...]
> > Maybe we want to support weird DSLs, where operators are reused with very different semantics? For example, the pyparsing parser generator allows you to write a grammar like [0] this:
> 
> Maybe... I usually hate that stuff a-la-C++ iostream but, inverting the question, do you want to disallow it?

AFAIK Walter's stance is that overloading operators with semantics other than generalizations of arithmetic operators are a bad idea.  This is why D uses opCmp for overloading <, <=, ==, >=, >, instead of one overload per operator like C++ has.  Separately overloading < and <=, for example, means that the programmer can do truly weird things with it, which makes code hard to read (when you see "a < b" and "a <= b", you have no idea what they *really* mean).

For DSLs, the official D recommendation is to use string mixins and CTFE instead. This gives you the flexibility to use *any* syntax you want for your DSL, including syntax that doesn't even fit in D syntax. You could even implement Unicode operators.  CTFE lets you parse such strings at compile-time and emit code for them via string mixins, so runtime performance is not a concern.

And I would tend to agree: I find iostream's (ab)use of << and >> to mean anything other than bitshifting very ugly and hard to read.  Look at the Boost.Xpressive library for an even more extreme example of this. (Though thankfully, they appear to be moving towards string DSLs by taking advantage of the latest C++ features, so there is hope that this will soon be just an ugly footnote in history.)

A long-standing item on my todo list is to implement compile-time writefln format strings using this technique.  I don't even want to imagine how ugly code will become if I were to do compile-time format strings the C++ way by overloading arithmetic operators... imagine format strings written using operators... Ugh!


T

-- 
He who sacrifices functionality for ease of use, loses both and deserves neither. -- Slashdotter
March 10, 2017
On Friday, 10 March 2017 at 19:02:06 UTC, H. S. Teoh wrote:
> On Fri, Mar 10, 2017 at 07:47:43AM +0000, XavierAP via Digitalmars-d wrote:
>> On Thursday, 9 March 2017 at 15:42:22 UTC, qznc wrote:
> [...]
>> > Maybe we want to support weird DSLs, where operators are reused with very different semantics? For example, the pyparsing parser generator allows you to write a grammar like [0] this:
>> 
>> Maybe... I usually hate that stuff a-la-C++ iostream but, inverting the question, do you want to disallow it?
>
> AFAIK Walter's stance is that overloading operators with semantics other than generalizations of arithmetic operators are a bad idea.  This is why D uses opCmp for overloading <, <=, ==, >=, >, instead of one overload per operator like C++ has.  Separately overloading < and <=, for example, means that the programmer can do truly weird things with it, which makes code hard to read (when you see "a < b" and "a <= b", you have no idea what they *really* mean).

* D does not use opCmp for == but I'm sure it slipped your tongue ;)

I agree with that stance on style myself, and also with the hard restriction to keep the (in)equality and comparison operators consistent with each other. But the question is whether to restrict programmers further with no other reason than style. D's philosophy is maximum @system freedom (just like C/C++) within sanity.

Also operator overloading style may depend on each engineer's background. Walter's education is mechanical engineering and mathematics (coincidentally just like myself), so he dislikes usage of operators not analogous to algebra or set theory. Me too; beyond that I would rather create methods with self-documenting names.

But there are software engineers and computer scientists who don't care about math, and they may even love iostream's "arrows" indicating the direction of the "stream". Leaving aside the specific example of iostream, this style discussion is not one where anyone can prove or claim to be right. Live and let live.