February 13, 2019
On Wednesday, 13 February 2019 at 02:00:32 UTC, Nicholas Wilson wrote:
> On Wednesday, 13 February 2019 at 01:24:45 UTC, Rubn wrote:
>> Always hear that D is somehow better than C++ for operators but it isn't in quite a few places already.
>>
>>     int a;
>>     auto c = (a) <= 0; // ok
>>     auto d = (a) => 0; // not ok
>
> Oooh. That one's nasty! Technically unambiguous, but nasty!
>
>> For some reason Walter thought in D if you overload the "+" operator you couldn't make it not commutative?? Still never got a reply to that, so I'll just assume he didn't know what commutative was. Yes you can make "a + b != b + a" be true quite easily.
>
> With opBinary doing something completely different to opBinaryRight? Anyway that falls under the category of deliberate abuse, something we should not be considering, if people do that kind of thing then they should get what they deserve.

There are valid deliberate uses for this. For scalar types multiplication might be commutative, but for matrix multiplication it is not commutative. What good would it be to not be able to use an operator that denotes multiplication in the way it is intended to be used regarding the two types that are being multiplied. If you have a poorly named function it is in effect the same thing as misusing an operator. It'd be the same as misnaming a function, you read the function it doesn't do what the name suggests you end up having to look it up and you know for next time. Arguably it'd be easier to detect, if you say a class named "Magic" and it is multiplied by an integer, it makes you wonder what it is doing. But if you have have a function in "Magic" called "deleteUniverse()" what do you think that function is going to do? Oh when you look inside you find an empty function with a comment "TODO: figure out if this is possible". Which is worse in this situation? I'd argue the misnamed function is much more dangerous, at least if I see an operator being used with a custom type I know I should lookup what the operator means. There's also no good way to prevent poorly named functions.

February 14, 2019
On Wednesday, 13 February 2019 at 18:42:43 UTC, H. S. Teoh wrote:
> On Wed, Feb 13, 2019 at 01:50:21AM +0000, Nicholas Wilson via Digitalmars-d wrote:
>> On Wednesday, 13 February 2019 at 00:56:48 UTC, H. S. Teoh wrote:
>> > Frankly, I think it's a very good thing that in D comparison operators are confined to opEquals/opCmp.
>> 
>> So do I. Many more objects have partial ordering than arithmetic, having opCmp under opBinary would be annoying.
>
> [opCmp doesn't actually work for partial ordering]

This proposal would actually let you match

<  : subset
<= : subset or equal
...

and would say, let you return a tuple of (result of predicate, are sets disjoint)

auto sub = setA < setB;
if (sub.pred && !sub.disjoint) { ... }

>> > Operator overloading should be reserved for objects that behave like arithmetical entities in some way.
>> 
>> Like symbolic math.
>
> I know what you're trying to drive at, but there's a difference arithmetic in code vs. arithmetic in math.

What difference? Graph execution of math math on a nice library looks like code math and I don't see anyone in PyTorch complaining.

> An expression in code is generally assumed to execute when control flow reaches that line of code.

async says hello.

> It's unexpected behaviour for what looks like an assignment of the result of an expression to instead assign the expression itself as something to be evaluated later.

You are likely missing the context of the expression, e.g. a DB query, or that you are dealing with symbolic math. Or you don't (and can't) care because you are in generic code.

> What I meant was that == and <= should behave consistently with each other.  Ideally both <= and == should be handled by the same function. Then consistency would be guaranteed. But for practical reasons we separate them, one reason being that often equality is much cheaper to compute than linear ordering.

Also manyfold more objects can be compared for (in)equality than can be for ordering.
February 14, 2019
On Wednesday, 13 February 2019 at 18:42:43 UTC, H. S. Teoh wrote:
> On Wed, Feb 13, 2019 at 01:50:21AM +0000, Nicholas Wilson via Digitalmars-d wrote:
>> On Wednesday, 13 February 2019 at 00:56:48 UTC, H. S. Teoh wrote:
>> > The usual invocation of such a DSL as a compile-time argument, say something like this:
>> > 
>> > 	myDsl!'a <= b'
>> > 
>> > contains one often overlooked, but very important element: the identifier `myDsl`, that sets it apart from other uses of `<=`, and clearly identifies which interpretation should be ascribed to the symbols found in the template argument.
>> 
>> Its also much uglier and does not commute with things that use <= i.e. generic functions.
>
> Ugliness is debatable; I find that the identifier serves as a good marker to indicate departure from usual semantics.

All of the examples in the thread are context-free. The distinction between eval now to a bool vs other uses should be a lot more pronounced with the context of the surrounding code. Or you simply just don't care.

> As for [myDsl!'<'] not commuting with generic functions, do you have a specific example in mind?  Because all the obvious (to me) examples I can think of are cases where I think would be better off *not* working with generic code, because the generic code expects one set of semantics but overloading opCmp/opEquals to return arbitrary objects produce a different set of semantics.

Math kernels. kern!float would be a scalar form, kern!float4 would be vectorised, kern!Expression would be something you could pass to e.g. TensorFlow.

>> No. auto y = p <= q; should not e.g. open a socket (you could probably do that already with an impure opCmp). Being able to capture the expression `p <= q` is the _entire point_ of the proposal.
>
> And that's the point I disagree on.  The current expectation of a line of code like `auto y = p <= q;` is that typeof(y) == bool.  For it to return a type other than bool is unexpected behaviour,

Such is the nature of change.

> and leads to subtle differences in semantics

Which should be obvious from the context of the code, or you simply don't care about the distinction.

> that will likely result in bugs.

I don't think so. Compile errors yes, because types won't match. But bugs?

> Explicitly marking it, e.g., as `auto y = symbolic!"p <= q";` makes it much more obvious what's really going on with the code.

Again these examples are context free: You know the types of p and q.
and if you don't (generic code) then you simply don't care. Also if you have a page full of `symbolic!"blah"` you're pretty quickly going to lose the forest for the trees, especially when you have opBinary overloaded to symbolics without `symbolic!"blah"`.

> Yes it's ugly, but at the same time this ugliness is IMO necessary warning for the would-be code maintainer to understand that the semantics depart from the usual expectations.

You have comments for that.

> My point was that there are no such operators as ∘ or ± in D, so even if you had free-for-all operator overloading you couldn't implement such an expression.  Using a DSL frees you from that constraint.
>
> And because there are no such operators in D, they have no standard meaning defined by the language, so different authors likely have different definitions for them.

Most of those operators have standard definitions in math which makes that point moot. And if you're making up operators willy-nilly then you should expect to end up with an unmaintainable mess. I'm not catering for those kind of people.

> So even in the case where you *could* add arbitrary operators to the language, you'll end up with a mess as soon as your code imports two libraries that happen to overload some of the same custom operators -- it would be hard to tell which set of semantics apply to the operators in any given piece of code; you'd have to study the context to be sure.

All you need to know are the types involved and more usefully what module they come from, which any decent editor with plugin should be able to tell you.

> Having the `eval` or whatever else prefixed identifier in front of the DSL resolves that question instantly.

Yes but does it need to be there? (aside the missing bounds and ambiguity of the integral) I'd be surprised if that wasn't valid Julia code. Now I'm not suggesting we go that far, having arbitrary unicode operators would be a nightmare, what with normalisation and all (not to mention trying to ender the damn things on a keyboard).

February 14, 2019
On Wednesday, 13 February 2019 at 21:01:46 UTC, Rubn wrote:
> On Wednesday, 13 February 2019 at 02:00:32 UTC, Nicholas Wilson
>> With opBinary doing something completely different to opBinaryRight? Anyway that falls under the category of deliberate abuse, something we should not be considering, if people do that kind of thing then they should get what they deserve.
>
> There are valid deliberate uses for this. For scalar types multiplication might be commutative, but for matrix multiplication it is not commutative.

Indeed, but you're example was with + ;). we could probably do a python and add @ as a binary operator for that kind of thing anyway. (# is also free)

> Which is worse in this situation? I'd argue the misnamed function is much more dangerous, at least if I see an operator being used with a custom type I know I should lookup what the operator means. There's also no good way to prevent poorly named functions.

Indeed.
February 14, 2019
On Wednesday, 13 February 2019 at 16:07:06 UTC, H. S. Teoh wrote:
> On Wed, Feb 13, 2019 at 12:12:17PM +0000, jmh530 via Digitalmars-d wrote:
>> On Wednesday, 13 February 2019 at 02:37:50 UTC, H. S. Teoh wrote:
>> > [snip}
>> > 
>> > Haha... yeah, I *thought* the choice of => for lambda syntax was not a good idea.  But people seemed to love it at the time, what can I say?  :-/
>> > 
>> 
>> What do you prefer instead? Can C++'s -> cause similar ambiguities?
>
> I'll admit, I've never really thought too hard about it.  But the main idea is to choose something that can't be confused for something else. Since -> isn't a token in D, it seems to be a viable candidate. But it might be confusing for the, *ahem*, droves of C++ coders who just can't wait to migrate to D. ;-)

-> is overloaded in C++ as:
 a->b as (*a).b
 a->b an overloadable operator
 a-->b ("goes to")
auto func() -> T (trailing return)
and I'm sure some others that I've missed.

Being confused is par for the course, I think they'd be right at home ;)

February 14, 2019
On Thursday, 14 February 2019 at 01:54:17 UTC, Nicholas Wilson wrote:
>  a-->b ("goes to")

What? Pretty sure that parses to (a--) > b
February 14, 2019
On Thursday, 14 February 2019 at 17:42:48 UTC, Olivier FAURE wrote:
> On Thursday, 14 February 2019 at 01:54:17 UTC, Nicholas Wilson wrote:
>>  a-->b ("goes to")
>
> What? Pretty sure that parses to (a--) > b

Yes, that's the joke.
1 2 3 4
Next ›   Last »