dynamic classes and duck typing

Steven Schveighoffer schveiguy at yahoo.com
Tue Dec 1 08:02:27 PST 2009


On Tue, 01 Dec 2009 10:25:43 -0500, Denis Koroskin <2korden at gmail.com>  
wrote:

> On Tue, 01 Dec 2009 17:12:38 +0300, Steven Schveighoffer  
> <schveiguy at yahoo.com> wrote:
>
>> On Tue, 01 Dec 2009 08:49:58 -0500, Denis Koroskin <2korden at gmail.com>  
>> wrote:
>>
>>> On Tue, 01 Dec 2009 16:46:25 +0300, Steven Schveighoffer  
>>> <schveiguy at yahoo.com> wrote:
>>>
>>>> On Mon, 30 Nov 2009 23:32:21 -0500, Bill Baxter <wbaxter at gmail.com>  
>>>> wrote:
>>>>
>>>>> On Mon, Nov 30, 2009 at 7:12 PM, Walter Bright
>>>>> <newshound1 at digitalmars.com> wrote:
>>>>>> Bill Baxter wrote:
>>>>>>>
>>>>>>> So we can overload on @property-ness?
>>>>>>
>>>>>> No.
>>>>>>
>>>>>>> I.e. this works
>>>>>>>
>>>>>>> struct S
>>>>>>> {
>>>>>>> @property
>>>>>>> float x() { return 1.0f; }
>>>>>>> float x() { return 2.0f; }
>>>>>>> }
>>>>>>>
>>>>>>> void main()
>>>>>>> {
>>>>>>>    S  s;
>>>>>>>    writefln("%s", s.x); // writes 1.0
>>>>>>>    writefln("%s", s.x()); // writes 2.0
>>>>>>> }
>>>>>>
>>>>>> That just looks wrong.
>>>>>>
>>>>>
>>>>> Ok, so you can't have both dynamic properties and dynamic methods  
>>>>> with
>>>>> this.  One or the other, your pick.
>>>>> Seems like an unfortunate limitation.
>>>>
>>>>
>>>> what a minute, can't you use template conditionals to distinguish?   
>>>> i.e. I would expect this to work:
>>>>
>>>> struct S
>>>> {
>>>>    @property float opDispatch(string s)() if (s == "x") {return 1.0f;}
>>>>    float opDispatch(string s)() { return 2.0f;}
>>>> }
>>>>
>>>> void main()
>>>> {
>>>>    S s;
>>>>    writefln("%s", s.x); // 1.0
>>>>    writefln("%s", s.y()); // 2.0
>>>> }
>>>>
>>>> Overloading opDispatch based on the called symbol name should always  
>>>> be possible, and overloading on parameter types is always possible.
>>>>
>>>> -Steve
>>>
>>> What if you don't know argument names a-priori? Consider a generic  
>>> Dynamic class that has nothing but a single opDispatch method.
>>
>> although opDispatch allows some dynamic function definitions, the  
>> *usage* of opDispatch is always static.  The question is, if you are  
>> for example wrapping another type, can you introspect the attributes of  
>> its methods?
>>
>> For example, I'd expect something like this should be possible in the  
>> future:
>>
>> struct Wrapper(T)
>> {
>>     T t;
>>     @property auto opDispatch(string s)() if(isProperty!T(s) )  
>> {mixin("return t." ~ s ~ ";");} // getters
>>     @property auto opDispatch(string s, A)(A arg) if(isProperty!T(s) )  
>> {mixin("return (t." ~ s ~ " = arg);"); } // setters
>>     auto opDispatch(string s, A...)(A args) { mixin("return t." ~ s ~  
>> "(args);");}
>> }
>>
>> Now, given the function attributes that are possible (this does not  
>> include const and immutable, which are overloaded via parameter types),  
>> this is going to get pretty ugly quickly.  Unfortunately, the  
>> attributes are not decided by the caller, but by the callee, so you  
>> have to use template conditionals.  It would be nice if there was a way  
>> to say "copy the attributes from function x" when defining template  
>> functions in a way that doesn't involve conditionals, but even then,  
>> you would have a hard time defining such usage because you don't know  
>> what function you want until you evaluate the template string.
>>
>> -Steve
>
> I might work with your design, but it will lead to considerable code  
> bloat, and it's not that static after all.
> I'd say that you could achieve the same with method forwarding using  
> alias this:
>
> struct Wrapper(T)
> {
>      T t;
>      alias this t;
> }
>
> The true power of opDispatch comes with a fully Dynamic type, that has  
> no type information until runtime:
>
> void foo(Dynamic duck)
> {
>     duck.quack():
> }

You are missing the point of opDispatch.  It is not runtime defined,  
because the compiler statically decides to call opDispatch.  The dynamic  
part of opDispatch comes if you want to do something based on runtime  
values within the opDispatch function.  e.g. the compiler doesn't decide  
at *runtime* whether to call opDispatch or some normal function named  
quack, it's decided at compile time.  opDispatch could be completely  
compile-time defined since it is a template.  But the 'dynamicness' of it  
is basically no more dynamic than a normal function which does something  
based on runtime values.

Compare that to a dynamic language with which you can add methods to any  
object instance to make it different than another object, or make it  
conform to some interface.

My example is not a complete example BTW.  You can do much more than just  
dispatch to a sub-type, you can do other things that alias this cannot.   
For example, you could log each call to a function before calling the  
sub-type.  But there are probably even better ways to do that with  
mixins.  The real power of opDispatch comes when you don't want the  
default mapping of case-sensitive function name to implementation.

-Steve



More information about the Digitalmars-d mailing list