April 19, 2009
BCS wrote:

> Hello Adam,
> 
>> On Sat, Apr 18, 2009 at 06:10:27PM -0700, Andrei Alexandrescu wrote:
>> 
>>> The point of using "." is not syntactic convenience as much as the ability of the Dynamic structure to work out of the box with algorithms that use the standard notation.
>>> 
>> What if the dot remained exactly like it is now and the -> took
>> the place of the dot in the proposal; regular method calls when
>> possible and forwarded to opExtension (opDotExp needs a better name)
>> when that fails?
>> Thus your generic algorithm can use -> and work in all cases, and the
>> dot operator remains the same as it is now.
> 
> Going the other way would be better; '.' works as Andrei wants and '->' is an explicit, "don't use the dynamic stuff" invocation. If it went the other way virtually all template code would end up needing to use '->' for everything just in cases someone wants to pass in a type that uses opDotExp.
Yea and that would be bad, since then as far as I am concerned you have destroyed the purpose of templates. Seems to me templates and dynamic calls are sorta there to solve the same problem, how do you write code that is independent of type? Dynamic takes it to extreme and makes the evaluation of methods (or public types variables I don't see how that would be different) and leave it all to runtime which is far more flexible but potentially has holes if the type doesn't meet the requirements. Templates take a more conservative route by still letting you write independent of type but then using the information provided at call time it can still perform static code checks to make sure the type meets its needs, making it a little more safe than dynamic. However soon as you throw some dynamic calls into a template the guarantees usually provided go out the window since the template is letting that none existent member slip by. Consider below, why not just use a completely dynamic function as at least then you know what you are letting yourself in for?:

void function(T)(T i)
{
   i.bob();
}

void function(Object i)
{
   i.bob();
}

In the above both functions do exactly the same thing, only difference is with the template sometimes the call is static sometimes and other times it isn't. All you are saving yourself is maybe a performance improvement (which I admit I would probably use the template for just that reason)? However you've lost the compile-time checking of templates and still add the possibility of typos or bad method names leaking in (consider in my previous example if I removed the open method from ServerProxy, with opDotExp it would take over control of the call to 'open' if I happen to forget to remove the call from somewhere).

However then I suppose then you are still screwing over the half and half people by not using dynamic calls in templates.

T function(T)(T i)
{
   i.bob();
   return i;
}

Variant function(Variant i)
{
   i.bob();
   return i;
}

Using the dynamic method the user is forced to perform a cast where as you don't with the template version.

Ok, proposal draft 2 :-P (bare with me this is mostly a mind dump so I apologize for if I may seem to ramble).

So what I said previously still stands. StaticAsMuchAsPoss lives with best of both worlds and therefore if I tomorrow decided to remove the method "open" it would fail with a compile-time error where as  DynamicAsMuchAsPoss leaves it till run-time. Object/Variant, or whatever opArrow returns, would implement default behaviour (and opArrow would be abstract) of searching the type for implementations of the provided function name (or it could be a public variable name) and if such a thing exists it executes it else throws a runtime error. This allows what you see in DynamicAsMuchAsPoss and AlsoShowBypassNeedToUpcast to be possible, as well as code like something-
>bob(10)->nono(11) without worrying about casts (if nono was implemented as
an actual function, so we live dynamically). So we get sort of below.

class Object
{
   .... usual ....
   Object opArrow(char[] name, v...)
   {
       if (this.publicmethodexists(name))
          return invoke(name, this, v);
       else if (this.publicvariableexists(name))
          return invoke(name, this);
       else
          throw NoSuchMemberException(name, v);
   }
   .... more usual ....
}

class A
{
    void bob() {}
    void opArrow(char[] name, v ...)
    {
        if (name == "cheese")
           dosomething();
        else
           super.opArrow(name, v);
    }
}

void main()
{
    A a = new A();
    a.bob();	// Compile time valid
    a.cheese();	// compile time invalid
    a->cheese();	// runtime valid
    a->bob();          // runtime valid
    a.nono();	// Compile time invalid
    a->nono();	// Runtime invalid

    Object obj = a;
    obj.bob();	// Compile time invalid
    obj.cheese();	// compile time invalid
    obj->cheese();	// runtime valid
    obj->bob();        // runtime valid
    obj.nono();	// Compile time invalid
    obj->nono();	// Runtime invalid
}

As for templates then how about a dynamic template call that does 1 or 2 passes through the code to prepare it to be dynamic? How about we replace the '!' with a '?' for the dynamic call, that way we can have strict templates (!) and dynamic temapltes (?). See below using the previously discussed template.

T myfunc(T i)
{
    i.bob();
    return i;
}

class MyStrictClass
{
    void bob() {}
}

MyStrictClass c = myfunc!(MyStrictClass)(new MyStrictClass());	// strict
call converts to below (default behaviour where i am not verbose with !() )

MyStrictClass myfunc(MyStrictClass i)
{
    i.bob();
    return i;
}


class MyDynamicClass
{
    void opArrow(char[] name, v ...)
    {
        if (name == "bob")
          something;
        else
           super.opArrow(name, v);
    }
}

MyDynamicClass c = myfunc?(MyDynamicClass)(new MyDynamicClass());	// dynamic
call converts to below

// It goes through and each . call is converted to a dynamic call
MyDynamicClass myfunc(MyDynamicClass i)
{
    i->bob();
    return i;
}

The above becomes completely dynamic. However that could make it all slow due to dynamic calls everywhere which in some cases may be unnecessary, think about below

T myfunc(T i)
{
    i.bob();
    i.nono();
    return i;
}

class MyStrictClass
{
    void bob() {}
    void nono() {}
}

class MyDynamicClass
{
    void bob() {}
    void opArrow(char[] name, v ...)
    {
        if (name == "nono")
          something;
        else
           super.opArrow(name, v);
    }
}

// static myfunc!(MyStrictClass)
MyStrictClass myfunc(MyStrictClass i)
{
    i.bob();
    i.nono();
    return i;
}

// dynamic myfunc?(MyDynamicClass)
MyDynamicClass myfunc(MyDynamicClass i)
{
    i->bob();	// This is unnecessary
    i->nono();
    return i;
}

// dynamic alternative myfunc?(MyDynamicClass)
MyDynamicClass myfunc(MyDynamicClass i)
{
    i.bob();	// Was identified as being there so kept as static
    i->nono();	// Wasn't found in type definition so made dynamic
    return i;
}

So now we get the template attempting to use the best of both worlds and only giving in to being dynamic where necessary, it means that in some cases your dynamic templates would not need to be dynamic at all.

The next place I see some potential issue is return types of methods call from dynamic inside the template where it may be again unnecessarily using dynamic.

T myfunc(T,U)(T t)
{
    U u = t.bob();
    u.something();
    return t;
}

class A
{
   Object opArrow(char[] name, v ...)
   {
       if (name == "bob")
         return call("bob", v);
       else
         return super.opArrow(name, v);
   }
}

class B
{
   void something() {}
}

A a = myfunc?(A,B)(new A()); // becomes
A myfunc(A t)
{
    B u = t->bob();	// Compile time error, not variant/object
    u.something();
    return t;
}

You could get around above by replacing U with auto

T myfunc(T)(T t)
{
    auto u = t.bob();
    u.something();
    return t;
}
A a = myfunc?(A)(new A()); // becomes
A myfunc(A t)
{
    auto u = t->bob();	// Gets return as object so from here on it starts to
unfold that the rest of the template is dynamic
    u->something();   // Since this is not object/variant it is forced to
dynamic
    return t;
}

However that doesn't fix below.

U myfunc(T,U)(T t)
{
    auto u = t.bob();
    u.something();
    return u;
}
B b = myfunc?(A,B)(new A()); // becomes
B myfunc(A t)
{
    auto u = t->bob();
    u->something();   // Since this is not object/variant it is forced to
dynamic
    return u;	// Doesn't work
}

Therefore we could add a rule that anything returned in a template from a dynamic call that is being passed to a template variable type is cast to that type (I don't like to just throw casts about, but seems to me that its in the same league as dynamic calls). So the below can happen instead.

U myfunc(T,U)(T t)
{
    U u = t.bob();
    u.something();
    return u;
}
B b = myfunc?(A,B)(new A()); // becomes
B myfunc(A t)
{
    B u = cast(B) t->bob();	// Here is the dynamic aspect of the template
    u.something();	// We are using B not Object so we can go static in our
call
    return u;
}

You could argue the above starts making templates more complicated, however I think these are already the sort of things you have to bare in mind with templates and dynamic types anyway.

Do that solve the problem?

p.s. I can see an issue with other operators turning dynamic in templates if you convert 'a = a + a;' to 'a = a.opAdd(a);' and don't implement opAdd in the type passed into dynamic template call, but that depends how you process the template call ..  I don't know :-). I would avoid letting such a thing happen (process the + to opAdd after its done a dynamic pass) otherwise you add a rule that operators can be dynamic in templates but not normal code which is confusing.

April 19, 2009
Denis Koroskin wrote:

> On Sun, 19 Apr 2009 05:40:32 +0400, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> 
>> On Sat, 18 Apr 2009 21:10:27 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
>>
>>> Adam Burton wrote:
>>>> Andrei Alexandrescu wrote:
>>>>>> What about using something like '->' for dynamic calls instead of '.'?
>>>>> That's absolutely useless. If I have to write anything different from "." I might as well write "bloodyMaryBloodyMaryBloodyMary".
>>>>>
>>>>> Andrei
>>>> You could even write 'noodles' but that doesn't really give me a reason as to why it's absolutely useless. Please clarify, I thought it seemed like a reasonable idea, if it isn't I would like to know why.
>>>
>>> I apologize for the snapping. There's no excuse really, but let me mention that this thread has been particularly meandering.
>>>
>>> The point of using "." is not syntactic convenience as much as the ability of the Dynamic structure to work out of the box with algorithms that use the standard notation.
>>
>> Hm... the thought just occurred to me.
>>
>> At what time are you going to use opDotExp so an entity be used in an algorithm rather than actually defining the functions directly?  For example, if you want to make a class/struct a range, why not just define the functions directly?  It seems odd to define them using opDotExp.
>>
I agree, seems to me templates attempt to acheive the same as dynamic calls but in a static way, so expecting a template handle functions that are not there for the most part contradicts its very nature. See my reply to BCS as a potential way around this by extending templates.
> 
> Variant variantRange = someRange();
> foreach (element; variantRange) {
>     // ...
> }
> 
> Variant forwards all the front/back/etc methods to an underlying range.
See my reply to BCS as a potential way around this by extending templates.

April 19, 2009
On 2009-04-18 22:21:50 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

> I did, but sorry, it doesn't make sense and does nothing but continue the terrible confusion going in this thread.

Then let's try to remove some of that confusion.


> You say: well if opDot were a template then a scripting language can't get to it because what it has is a dynamic string. Yes. That's why opDot does whatever static string processing and then forwards, if the design asks for it, to the dynamic function. opDot's participation is solely a syntax helper within D.

Yes, I understand that template opDot as you proposed is just a syntax helper.

I'm arguing that many uses proposed for this syntax helper are better implemented by mixins (because functions added by mixin are accessible using reflection, while functions added by a templated syntax helper are not). Of the remaining uses that can't be served by mixins, most need runtime dispatch, which can be made compatible with a runtime-dispatch system if there is a standard name for a non-template catch-all function (as opposed to a user-defined name you can only know from within a template opDot). And the remaining ones that require a template opDot are not really useful (case-insentiveness?).


That was the summary. Following is the detailed explanation. Now, Andrei, if you think something doesn't make any sense in all this, please tell me where it stops making sense so I can understand were we're disagreeing.


Let's consider how runtime reflection works. Basically, you have a list of available functions for a given type, and their function pointers. When you want to invoke a function using runtime reflection, you find it in the list, and call the pointer. Something like this (simplified) function:

	void invokeViaReflection(string name)
	{
		void function()* fptr = name in functionList;
		if (fptr)
			ftpr();
		else
			throw Exception("Function "~ name ~" not found.");
	}

(Note that you can already this kind of reflection by building the list at compile time for the types you want with something like ClassInfoEx, but this could also be built into the compiler.)

So if you have an external script that wants to call a D function, or you receive a remote invocation via network, the script will call that invokeViaReflection function above which will call the right function.

Now, let's say we want to add the ability for a D class to act as a proxy for another object, then we need to catch calls to undefined functions and do something with them (most likely forward them to someone else). For that to be reflected to the script, we'll have to modify the invokeViaReflection like this:

	void invokeViaReflection(string name)
	{
		void function()* fptr = name in functionList;
		if (fptr)
			ftpr();
		else
		{
			void function(string name)* catchAll = "catchAllHandlerFunc" in functionList;
			if (catchAll)
				catchAll(name);
			else
				throw Exception("Function "~ name ~" not found.");
		}
	}

The thing is that the name of that "catchAllHandlerFunc" function needs to be standardised for it to work with runtime reflection. If the standardized way of doing this is to implement things in a template and from that template call a catch-all function with a user-defined, unpredictable name, then we can't use the catch-all function at all with runtime reflection since the name of that runtime catch-all function is burried in a template.

This was my first point. I hope it makes sense and that we agree about the conclusion: there needs to be a standardized name for a non-template catch-all function if we want runtime reflection to reflect the compile-time dispatching process.

(By the way this, in itself doesn't preclude having a template catch-all too, only if there is one the runtime one should be standardized too.)


Now, for my second point, let's consider some use cases for a catch-all mecanism.

Let's look at the swizzle case. You want a class or a struct with a function for every permutation of "xwyz". Beside doing that manually, here's what you can do:

Solution A: use a mixin. This will add 256 functions to your struct, and those functions will appear in the functionList used by invokeViaReflection.

Solution B.1: use a templated catch-all function, and use the name to instanciate the catch-all. Calling the function will work at compile time, but at runtime invokeViaReflection is left in the dust.

Solution B.2: in addition to the templated catch-all function in B.1, add a runtime catch-all. This is about twice the work, but invokeViaReflection can work with those functions.

So of all the solutions above, B.2 is twice the work and adds the risk of the two implementations becoming out of sync, and is probably slower than A when called through invokeViaReflection. B.1 breaks invokeViaReflection. And A works for everything. My conclusion is that solution A is preferable. And I think the language should encourage the use of solution A in this case.


Now, let's look at the proxy case, where you want to forward function calls to another object. In fact, there are two variant of this case: one where you know the type at compile-time, one where you don't (you only know the base class) but still want to forward all calls.

Solution A: use a mixin. The mixin will create a wrapper function for all functions in the wrapped object it can find using compile-time reflection, and those functions will appear in the functionList used by invokeViaReflection.

Solution B.1: use a templated catch-all function which will simply issue a call on the wrapped object. Calling the function will work at compile time, but at runtime invokeViaReflection is left in the dust if we try to use it on our proxy.

Solution B.2: in addition to the templated catch-all function in B.2, add a runtime catch-all that will check the runtime string against the function names obtained at compile time and call the right function. This is more than twice the work, but invokeViaReflection can call functions on our proxy.

Solution C: create a runtime catch-all function that will use invokeViaReflection to call the right function on the wrapped object.

Note that solution C is the only option if you don't know the exact type at compile-time, like when you only know the base class. Unfortunately, solution C for our proxy won't work if the wrapped object uses B.1. Same for solution A, which relies on compile-time reflection.

So that's why I think B.1 (templated catch-all function) should be avoided whenever a mixin solution is possible. In the two use cases above, you can use a mixin and get the same "staticness" as in B, but the mixin solution also offer support for reflection, which makes it superior for the cases you're stuck using the object at runtime, or when we want to introspect at compile-time. And when a mixin doesn't cut it, the template will most likely just be a stub for calling a runtime mecanism.


Note that I'm not that much against a templated opDot, I just want to point out its drawbacks for reflection. I'm also unconvinced of its usefulness considering you can do the same things better using mixins leaving only the runtime case to be solved, where you are better served without a template anyway.

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

April 19, 2009
Adam Burton wrote:

> BCS wrote:
> 
>> Hello Adam,
>> 
>>> On Sat, Apr 18, 2009 at 06:10:27PM -0700, Andrei Alexandrescu wrote:
>>> 
>>>> The point of using "." is not syntactic convenience as much as the ability of the Dynamic structure to work out of the box with algorithms that use the standard notation.
>>>> 
>>> What if the dot remained exactly like it is now and the -> took
>>> the place of the dot in the proposal; regular method calls when
>>> possible and forwarded to opExtension (opDotExp needs a better name)
>>> when that fails?
>>> Thus your generic algorithm can use -> and work in all cases, and the
>>> dot operator remains the same as it is now.
>> 
>> Going the other way would be better; '.' works as Andrei wants and '->' is an explicit, "don't use the dynamic stuff" invocation. If it went the other way virtually all template code would end up needing to use '->' for everything just in cases someone wants to pass in a type that uses opDotExp.
> Yea and that would be bad, since then as far as I am concerned you have destroyed the purpose of templates. Seems to me templates and dynamic calls are sorta there to solve the same problem, how do you write code that is independent of type? Dynamic takes it to extreme and makes the evaluation of methods (or public types variables I don't see how that would be different) and leave it all to runtime which is far more flexible but potentially has holes if the type doesn't meet the requirements. Templates take a more conservative route by still letting you write independent of type but then using the information provided at call time it can still perform static code checks to make sure the type meets its needs, making it a little more safe than dynamic. However soon as you throw some dynamic calls into a template the guarantees usually provided go out the window since the template is letting that none existent member slip by. Consider below, why not just use a completely dynamic function as at least then you know what you are letting yourself in for?:
> 
> void function(T)(T i)
> {
>    i.bob();
> }
> 
> void function(Object i)
> {
>    i.bob();
> }
> 
> In the above both functions do exactly the same thing, only difference is with the template sometimes the call is static sometimes and other times it isn't. All you are saving yourself is maybe a performance improvement (which I admit I would probably use the template for just that reason)? However you've lost the compile-time checking of templates and still add the possibility of typos or bad method names leaking in (consider in my previous example if I removed the open method from ServerProxy, with opDotExp it would take over control of the call to 'open' if I happen to forget to remove the call from somewhere).
> 
> However then I suppose then you are still screwing over the half and half people by not using dynamic calls in templates.
> 
> T function(T)(T i)
> {
>    i.bob();
>    return i;
> }
> 
> Variant function(Variant i)
> {
>    i.bob();
>    return i;
> }
> 
> Using the dynamic method the user is forced to perform a cast where as you don't with the template version.
> 
> Ok, proposal draft 2 :-P (bare with me this is mostly a mind dump so I apologize for if I may seem to ramble).
> 
> So what I said previously still stands. StaticAsMuchAsPoss lives with best of both worlds and therefore if I tomorrow decided to remove the method "open" it would fail with a compile-time error where as DynamicAsMuchAsPoss leaves it till run-time. Object/Variant, or whatever opArrow returns, would implement default behaviour (and opArrow would be abstract) of searching the type for implementations of the provided function name (or it could be a public variable name) and if such a thing exists it executes it else throws a runtime error. This allows what you see in DynamicAsMuchAsPoss and AlsoShowBypassNeedToUpcast to be possible, as well as code like something-
>>bob(10)->nono(11) without worrying about casts (if nono was implemented as
> an actual function, so we live dynamically). So we get sort of below.
> 
> class Object
> {
>    .... usual ....
>    Object opArrow(char[] name, v...)
>    {
>        if (this.publicmethodexists(name))
>           return invoke(name, this, v);
>        else if (this.publicvariableexists(name))
>           return invoke(name, this);
>        else
>           throw NoSuchMemberException(name, v);
>    }
>    .... more usual ....
> }
> 
> class A
> {
>     void bob() {}
>     void opArrow(char[] name, v ...)
>     {
>         if (name == "cheese")
>            dosomething();
>         else
>            super.opArrow(name, v);
>     }
> }
> 
> void main()
> {
>     A a = new A();
>     a.bob();	// Compile time valid
>     a.cheese();	// compile time invalid
>     a->cheese();	// runtime valid
>     a->bob();          // runtime valid
>     a.nono();	// Compile time invalid
>     a->nono();	// Runtime invalid
> 
>     Object obj = a;
>     obj.bob();	// Compile time invalid
>     obj.cheese();	// compile time invalid
>     obj->cheese();	// runtime valid
>     obj->bob();        // runtime valid
>     obj.nono();	// Compile time invalid
>     obj->nono();	// Runtime invalid
> }
> 
> As for templates then how about a dynamic template call that does 1 or 2 passes through the code to prepare it to be dynamic? How about we replace the '!' with a '?' for the dynamic call, that way we can have strict templates (!) and dynamic temapltes (?). See below using the previously discussed template.
> 
> T myfunc(T i)
> {
>     i.bob();
>     return i;
> }
> 
> class MyStrictClass
> {
>     void bob() {}
> }
> 
> MyStrictClass c = myfunc!(MyStrictClass)(new MyStrictClass());	// strict
> call converts to below (default behaviour where i am not verbose with !()
> )
> 
> MyStrictClass myfunc(MyStrictClass i)
> {
>     i.bob();
>     return i;
> }
> 
> 
> class MyDynamicClass
> {
>     void opArrow(char[] name, v ...)
>     {
>         if (name == "bob")
>           something;
>         else
>            super.opArrow(name, v);
>     }
> }
> 
> MyDynamicClass c = myfunc?(MyDynamicClass)(new MyDynamicClass());	//
> dynamic call converts to below
> 
> // It goes through and each . call is converted to a dynamic call
> MyDynamicClass myfunc(MyDynamicClass i)
> {
>     i->bob();
>     return i;
> }
> 
> The above becomes completely dynamic. However that could make it all slow due to dynamic calls everywhere which in some cases may be unnecessary, think about below
> 
> T myfunc(T i)
> {
>     i.bob();
>     i.nono();
>     return i;
> }
> 
> class MyStrictClass
> {
>     void bob() {}
>     void nono() {}
> }
> 
> class MyDynamicClass
> {
>     void bob() {}
>     void opArrow(char[] name, v ...)
>     {
>         if (name == "nono")
>           something;
>         else
>            super.opArrow(name, v);
>     }
> }
> 
> // static myfunc!(MyStrictClass)
> MyStrictClass myfunc(MyStrictClass i)
> {
>     i.bob();
>     i.nono();
>     return i;
> }
> 
> // dynamic myfunc?(MyDynamicClass)
> MyDynamicClass myfunc(MyDynamicClass i)
> {
>     i->bob();	// This is unnecessary
>     i->nono();
>     return i;
> }
> 
> // dynamic alternative myfunc?(MyDynamicClass)
> MyDynamicClass myfunc(MyDynamicClass i)
> {
>     i.bob();	// Was identified as being there so kept as static
>     i->nono();	// Wasn't found in type definition so made dynamic
>     return i;
> }
> 
> So now we get the template attempting to use the best of both worlds and only giving in to being dynamic where necessary, it means that in some cases your dynamic templates would not need to be dynamic at all.
> 
> The next place I see some potential issue is return types of methods call from dynamic inside the template where it may be again unnecessarily using dynamic.
> 
> T myfunc(T,U)(T t)
> {
>     U u = t.bob();
>     u.something();
>     return t;
> }
> 
> class A
> {
>    Object opArrow(char[] name, v ...)
>    {
>        if (name == "bob")
>          return call("bob", v);
>        else
>          return super.opArrow(name, v);
>    }
> }
> 
> class B
> {
>    void something() {}
> }
> 
> A a = myfunc?(A,B)(new A()); // becomes
> A myfunc(A t)
> {
>     B u = t->bob();	// Compile time error, not variant/object
>     u.something();
>     return t;
> }
> 
> You could get around above by replacing U with auto
> 
> T myfunc(T)(T t)
> {
>     auto u = t.bob();
>     u.something();
>     return t;
> }
> A a = myfunc?(A)(new A()); // becomes
> A myfunc(A t)
> {
>     auto u = t->bob();	// Gets return as object so from here on it starts
>     to
> unfold that the rest of the template is dynamic
>     u->something();   // Since this is not object/variant it is forced to
> dynamic
>     return t;
> }
> 
> However that doesn't fix below.
> 
> U myfunc(T,U)(T t)
> {
>     auto u = t.bob();
>     u.something();
>     return u;
> }
> B b = myfunc?(A,B)(new A()); // becomes
> B myfunc(A t)
> {
>     auto u = t->bob();
>     u->something();   // Since this is not object/variant it is forced to
> dynamic
>     return u;	// Doesn't work
> }
> 
> Therefore we could add a rule that anything returned in a template from a dynamic call that is being passed to a template variable type is cast to that type (I don't like to just throw casts about, but seems to me that its in the same league as dynamic calls). So the below can happen instead.
> 
> U myfunc(T,U)(T t)
> {
>     U u = t.bob();
>     u.something();
>     return u;
> }
> B b = myfunc?(A,B)(new A()); // becomes
> B myfunc(A t)
> {
>     B u = cast(B) t->bob();	// Here is the dynamic aspect of the template
>     u.something();	// We are using B not Object so we can go static in our
> call
>     return u;
> }
> 
> You could argue the above starts making templates more complicated, however I think these are already the sort of things you have to bare in mind with templates and dynamic types anyway.
> 
> Do that solve the problem?
> 
> p.s. I can see an issue with other operators turning dynamic in templates
> if you convert 'a = a + a;' to 'a = a.opAdd(a);' and don't implement opAdd
> in the type passed into dynamic template call, but that depends how you
> process
> the template call ..  I don't know :-). I would avoid letting such a thing
> happen (process the + to opAdd after its done a dynamic pass) otherwise
> you add a rule that operators can be dynamic in templates but not normal
> code which is confusing.

Just thought as well, in a dynamic template call you would probably disable certain kinds of template constraints. Consider below wouldn't work for a dynamic call without disabling the interface constraint.

interface A
{
   void bob();
}

class B
{
    void opArrow(char[] name, v...)
    {
        if (name == "bob")
             something;
        else
            super.opArrow(name, v);
    }
}

void function(T : A)(T i)
{
    i.bob();
}

function?(B)(new B());	// Won't work, B does not meet criteria

On another note:

function?(new B());	// Potential short hand using type infurance(sp?)??
although if you replace '?' with '!' then the short hand becomes ambiguous
but I suppose since I say strict should be default then short hand for
strict isn't necessary

April 19, 2009
On Sun, 19 Apr 2009 02:00:50 -0400, Don <nospam@nospam.com> wrote:

> Steven Schveighoffer wrote:
>>  Yeah, I get that it can be done manually.  What I'm suggesting is that the compiler makes sure the static assert occurs if thbe result of compiling the template instance results in an empty function.  I look at it like adding methods to a class, you don't have to define which methods are not valid, all methods are by default invalid, only the ones you define are allowed.  With opDotExp, you are forced to define not only which calls to it are valid, but which ones aren't.  I'm saying, don't require defining which ones aren't, just like if you wanted to add methods to a class.  I'm sure I'm not explaining this perfectly...
>>  For example, the swizzle example that has been brought up many times, you want to handle 256 possible method names, but there are an infinite number of method names.  You don't care about defining what to do in all situations, only in the situations you care about.  But without compiler help, you have to.
>>  -Steve
>
> You'll get a "missing return statement", except in the case where it's void. So it's only that one case where it's a problem.

I thought that the point of the auto return is that the dynamic function could return anything...

-Steve
April 19, 2009
On Sun, 19 Apr 2009 06:26:57 -0400, Denis Koroskin <2korden@gmail.com> wrote:

> On Sun, 19 Apr 2009 05:40:32 +0400, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>
>> On Sat, 18 Apr 2009 21:10:27 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
>>
>>> Adam Burton wrote:
>>>> Andrei Alexandrescu wrote:
>>>>>> What about using something like '->' for dynamic calls instead of '.'?
>>>>> That's absolutely useless. If I have to write anything different from
>>>>> "." I might as well write "bloodyMaryBloodyMaryBloodyMary".
>>>>>
>>>>> Andrei
>>>> You could even write 'noodles' but that doesn't really give me a reason as to why it's absolutely useless. Please clarify, I thought it seemed like a reasonable idea, if it isn't I would like to know why.
>>>
>>> I apologize for the snapping. There's no excuse really, but let me mention that this thread has been particularly meandering.
>>>
>>> The point of using "." is not syntactic convenience as much as the ability of the Dynamic structure to work out of the box with algorithms that use the standard notation.
>>
>> Hm... the thought just occurred to me.
>>
>> At what time are you going to use opDotExp so an entity be used in an algorithm rather than actually defining the functions directly?  For example, if you want to make a class/struct a range, why not just define the functions directly?  It seems odd to define them using opDotExp.
>>
>
> Variant variantRange = someRange();
> foreach (element; variantRange) {
>     // ...
> }
>
> Variant forwards all the front/back/etc methods to an underlying range.

Doesn't the current opDot solution do this?  Forwarding all calls to a certain member is not a really compelling argument for changing opDot to allow dynamic method names.

-Steve
April 19, 2009
On Sun, 19 Apr 2009 10:26:11 -0400, Steven Schveighoffer <schveiguy@yahoo.com> wrote:

> On Sun, 19 Apr 2009 06:26:57 -0400, Denis Koroskin <2korden@gmail.com> wrote:
>
>> On Sun, 19 Apr 2009 05:40:32 +0400, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>>
>>> On Sat, 18 Apr 2009 21:10:27 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
>>>
>>>> Adam Burton wrote:
>>>>> Andrei Alexandrescu wrote:
>>>>>>> What about using something like '->' for dynamic calls instead of '.'?
>>>>>> That's absolutely useless. If I have to write anything different from
>>>>>> "." I might as well write "bloodyMaryBloodyMaryBloodyMary".
>>>>>>
>>>>>> Andrei
>>>>> You could even write 'noodles' but that doesn't really give me a reason as to why it's absolutely useless. Please clarify, I thought it seemed like a reasonable idea, if it isn't I would like to know why.
>>>>
>>>> I apologize for the snapping. There's no excuse really, but let me mention that this thread has been particularly meandering.
>>>>
>>>> The point of using "." is not syntactic convenience as much as the ability of the Dynamic structure to work out of the box with algorithms that use the standard notation.
>>>
>>> Hm... the thought just occurred to me.
>>>
>>> At what time are you going to use opDotExp so an entity be used in an algorithm rather than actually defining the functions directly?  For example, if you want to make a class/struct a range, why not just define the functions directly?  It seems odd to define them using opDotExp.
>>>
>>
>> Variant variantRange = someRange();
>> foreach (element; variantRange) {
>>     // ...
>> }
>>
>> Variant forwards all the front/back/etc methods to an underlying range.
>
> Doesn't the current opDot solution do this?  Forwarding all calls to a certain member is not a really compelling argument for changing opDot to allow dynamic method names.

Not to mention, how does the compiler know that it should use range-style foreach instead of opApply-style foreach...

In fact, I think any object that uses opDotExp to define the foreachable methods might be unusable in foreach.

-Steve
April 19, 2009
On Sun, 19 Apr 2009 18:26:11 +0400, Steven Schveighoffer <schveiguy@yahoo.com> wrote:

> On Sun, 19 Apr 2009 06:26:57 -0400, Denis Koroskin <2korden@gmail.com> wrote:
>
>> On Sun, 19 Apr 2009 05:40:32 +0400, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>>
>>> On Sat, 18 Apr 2009 21:10:27 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
>>>
>>>> Adam Burton wrote:
>>>>> Andrei Alexandrescu wrote:
>>>>>>> What about using something like '->' for dynamic calls instead of '.'?
>>>>>> That's absolutely useless. If I have to write anything different from
>>>>>> "." I might as well write "bloodyMaryBloodyMaryBloodyMary".
>>>>>>
>>>>>> Andrei
>>>>> You could even write 'noodles' but that doesn't really give me a reason as to why it's absolutely useless. Please clarify, I thought it seemed like a reasonable idea, if it isn't I would like to know why.
>>>>
>>>> I apologize for the snapping. There's no excuse really, but let me mention that this thread has been particularly meandering.
>>>>
>>>> The point of using "." is not syntactic convenience as much as the ability of the Dynamic structure to work out of the box with algorithms that use the standard notation.
>>>
>>> Hm... the thought just occurred to me.
>>>
>>> At what time are you going to use opDotExp so an entity be used in an algorithm rather than actually defining the functions directly?  For example, if you want to make a class/struct a range, why not just define the functions directly?  It seems odd to define them using opDotExp.
>>>
>>
>> Variant variantRange = someRange();
>> foreach (element; variantRange) {
>>     // ...
>> }
>>
>> Variant forwards all the front/back/etc methods to an underlying range.
>
> Doesn't the current opDot solution do this?  Forwarding all calls to a certain member is not a really compelling argument for changing opDot to allow dynamic method names.
>
> -Steve

opDot is reprecated (use alias this instead) and will be eventually removed.

But "alias this" is quite unsafe because it exposes the inner data (which is supposed to be hidden in some case, think of reference counting wrapper - you never want to give raw access to an underlying data).

Besides, there are is a use-cases that you can't implement in a way other than opDotExp. For example, add some logging or profiling:

struct Profile(T)
{
   private T inner;

   auto opDotExp(string name, T...)(T args)
   {
       profiler.enter(name);
       scope(exit) profiler.leave();

       return <forward a call to 'inner' - how one would do this?>(args);
   }
}

April 19, 2009
Michel Fortin wrote:
> The thing is that the name of that "catchAllHandlerFunc" function needs to be standardised for it to work with runtime reflection.

I agree with this wholeheartedly.

However, opDotExp would be hamstringed if it were made to serve this function.

Since classes can implement interfaces, you could expose an interface IDispatch or the like. This doesn't suffice for structs, though.

> Let's look at the swizzle case. You want a class or a struct with a function for every permutation of "xwyz". Beside doing that manually, here's what you can do:
> 
> Solution A: use a mixin. This will add 256 functions to your struct, and those functions will appear in the functionList used by invokeViaReflection.
> 
> Solution B.1: use a templated catch-all function, and use the name to instanciate the catch-all. Calling the function will work at compile time, but at runtime invokeViaReflection is left in the dust.
> 
> Solution B.2: in addition to the templated catch-all function in B.1, add a runtime catch-all. This is about twice the work, but invokeViaReflection can work with those functions.

The standard form will be something like:
class Dispatch : IDispatch
{
   auto opDotExp(string name)(...) { return dispatch(name, _arguments, _argptr); }
   auto dispatch(string name, ...)
   {
      // do stuff
   }
}

This isn't much added work.

> Now, let's look at the proxy case, where you want to forward function calls to another object. In fact, there are two variant of this case: one where you know the type at compile-time, one where you don't (you only know the base class) but still want to forward all calls.
> 
> Solution A: use a mixin. The mixin will create a wrapper function for all functions in the wrapped object it can find using compile-time reflection, and those functions will appear in the functionList used by invokeViaReflection.

That's a lot of work, but it can get you a performance increase in the case where you know something about the class at compile time (for instance, you have an object that implements a particular interface, but you want to call non-interface functions via the wrapper).

Of course, you could manually forward methods as well and get the same performance increase. But that would be more difficult to write.

> Note that I'm not that much against a templated opDot, I just want to point out its drawbacks for reflection. I'm also unconvinced of its usefulness considering you can do the same things better using mixins leaving only the runtime case to be solved, where you are better served without a template anyway.

Well, you can do the same things with some other advantages with mixins, but it's harder to write.

I think the appropriate solution is to add a solver to the compiler that can determine valid inputs for opDotExp and, if that set is below a threshold, unroll the template into regular member functions.

Or, you know, just implement a static foreach that you can use in templates. And anything else that we can think of that's reasonable to make the mixin solution easier to work with.

If that's accomplished, there'd be no reason to have opDotExp as a template.
April 19, 2009
Michel Fortin wrote:
> On 2009-04-18 22:21:50 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:
> 
>> I did, but sorry, it doesn't make sense and does nothing but continue the terrible confusion going in this thread.
> 
> Then let's try to remove some of that confusion.

Thanks for doing so. Given that my wits are spent with regard to this thread, I am replying out of respect for the considerable effort you have put in writing an explanation.

I agree that method names swallowed by opDotExp will not be exposed to any reflection mechanism and that that can be a drawback. But sometimes you don't even want to exhaustively generate all method names. In the Pascalize example, you wouldn't want to generate all case combinations of your methods. Also in the swizzle example, maybe you don't want to compulsively expose all of the xyzw combinations.

You completely lost me about the necessity of a standardized catch-all function. My view is that if you want to forward to someone else, you just call the runtime invoke() for the guy you want to forward to. So I see exactly zero need for a standardized catch-all, and a red herring in the discussion about the syntactic help brought about by opDotExp. Given the size of your latest effort to explain things, I guess it would be out of place for me to ask for further explanation.

Also, when a thread becomes an exchange between two people, it's a clear sign that that thread should be put to rest. I'm still a bit dumbfounded that such a minor issue has caused so much aggravation, but I guess odder things have happened on the Usenet.


Andrei