Fully dynamic d by opDotExp overloading

Denis Koroskin 2korden at gmail.com
Sat Apr 18 13:45:53 PDT 2009


On Sat, 18 Apr 2009 21:43:15 +0400, Steven Schveighoffer <schveiguy at yahoo.com> wrote:

> On Fri, 17 Apr 2009 23:43:22 -0400, Steven Schveighoffer  
> <schveiguy at yahoo.com> wrote:
>
>> On Fri, 17 Apr 2009 21:54:52 -0400, Steven Schveighoffer  
>> <schveiguy at yahoo.com> wrote:
>>
>>> Andrei wrote:
>>>> We are discussing a language extension. That language extension will  
>>>> allow a type to choose flexibility in defining methods dynamically,  
>>>> while being otherwise integrated syntactically with the current  
>>>> values. This has advantages, but also alters the expectations.
>>>
>>> As long as it identifies what can be dynamic and what cannot.  I can't  
>>> imagine Walter will go for this with his strict view of hijacking.
>>
>> Let me add that if there was a way for syntax to easily allow for  
>> unintentional calls to be translated to compile-time errors, I think  
>> this would be a workable solution.
>>
>> For example, I don't have any problem with your Pascalize example,  
>> because you have not removed any static typing from the code (i.e. no  
>> unexpected noops or exceptions are built in).  If there were some way  
>> to enforce this, then I think it would be a usable idea.  For instance,  
>> if you only allow CTFE to specify a function that is called when  
>> certain strings are passed in, I don't have a problem with that,  
>> because you are simply dispatching the data to strongly typed functions  
>> at compile time, which provide compile-time errors when you mess up.
>
> I gave this a lot of thought, and I think here is a possible solution:
>
> the main reason I'm hesitant on this idea is because of code like this:
>
> class X
> {
>    auto opDotExp(string fname, T...)(T args)
>    {
>       if(fname == "blah")
>         return foo(args);
>       else if(fname == "blither")
>         return bar(args);
>       // else, nothing happens
>    }
> }
>
> Which leaves code open to lots of compiled code that doesn't do the  
> right thing (or throws some runtime exception).  What would be nice is  
> if the default behavior is what statically bound functions do, that is,  
> compile error, and only let the cases be handled which the author  
> expects to handle.
>
> For that, I think if we make the following rule, we will see much less  
> code that is poorly written, and I think dynamic functions will be  
> feasible:
>
> If the compiler can determine during compilation of an opDotExp instance  
> that the resulting function is empty, then it is a compiler error, just  
> like if you tried to call a function that doesn't exist.  This behavior  
> can be overridden by putting a return statement in an otherwise empty  
> function.
>
> So for example, the above can be statically determined to compile to  
> nothing if fname is not "blah" or "blither", and therefore would be a  
> compiler error.  Of course, if you call functions that are not  
> statically evaluable, then you are back to the danger of truly dynamic  
> bindings, but that would make sense for things that cannot be evaluated  
> at compile time.
>
> What do you think?
>
> -Steve

Here is an example of a more sophisticated opDotExp use case that relies on its templated version:

A Wrapper struct is a simple wrapper around any arbitrary data. It fully encapsulates the underlying object and never gives it away.
A simple example is a reference-counter. You want this object to allow any operations on it, but you want to disallow raw object access.
"alias this" is very unsafe in this respect.

struct Wrapper(T)
{
   private T t;

   this(ref T obj) {
       _obj = obj; // capture. I believe there must be a better way to transfer ownership, but I don't know how
   }

   auto opDotExp(string fname, T...)(T args)
   {
       return t.opDotExp!(fname, T)(args); // how do you forward a call? Do all types have an implicit opDotExp method, i.e. for any given type T, T.bar == T.opDotExp("bar")?
   }
}

class Foo
{
    int bar() { return 42; }
    void baz(int n) { ... }
    // ...
}

auto o = Wrapper(new Foo());
int x = o.bar();
o.baz = -1;

Foo f = o; // disallowed!

It works by referring all the methods to an underlying object. You'll get a compile-time error if it doesn't have such method or property:

o.call(); // Error: no such method

That's a great functionality, and you can't do it if opDotExp was a runtime method.





More information about the Digitalmars-d mailing list