Fully dynamic d by opDotExp overloading

Adam Burton adz21c at googlemail.com
Sun Apr 19 07:17:22 PDT 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




More information about the Digitalmars-d mailing list