Jump to page: 1 210  
Page
Thread overview
Overloading relational operators separately; thoughts?
Sep 28, 2016
Minty Fresh
Sep 28, 2016
Jonathan M Davis
Sep 28, 2016
Minty Fresh
Sep 28, 2016
Jonathan M Davis
Sep 28, 2016
Chris Wright
Sep 28, 2016
Guillaume Boucher
Sep 28, 2016
pineapple
Sep 28, 2016
pineapple
Sep 28, 2016
Chris Wright
Sep 28, 2016
Walter Bright
Sep 28, 2016
Matthias Bentrup
Sep 28, 2016
Timon Gehr
Sep 28, 2016
Walter Bright
Sep 28, 2016
Timon Gehr
Sep 28, 2016
John Colvin
Sep 28, 2016
Walter Bright
Sep 28, 2016
pineapple
Sep 29, 2016
Russel Winder
Sep 29, 2016
Minty Fresh
Sep 29, 2016
Russel Winder
Sep 29, 2016
pineapple
Sep 28, 2016
Minty Fresh
Sep 28, 2016
Timon Gehr
Sep 28, 2016
Minty Fresh
Sep 29, 2016
Walter Bright
Sep 29, 2016
Minty Fresh
Sep 29, 2016
Mike Parker
Sep 29, 2016
Jonathan M Davis
Sep 29, 2016
Timon Gehr
Sep 30, 2016
Chris Wright
Sep 30, 2016
Timon Gehr
Sep 30, 2016
Timon Gehr
Sep 30, 2016
Timon Gehr
Sep 29, 2016
Russel Winder
Oct 01, 2016
Martin Nowak
Oct 01, 2016
pineapple
Sep 28, 2016
Minty Fresh
Sep 29, 2016
Jacob Carlborg
Sep 29, 2016
Walter Bright
Sep 29, 2016
pineapple
Sep 29, 2016
Jacob Carlborg
Sep 29, 2016
Jacob Carlborg
Sep 29, 2016
Minty Fresh
Sep 29, 2016
Sai
Sep 29, 2016
Walter Bright
Sep 29, 2016
Russel Winder
Sep 29, 2016
Minty Fresh
Sep 29, 2016
Jonathan M Davis
Sep 29, 2016
Matthias Bentrup
Sep 29, 2016
pineapple
Sep 29, 2016
Minty Fresh
Sep 29, 2016
Jonathan M Davis
Sep 29, 2016
Minty Fresh
Sep 29, 2016
pineapple
Sep 29, 2016
Jonathan M Davis
Sep 29, 2016
bachmeier
Sep 29, 2016
Minty Fresh
Sep 29, 2016
bachmeier
Sep 30, 2016
Jonathan M Davis
Sep 30, 2016
Chris Wright
Sep 30, 2016
bachmeier
Sep 30, 2016
Minty Fresh
Sep 30, 2016
Walter Bright
Oct 01, 2016
pineapple
Oct 01, 2016
Russel Winder
Oct 01, 2016
Minty Fresh
Sep 30, 2016
pineapple
Sep 30, 2016
Jonathan M Davis
Sep 30, 2016
Timon Gehr
Sep 30, 2016
Timon Gehr
Oct 01, 2016
Russel Winder
Sep 30, 2016
Minty Fresh
Sep 30, 2016
Jonathan M Davis
Sep 30, 2016
pineapple
Oct 01, 2016
Chris Wright
Sep 30, 2016
Sai
Oct 01, 2016
Timon Gehr
Oct 01, 2016
Russel Winder
Oct 01, 2016
Chris Wright
Oct 01, 2016
Russel Winder
Oct 02, 2016
Minty Fresh
Oct 01, 2016
Minty Fresh
Sep 29, 2016
Timon Gehr
Sep 28, 2016
Jacob Carlborg
Sep 29, 2016
rikki cattermole
Sep 29, 2016
Jacob Carlborg
Sep 29, 2016
rikki cattermole
Oct 02, 2016
Atila Neves
September 28, 2016
Currently, D offers fine grained overloading for most of the unary and arithmetic binary operators, but at some point a decision was made to lump together all of the relational operators (and if necessary, the equality operators) into a single function `opCmp`.
And while it does add a lot of convenience and consistency to how operators behave, it does also put a lot of limitations on what they can do.

For example, it's not currently possible to implement comparison operators that would behave lazily and only compute a certain value once a function call forces the computation.

Similarly, it's not possible to construct a DSL that would produce code or a query in another language, by returning values behave like AST nodes rather than actually performing a comparison.

ie.
  table.users
       .where!((users) => users.joined_on >= 3.days.ago)
       .joins(table.posts)
       .on!((posts, users) => posts.user_id == users.id)
       .limit(10)
       .toSql;

It's a little odd to me that D puts so little focus on DSLs, since language features like template constraints, `opDispatch`, and even the fact that binary operators are overloadable via a single templated function are all very useful tools for implementing them.

So, I'm just interested in other people's thoughts are on the subject, and whether there are any plans to allow overloading these operators separately.
September 27, 2016
On Wednesday, September 28, 2016 01:18:58 Minty Fresh via Digitalmars-d wrote:
> So, I'm just interested in other people's thoughts are on the subject, and whether there are any plans to allow overloading these operators separately.

Basically, having them be overloaded separately is error-prone, and it leads to folks overloading them in ways that have nothing to do with what they mean for the built-in types, which makes code harder to read and maintain. D has a concatenation operator specifically because Walter thought that it was horrible to use + for concatenation.

If you want to write DSLs, then use strings and mixins. D gives you a _lot_ of power in that area, and it avoids the problems associated with making overloaded operators do things other than what those operators are normally intended to do. With strings, you can make your DSL look however you want - even doing stuff like using various Unicode symbols as operators if you want to. Even with how C++ overloads operators, overloading operators gives you fairly limited options with what you can do with a DSL, whereas you have full freedom with strings - and without the problems that come with trying to make your DSL look like normal D code.

- Jonathan M Davis

September 28, 2016
On Wednesday, 28 September 2016 at 03:12:05 UTC, Jonathan M Davis wrote:
> On Wednesday, September 28, 2016 01:18:58 Minty Fresh via Digitalmars-d wrote:
> Basically, having them be overloaded separately is error-prone, and it leads to folks overloading them in ways that have nothing to do with what they mean for the built-in types, which makes code harder to read and maintain. D has a concatenation operator specifically because Walter thought that it was horrible to use + for concatenation.
>
> If you want to write DSLs, then use strings and mixins. D gives you a _lot_ of power in that area, and it avoids the problems associated with making overloaded operators do things other than what those operators are normally intended to do. With strings, you can make your DSL look however you want - even doing stuff like using various Unicode symbols as operators if you want to. Even with how C++ overloads operators, overloading operators gives you fairly limited options with what you can do with a DSL, whereas you have full freedom with strings - and without the problems that come with trying to make your DSL look like normal D code.
>
> - Jonathan M Davis

Using strings and mixins does have quite a number of downsides. The additional work required to past the DSL aside, you also usually lose the scope in which things are declared (especially if you wish to reference or declare variables/functions/other constructs within your DSL), error reporting becomes much less precise, and the transition between the outer code and the inner DSL becomes jarring (especially in terms of syntax highlighting).

That said, I love the fact D has a separate operator for concatenation. It's extremely useful to not have that ambiguity.

Another thought is, operators not behaving as expected doesn't strike me as a failure of the language, given the language supports operator overloading. It's a failure in documentation to explain what the operators are intended to do, or a failure in implementation to behave as they should.

Lastly, if operators are intended to behave so strictly, why does this not extend then to binary arithmetic operators (+, -, *, /, %, etc.)
They don't follow the same rules as the binary relational operators, after all.
September 27, 2016
On 9/27/2016 6:18 PM, Minty Fresh wrote:
> So, I'm just interested in other people's thoughts are on the subject, and
> whether there are any plans to allow overloading these operators separately.

The limitations are deliberate based on the idea that comparison operators need to be consistent and predictable, and should have a close relationship to the mathematical meaning of the operators. Overloading <= to mean something other than "less than or equal" is considered poor style in D, and the same goes for the other arithmetic operators.

The use of them to create DSLs (a technique called "expression templates" in C++) is discouraged in D, for several reasons. The recommended way to create DSLs in D is to parse strings using CTFE. An excellent example of that is the std.regex package.

There are no plans to change this.
September 27, 2016
On Wednesday, September 28, 2016 03:28:50 Minty Fresh via Digitalmars-d wrote:
> Lastly, if operators are intended to behave so strictly, why does
> this not extend then to binary arithmetic operators (+, -, *, /,
> %, etc.)
> They don't follow the same rules as the binary relational
> operators, after all.

It's not possible in the general case to define the arithmetic operators as functions of each other. They actually need to be defined separately in order to work. The comparison operators don't have that problem. And in the cases where the arithmetic operations are essentially being passed on to a member variable such that it's just the operator itself that's changing, and the code is otherwise the same, the fact that opBinary takes a string for the operator makes it trivial to use string mixins so that you only need one function body for all of the operators, whereas in the cases where you can't do that (e.g. the operation involves multiple member variables), you can declare separate functions.

The increment and decrement operators are in a similar boat to the comparison operators in that post and pre can be derived from a single function. So, it's not just the comparison operators that got combined. In the cases where it made sense to combine operators, they were combined, and in the cases where it didn't make sense, they weren't. It would be nice if more could be combined, but most of them actually need to be separate to work. Fortunately though, some of them can be combined, and those were combined, which makes operator overloading in D easier and less error-prone.

- Jonathan M Davis

September 28, 2016
On 2016-09-28 03:18, Minty Fresh wrote:
> Currently, D offers fine grained overloading for most of the unary and
> arithmetic binary operators, but at some point a decision was made to
> lump together all of the relational operators (and if necessary, the
> equality operators) into a single function `opCmp`.
> And while it does add a lot of convenience and consistency to how
> operators behave, it does also put a lot of limitations on what they can
> do.
>
> For example, it's not currently possible to implement comparison
> operators that would behave lazily and only compute a certain value once
> a function call forces the computation.
>
> Similarly, it's not possible to construct a DSL that would produce code
> or a query in another language, by returning values behave like AST
> nodes rather than actually performing a comparison.
>
> ie.
>   table.users
>        .where!((users) => users.joined_on >= 3.days.ago)
>        .joins(table.posts)
>        .on!((posts, users) => posts.user_id == users.id)
>        .limit(10)
>        .toSql;
>
> It's a little odd to me that D puts so little focus on DSLs, since
> language features like template constraints, `opDispatch`, and even the
> fact that binary operators are overloadable via a single templated
> function are all very useful tools for implementing them.

I completely agree. Or we could go with an even more general approach: AST macros [1].

[1] http://wiki.dlang.org/DIP50

-- 
/Jacob Carlborg
September 28, 2016
On Wednesday, 28 September 2016 at 04:02:59 UTC, Walter Bright wrote:
> The limitations are deliberate based on the idea that comparison operators need to be consistent and predictable, and should have a close relationship to the mathematical meaning of the operators.

In Mathematics the comparison operators are also commonly used for semi orders, which cannot be implemented by opCmp, because opCmp has no way to indicate that two values are incomparable.

Interestingly the floating point types are semi ordered (due to NaNs), and for those D has some (non-overridable and deprecated) operators like !<, which would be easily extendable to any semi order, whereas the suggested replacement (i.e. test for NaNs manually) works only on floats.

September 28, 2016
On Wednesday, 28 September 2016 at 03:28:50 UTC, Minty Fresh wrote:
> Using strings and mixins does have quite a number of downsides. The additional work required to past the DSL aside, you also usually lose the scope in which things are declared (especially if you wish to reference or declare variables/functions/other constructs within your DSL), error reporting becomes much less precise, and the transition between the outer code and the inner DSL becomes jarring (especially in terms of syntax highlighting).
>
> That said, I love the fact D has a separate operator for concatenation. It's extremely useful to not have that ambiguity.
>
> Another thought is, operators not behaving as expected doesn't strike me as a failure of the language, given the language supports operator overloading. It's a failure in documentation to explain what the operators are intended to do, or a failure in implementation to behave as they should.

I agree that opCmp has the benefit of unambiguity, but I still think Minty makes very good points.

I think the cleanest possible solution would be to allow comparison operators to be overridden with opBinary and opBinaryRight, as well, but use opCmp and opEquals instead if they exist. I can't think of any way this would break existing code.

On Wednesday, 28 September 2016 at 09:48:48 UTC, Matthias Bentrup wrote:
> In Mathematics the comparison operators are also commonly used for semi orders, which cannot be implemented by opCmp, because opCmp has no way to indicate that two values are incomparable.
>
> Interestingly the floating point types are semi ordered (due to NaNs), and for those D has some (non-overridable and deprecated) operators like !<, which would be easily extendable to any semi order, whereas the suggested replacement (i.e. test for NaNs manually) works only on floats.

It's probably a terrible idea, but I'd love if those FP comparison operators would be de-deprecated and made possible to overload using opBinary.

On Wednesday, 28 September 2016 at 06:05:54 UTC, Jonathan M Davis wrote:
> The increment and decrement operators are in a similar boat to the comparison operators in that post and pre can be derived from a single function. So, it's not just the comparison operators that got combined.

Incidentally, I'm a little surprised and disappointed that D doesn't allow pre- and postfix operators to be separately overloaded. Postfix operators should default to their current behavior, where `x<op>` is the same as `auto t = x; x = <op>x; return t;` where prefix unary <op> is defined, but I also think it'd be fantastic if we introduced an opUnaryPostfix override. (And should that be done, for the sake of clarity, it might be a good idea deprecate usage of opUnary in favor of calling the method opUnaryPrefix.)



September 28, 2016
I'd also like to point out a generally sound design principle:

Give the programmer as much power and flexibility as possible - but don't forget to provide tools for simplifying common use cases, and don't forget to define sensible defaults.

It makes a lot of sense that opCmp and opEquals exist, they fit the by-far-most-common use case perfectly, but they aren't always flexible enough for what the programmer wants to do. What if comparison operators should return something other than booleans? What if `!=` should be implemented differently from `==`? These overrides great tools to have, but it would be better if they weren't the only options we had.

Similarly, it makes a lot of sense that postfix operators are rewritten in terms of prefix operators, but that's not always flexible enough for what the programmer wants to do. It's a great default to have, but it would be better if we also had the option to define our own distinct behaviors for postfix operators.
September 28, 2016
On 28.09.2016 11:48, Matthias Bentrup wrote:
> On Wednesday, 28 September 2016 at 04:02:59 UTC, Walter Bright wrote:
>> The limitations are deliberate based on the idea that comparison
>> operators need to be consistent and predictable, and should have a
>> close relationship to the mathematical meaning of the operators.
>
> In Mathematics the comparison operators are also commonly used for semi
> orders, which cannot be implemented by opCmp, because opCmp has no way
> to indicate that two values are incomparable.
> ...

Yes it does.

float opCmp(S rhs){
    return float.nan; // incomparable
}

« First   ‹ Prev
1 2 3 4 5 6 7 8 9 10