December 01, 2009
Steven Schveighoffer wrote:
> On Tue, 01 Dec 2009 13:50:38 -0500, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> 
>> Steven Schveighoffer wrote:
>>> On Sat, 28 Nov 2009 18:36:07 -0500, Walter Bright <newshound1@digitalmars.com> wrote:
>>>
>>>> And here it is (called opDispatch, Michel Fortin's suggestion):
>>>>
>>>> http://www.dsource.org/projects/dmd/changeset?new=trunk%2Fsrc@268&old=trunk%2Fsrc@267 
>>>>
>>>  I have a few questions:
>>>  1. How should the compiler restrict opDispatch's string argument?  i.e. if I implement opDispatch, I'm normally expecting the string to be a symbol, but one can directly call opDispatch with any string (I can see clever usages which compile but for instance circumvent const or something), forcing me to always constrain the string argument, i.e. always have isValidSymbol(s) in my constraints.  Should the compiler restrict the string to always being a valid symbol name (or operator, see question 2)?
>>
>> Where in doubt, acquire more power :o). I'd say no checks; let user code do that or deal with those cases.
> 
> It is unlikely that anything other than symbols are expected for opDispatch, I can't think of an example that would not want to put the isValidSymbol constraint on the method.
> 
> An example of abuse:
> 
> struct caseInsensitiveWrapper(T)
> {
>    T _t;
>    auto opDispatch(string fname, A...) (A args)
>    {
>       mixin("return _t." ~ toLower(fname) ~ "(args);");
>    }
> }
> 
> class C { int x; void foo(); }
> 
> caseInsensitiveWrapper!(C) ciw;
> ciw._t = new C;
> ciw.opDispatch!("x = 5, delete _t, _t.foo")();
> 
> I don't know if this is anything to worry about, but my preference as an author for caseInsensitiveWrapper is that this last line should never compile without any special requirements from me.
> 
>>
>>> 2. Can we cover templated operators with opDispatch?  I can envision something like this:
>>>  opDispatch(string s)(int rhs) if(s == "+") {...}
>>
>> How do you mean that?
> 
> Isn't opBinary almost identical to opDispatch?  The only difference I see is that opBinary works with operators as the 'symbol' and dispatch works with valid symbols.  Is it important to distinguish between operators and custom dispatch?
> 
> -Steve
opBinary is a binary operator, opDispatch can be anything. I think they should be kept separate.
December 01, 2009
On Tue, 01 Dec 2009 15:06:27 -0500, Pelle Månsson <pelle.mansson@gmail.com> wrote:

> Steven Schveighoffer wrote:

>>  Isn't opBinary almost identical to opDispatch?  The only difference I see is that opBinary works with operators as the 'symbol' and dispatch works with valid symbols.  Is it important to distinguish between operators and custom dispatch?
>>  -Steve
> opBinary is a binary operator, opDispatch can be anything. I think they should be kept separate.

You could say the same thing about dynamic properties.  How come we don't split those out as opProperty?

opDispatch can do opBinary, it's a subset.  It makes no sense to define opDispatch(string s)() if(s == "+") I agree, but I don't see any reason why opBinary(string s)() would fail to compile...

-Steve
December 01, 2009
On Tue, Dec 1, 2009 at 12:38 PM, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> On Tue, 01 Dec 2009 15:06:27 -0500, Pelle Månsson <pelle.mansson@gmail.com> wrote:
>
>> Steven Schveighoffer wrote:
>
>>>  Isn't opBinary almost identical to opDispatch?  The only difference I
>>> see is that opBinary works with operators as the 'symbol' and dispatch works
>>> with valid symbols.  Is it important to distinguish between operators and
>>> custom dispatch?
>>>  -Steve
>>
>> opBinary is a binary operator, opDispatch can be anything. I think they should be kept separate.
>
> You could say the same thing about dynamic properties.  How come we don't split those out as opProperty?

That's because of what Andrei pointed out:  &a.b .
The compiler can't tell if you want a delegate to the method b, or the
address of a property b.

> opDispatch can do opBinary, it's a subset.  It makes no sense to define
> opDispatch(string s)() if(s == "+") I agree, but I don't see any reason why
> opBinary(string s)() would fail to compile...

I don't get your point.  It's the compiler that decides to call opBinary and it's only gonna decide to do so for binary operators. Even if you pretend opBinary can accept any string.

--bb
December 01, 2009
On Tue, Dec 1, 2009 at 1:01 PM, Bill Baxter <wbaxter@gmail.com> wrote:
> On Tue, Dec 1, 2009 at 12:38 PM, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>> On Tue, 01 Dec 2009 15:06:27 -0500, Pelle Månsson <pelle.mansson@gmail.com> wrote:
>>
>>> Steven Schveighoffer wrote:
>>
>>>>  Isn't opBinary almost identical to opDispatch?  The only difference I
>>>> see is that opBinary works with operators as the 'symbol' and dispatch works
>>>> with valid symbols.  Is it important to distinguish between operators and
>>>> custom dispatch?
>>>>  -Steve
>>>
>>> opBinary is a binary operator, opDispatch can be anything. I think they should be kept separate.
>>
>> You could say the same thing about dynamic properties.  How come we don't split those out as opProperty?
>
> That's because of what Andrei pointed out:  &a.b .
> The compiler can't tell if you want a delegate to the method b, or the
> address of a property b.

... but maybe the syntax for "the function itself" should be distinct
from "dereference" anyway.
I can't think of any reason the two need to use the same syntax other
than that &func was called a "function pointer" back in C.
There's no case for "generic code" needing it to be the same syntax as
far as I can tell.

--bb
December 01, 2009
On Tue, 01 Dec 2009 16:01:41 -0500, Bill Baxter <wbaxter@gmail.com> wrote:

> On Tue, Dec 1, 2009 at 12:38 PM, Steven Schveighoffer
> <schveiguy@yahoo.com> wrote:
>> On Tue, 01 Dec 2009 15:06:27 -0500, Pelle Månsson <pelle.mansson@gmail.com>
>> wrote:
>>
>>> Steven Schveighoffer wrote:
>>
>>>>  Isn't opBinary almost identical to opDispatch?  The only difference I
>>>> see is that opBinary works with operators as the 'symbol' and dispatch works
>>>> with valid symbols.  Is it important to distinguish between operators and
>>>> custom dispatch?
>>>>  -Steve
>>>
>>> opBinary is a binary operator, opDispatch can be anything. I think they
>>> should be kept separate.
>>
>> You could say the same thing about dynamic properties.  How come we don't
>> split those out as opProperty?
>
> That's because of what Andrei pointed out:  &a.b .
> The compiler can't tell if you want a delegate to the method b, or the
> address of a property b.

Huh?

>
>> opDispatch can do opBinary, it's a subset.  It makes no sense to define
>> opDispatch(string s)() if(s == "+") I agree, but I don't see any reason why
>> opBinary(string s)() would fail to compile...
>
> I don't get your point.  It's the compiler that decides to call
> opBinary and it's only gonna decide to do so for binary operators.
> Even if you pretend opBinary can accept any string.

My point is, the set of strings passed by the compiler to opBinary is completely disjoint from the set of strings passed by the compiler to opDispatch.  So the only reason to keep them separate is because you want to force people to split their code between operators and methods/properties.

There is no technical reason we need to keep them separate or to combine them that I can see.

-Steve
December 01, 2009
Tue, 01 Dec 2009 10:39:44 -0800, Andrei Alexandrescu wrote:

> retard wrote:
>> Tue, 01 Dec 2009 03:16:47 -0800, Walter Bright wrote:
>> 
>>> Ary Borenszweig wrote:
>>>> Can you show examples of points 2, 3 and 4?
>>> Have opDispatch look up the string in an associative array that returns an associated delegate, then call the delegate.
>>>
>>> The dynamic part will be loading up the associative array at run time.
>> 
>> This is not exactly what everyone of us expected. I'd like to have something like
>> 
>> void foo(Object o) {
>>   o.duckMethod();
>> }
>> 
>> foo(new Object() { void duckMethod() {} });
>> 
>> The feature isn't very dynamic since the dispatch rules are defined statically. The only thing you can do is rewire the associative array when forwarding statically precalculated dispatching.
> 
> Walter is right. But as it seems there is a lot of confusion about the feature, maybe we didn't define the feature (which is very general and powerful and as dynamic as you ever want to make it) in a palatable way.
> 
> Ideas?

Well, the most important feature of dynamic types in languages like Python is that you don't need to worry about types anywhere. Even with opDispatch you need to configure parametric types for parameters etc. A python coder wouldn't use D unless you can get rid of all type annotations.
December 01, 2009
On Tue, Dec 1, 2009 at 1:10 PM, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> On Tue, 01 Dec 2009 16:01:41 -0500, Bill Baxter <wbaxter@gmail.com> wrote:
>
>> On Tue, Dec 1, 2009 at 12:38 PM, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>>>
>>> On Tue, 01 Dec 2009 15:06:27 -0500, Pelle Månsson
>>> <pelle.mansson@gmail.com>
>>> wrote:
>>>
>>>> Steven Schveighoffer wrote:
>>>
>>>>>  Isn't opBinary almost identical to opDispatch?  The only difference I
>>>>> see is that opBinary works with operators as the 'symbol' and dispatch
>>>>> works
>>>>> with valid symbols.  Is it important to distinguish between operators
>>>>> and
>>>>> custom dispatch?
>>>>>  -Steve
>>>>
>>>> opBinary is a binary operator, opDispatch can be anything. I think they should be kept separate.
>>>
>>> You could say the same thing about dynamic properties.  How come we don't split those out as opProperty?
>>
>> That's because of what Andrei pointed out:  &a.b .
>> The compiler can't tell if you want a delegate to the method b, or the
>> address of a property b.
>
> Huh?

If you have this:

struct S {
int opProperty(string s)() if(s=="b") { ... }
int opDispatch(string s)() if(s=="b") { ... }
}

S a;
auto x = &a.b;

which one are you talking about?  The property a.b or the method a.b()?  That's why you can't split out properties as opProperty.

But actually maybe this is no longer true?  I'm not sure what the @property is going to do to how we refer to a function as a piece of data.  Maybe D won't require the & any more.  Then &a.b could only refer to the property.

So anyway, I think your argument is bad as things currently stand. You asked if opBinary and opDispatch are separate, then why not opProperty.  Well, there's an ambiguity if you split off opProperty, that's why not.  There isn't any ambiguity in splitting off opBinary.

>>> opDispatch can do opBinary, it's a subset.  It makes no sense to define
>>> opDispatch(string s)() if(s == "+") I agree, but I don't see any reason
>>> why
>>> opBinary(string s)() would fail to compile...
>>
>> I don't get your point.  It's the compiler that decides to call opBinary and it's only gonna decide to do so for binary operators. Even if you pretend opBinary can accept any string.
>
> My point is, the set of strings passed by the compiler to opBinary is completely disjoint from the set of strings passed by the compiler to opDispatch.  So the only reason to keep them separate is because you want to force people to split their code between operators and methods/properties.
>
> There is no technical reason we need to keep them separate or to combine them that I can see.

How about this: given only a catch-all opDispatch which implements dynamic dispatch, the compiler cannot statically determine if operators are really implemented or not.  Since the list of operators is always finite, it makes sense to have them in a separate "namespace" of sorts.   That way if you implement a catch-all opBinary, you're only saying that you implement all /operators/ not all possible methods.  And vice versa, you can specify that you only implement some operators, but still have dynamic dispatch that forwards all named methods.

Perhaps, though, there should be a rule where opBinary("+") is tried
first, and if not defined then opDispatch("+") could be tried.  Not
sure if it's worth the mental burden of another rule, though.

--bb
December 01, 2009
Andrei Alexandrescu wrote:
> Parameters to dynDispatch (the user-defined forwarding function), NOT opDispatch. opDispatch can take _anything_.
> 
> Sorry if I'm repeating what you know already, but I am obsessing over a small misunderstanding could end up hamstringing this very powerful feature.
> 
> So: opDispatch has absolutely no restrictions except a string in the first static parameters position.

I agree and I'm not misunderstanding you. I'm just saying how Variant should work, not opDispatch!
December 01, 2009
On Tue, 01 Dec 2009 17:24:30 -0500, Bill Baxter <wbaxter@gmail.com> wrote:

> On Tue, Dec 1, 2009 at 1:10 PM, Steven Schveighoffer
> <schveiguy@yahoo.com> wrote:
>> On Tue, 01 Dec 2009 16:01:41 -0500, Bill Baxter <wbaxter@gmail.com> wrote:
>>
>>> On Tue, Dec 1, 2009 at 12:38 PM, Steven Schveighoffer
>>> <schveiguy@yahoo.com> wrote:
>>>>
>>>> On Tue, 01 Dec 2009 15:06:27 -0500, Pelle Månsson
>>>> <pelle.mansson@gmail.com>
>>>> wrote:
>>>>
>>>>> Steven Schveighoffer wrote:
>>>>
>>>>>>  Isn't opBinary almost identical to opDispatch?  The only difference I
>>>>>> see is that opBinary works with operators as the 'symbol' and dispatch
>>>>>> works
>>>>>> with valid symbols.  Is it important to distinguish between operators
>>>>>> and
>>>>>> custom dispatch?
>>>>>>  -Steve
>>>>>
>>>>> opBinary is a binary operator, opDispatch can be anything. I think they
>>>>> should be kept separate.
>>>>
>>>> You could say the same thing about dynamic properties.  How come we don't
>>>> split those out as opProperty?
>>>
>>> That's because of what Andrei pointed out:  &a.b .
>>> The compiler can't tell if you want a delegate to the method b, or the
>>> address of a property b.
>>
>> Huh?
>
> If you have this:
>
> struct S {
> int opProperty(string s)() if(s=="b") { ... }
> int opDispatch(string s)() if(s=="b") { ... }
> }
>
> S a;
> auto x = &a.b;
>
> which one are you talking about?  The property a.b or the method
> a.b()?  That's why you can't split out properties as opProperty.

This seems like an ambiguity.  You cannot define both the property b and the method b.

> But actually maybe this is no longer true?  I'm not sure what the
> @property is going to do to how we refer to a function as a piece of
> data.  Maybe D won't require the & any more.  Then &a.b could only
> refer to the property.
>
> So anyway, I think your argument is bad as things currently stand.
> You asked if opBinary and opDispatch are separate, then why not
> opProperty.  Well, there's an ambiguity if you split off opProperty,
> that's why not.  There isn't any ambiguity in splitting off opBinary.

FTR, I'm not pushing this, just pointing out the inconsistency.

>>>> opDispatch can do opBinary, it's a subset.  It makes no sense to define
>>>> opDispatch(string s)() if(s == "+") I agree, but I don't see any reason
>>>> why
>>>> opBinary(string s)() would fail to compile...
>>>
>>> I don't get your point.  It's the compiler that decides to call
>>> opBinary and it's only gonna decide to do so for binary operators.
>>> Even if you pretend opBinary can accept any string.
>>
>> My point is, the set of strings passed by the compiler to opBinary is
>> completely disjoint from the set of strings passed by the compiler to
>> opDispatch.  So the only reason to keep them separate is because you want to
>> force people to split their code between operators and methods/properties.
>>
>> There is no technical reason we need to keep them separate or to combine
>> them that I can see.
>
> How about this: given only a catch-all opDispatch which implements
> dynamic dispatch, the compiler cannot statically determine if
> operators are really implemented or not.

Why does it have to?

proposed implementation:

compiler sees 'a + b'
compiler rewrites 'a.opBinary!"+"(b)'
does it compile?  If yes, then a implements the operator.

With opDispatch:

compiler sees 'a + b'
compiler rewrites 'a.opDispatch!"+"(b)'
does it compile?  If yes, then a implements the operator.

I don't see the problem.

> Since the list of operators
> is always finite, it makes sense to have them in a separate
> "namespace" of sorts.   That way if you implement a catch-all
> opBinary, you're only saying that you implement all /operators/ not
> all possible methods.  And vice versa, you can specify that you only
> implement some operators, but still have dynamic dispatch that
> forwards all named methods.

opDispatch(string s, T)(T arg) if(isOperator(s))

opDispatch(string s, T...)(T arg) if(isSymbol(s))

BTW, you are already going to want to do that for both to prevent abuse, see my original reply in this sub-thread.

-Steve
December 01, 2009
On Tue, Dec 1, 2009 at 3:01 PM, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>> How about this: given only a catch-all opDispatch which implements dynamic dispatch, the compiler cannot statically determine if operators are really implemented or not.
>
> Why does it have to?
> proposed implementation:
>
> compiler sees 'a + b'
> compiler rewrites 'a.opBinary!"+"(b)'
> does it compile?  If yes, then a implements the operator.
>
> With opDispatch:
>
> compiler sees 'a + b'
> compiler rewrites 'a.opDispatch!"+"(b)'
> does it compile?  If yes, then a implements the operator.
>
> I don't see the problem.
>
>> Since the list of operators
>> is always finite, it makes sense to have them in a separate
>> "namespace" of sorts.   That way if you implement a catch-all
>> opBinary, you're only saying that you implement all /operators/ not
>> all possible methods.  And vice versa, you can specify that you only
>> implement some operators, but still have dynamic dispatch that
>> forwards all named methods.
>
> opDispatch(string s, T)(T arg) if(isOperator(s))
>
> opDispatch(string s, T...)(T arg) if(isSymbol(s))

Good counterpoints to my argument.  So I give up on that line.

Here's another, how do you implement the opBinary_r operators with opDispatch?

--bb