April 18, 2009
Andrei Alexandrescu Wrote:

> Nick Sabalausky wrote:
> > Please do not accuse me of such a thing simply because I haven't changed my opinion. You've held your ground as well, so I could just as easily accuse you of being closed-minded and merely reaffirming a your preconceived opinion. I have indeed listened to the arguments and responded to them.
> 
> Given my track record, I think it should come at no surprise that I'm not a fan of dynamic typing.
> 
> Andrei

I'm no fan of it either. I will be pissed if one day I'm using a D library and a function name typo becomes a runtime error. There is no program too short for me to introduce typos and bugs.

Even still, this dynamic stuff does not bother me much because it only gets turned on if the class designer wanted it. I assume that it'd be a corner of the language I could avoid. The use seems reasonable besides sone bicycle shed coloring, so I plan to sit quietly in the corner and await an outcome.

My only 2 cents is to use something other than dot for dynamic function invocation. Given how much I plan to use it, it's probably better for me to abstain.
April 18, 2009
Andrei Alexandrescu Wrote:

> Christopher Wright wrote:
> > Andrei Alexandrescu wrote:
> >> Yah, glad someone mentioned it :o). The best way is a blend - you can statically dispatch on some popular/heavily-used names, then rely on a hashtable lookup for dynamic stuff.
> >>
> >> Andrei
> > 
> > 
> > You suggest:
> > 
> > auto opDotExp(string name)(...)
> > {
> >     static if (name == "something")
> >     {
> >         code...
> >     }
> >     else
> >     {
> >         dynamic stuff
> >     }
> > }
> > 
> > That isn't very clear. Why not write it this way:
> > 
> > auto opDotExp(string name, ...)
> > {
> >     dynamic stuff
> > }
> > 
> > auto something (...)
> > {
> >     code...
> > }
> 
> It's a good question. opDotExp leaves more flexibility because it allows for a host of compile-time manipulations, e.g. decide to forward to a member etc. Also consider this (probably Nick will turn blue):
> 
> struct Pascalize(T)
> {
>      T m;
>      auto opDotExp(string name, T...)(T args)
>      {
>          return mixin("m."~tolower(name))(args);
>      }
> }
> 
> struct S { void foo() { ... } }
> 
> Pascalize!S s;
> s.foo(); // works
> s.Foo(); // works too
> s.fOo(); // yup, works again
> 
> 
> Andrei

I find that funny. I can already imagine a developer that adds the equivalent of autocorrection while they type because they make trivial typos all teh timr
April 18, 2009
On Fri, 17 Apr 2009 18:01:51 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> Nick Sabalausky wrote:
>> "Andrei Alexandrescu" <SeeWebsiteForEmail@erdani.org> wrote in message news:gsapl6$24ei$1@digitalmars.com...
>>> Steven Schveighoffer wrote:
>>>> Sure, how do you know that the class actively chose it, or did not actively choose it, or will *never* actively choose it simply by looking at the statement?
>>> You shouldn't worry about it as much as you shouldn't when you iterate a built-in array vs. a user-defined range.
>>>
>>> Would you like ranges that work very different from built-in arrays, and everybody to special-case around that?
>>>
>>  That's an inadequate comparison. We *can* make arrays and ranges usable in the same way. But opDotExp cannot make dynamic calls usable in the same way as static calls, because one of the rules of static method invokation is that trying to call a non-existant function results in a compile-time error. The best opDotExp can do it make dynamic calls *seem* the same which is deceptive.
>
> Au contraire, it's a very adequate comparison. We changed the language to support ranges/arrays uniformly. Here, I'll paste your argument with the appropriate changes:
>
> ====
> But globals acting as members cannot make arrays usable in the same way
> as user-defined types, because one of the rules of arrays is that trying to call a non-existant member function on an array results in a compile-time error. The best your rule can do it make nonmember calls *seem* the same which is deceptive.
> ====

Calling global functions as if they were array members does not subvert the type system.  It is not even close to the same level of danger that this is.

I'm all for expanding runtime introspection that remains within the type system, I'm even for adding some possibility to create dynamically dispatched functions, as long as those functions are called differently from normal functions.

I think you would agree that one of the main roles of the compiler is to prevent you from making mistakes before it even gets to runtime.  Without knowing which calls the compiler checked and which ones it didn't, I can't know where to spend time scrutinizing.

>> If you want static and dynamic calls to be really usable in the same way (like iterating over a range vs array), then there's only two possibilities:
>>  1. Make attempts to invokation a non-existant static function a runtime error (obviously a bad idea).
>> or
>> 2. Provide a *secondary* syntax to invoke a method that works for both static and dynamic. Such as through a reflection api:
>>  traits(new Foo()).invokeMethod("bar");
>
> If I want to write an algorithm that calls "bar" twice, it should be:
>
> void twix(T)(T value)
> {
>     value.bar();
>     value.bar();
> }
>
> NOT
>
> void twix(T)(T value)
> {
>      static if (isDynamicType!T)
>      {
>          value.invokeMethod("bar");
>          value.invokeMethod("bar");
>      }
>      else
>      {
>          value.bar();
>          value.bar();
>      }
> }
>
> Please at least acknowledge that you are in receipt of this argument.

I think Nick's point number 2 is that you would have to write the above as:

void twix(T)(T value)
{
      value.invokeMethod("bar");
      value.invokeMethod("bar");
}

which would work whether bar was dynamically or statically defined.

>
> 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.

-Steve
April 18, 2009
Hello Jason,

> I find that funny. I can already imagine a developer that adds the
> equivalent of autocorrection while they type because they make trivial
> typos all teh timr
> 

there is a daily WTF on that, but using URLs.


April 18, 2009
On Fri, Apr 17, 2009 at 9:47 PM, Jason House <jason.james.house@gmail.com> wrote:
>
> I find that funny. I can already imagine a developer that adds the equivalent of autocorrection while they type because they make trivial typos all teh timr
>

Method name resolution using fuzzy string matching.
April 18, 2009
Andrei Alexandrescu wrote:
> Christopher Wright wrote:
>> Andrei Alexandrescu wrote:
>>> Cool! I suggest the rewrite:
>>>
>>> c.unknownmethod(args) -> c.opDotExp!("unknownmethod")(args)
>>>
>>> That way you have the option of handling the method name statically or dynamically.
>>
>> How would that allow you to handle the method name dynamically, if you're passing it as a template argument?
>>
>> You mean that the *callee* can be dynamic. However, the *caller* cannot. 
> 
> Of course. It makes no sense to ask for integrated syntax with a variable string. Think of it for a minute.

It standardizes a system for dynamic method dispatch with arguments generated at runtime or compile time. For arguments generated at compile time, it provides syntactic sugar.

Yours standardizes a system for dynamic method dispatch with arguments generated at compile time, and provides syntactic sugar. It also provides some minor opportunities for partial compile-time checking for these arguments.

In the end, I think that standardization of the runtime portions isn't so important. I accede.
April 18, 2009
On Fri, 17 Apr 2009 21:54:52 -0400, Steven Schveighoffer <schveiguy@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.

-Steve
April 18, 2009

Steven Schveighoffer wrote:
> ...
> 
> I think Nick's point number 2 is that you would have to write the above as:
> 
> void twix(T)(T value)
> {
>       value.invokeMethod("bar");
>       value.invokeMethod("bar");
> }
> 
> which would work whether bar was dynamically or statically defined.

So we have to write ALL templated code that calls member functions or accesses properties/fields like this?  Because otherwise objects may as well not be able to do this, since it wouldn't play with any templates ever.

You have to be joking.

> ...
> 
> -Steve
April 18, 2009
Steven Schveighoffer wrote:
> On Fri, 17 Apr 2009 18:01:51 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
>> Au contraire, it's a very adequate comparison. We changed the language to support ranges/arrays uniformly. Here, I'll paste your argument with the appropriate changes:
>>
>> ====
>> But globals acting as members cannot make arrays usable in the same way
>> as user-defined types, because one of the rules of arrays is that trying to call a non-existant member function on an array results in a compile-time error. The best your rule can do it make nonmember calls *seem* the same which is deceptive.
>> ====
> 
> Calling global functions as if they were array members does not subvert the type system.  It is not even close to the same level of danger that this is.

It's also far away from the same level of opportunity.

> I'm all for expanding runtime introspection that remains within the type system, I'm even for adding some possibility to create dynamically dispatched functions, as long as those functions are called differently from normal functions.
> 
> I think you would agree that one of the main roles of the compiler is to prevent you from making mistakes before it even gets to runtime.  Without knowing which calls the compiler checked and which ones it didn't, I can't know where to spend time scrutinizing.

Of course I agree. The thing is, if you decide to use a dynamic type, then it will become like a dynamic type.

>>> If you want static and dynamic calls to be really usable in the same way (like iterating over a range vs array), then there's only two possibilities:
>>>  1. Make attempts to invokation a non-existant static function a runtime error (obviously a bad idea).
>>> or
>>> 2. Provide a *secondary* syntax to invoke a method that works for both static and dynamic. Such as through a reflection api:
>>>  traits(new Foo()).invokeMethod("bar");
>>
>> If I want to write an algorithm that calls "bar" twice, it should be:
>>
>> void twix(T)(T value)
>> {
>>     value.bar();
>>     value.bar();
>> }
>>
>> NOT
>>
>> void twix(T)(T value)
>> {
>>      static if (isDynamicType!T)
>>      {
>>          value.invokeMethod("bar");
>>          value.invokeMethod("bar");
>>      }
>>      else
>>      {
>>          value.bar();
>>          value.bar();
>>      }
>> }
>>
>> Please at least acknowledge that you are in receipt of this argument.
> 
> I think Nick's point number 2 is that you would have to write the above as:
> 
> void twix(T)(T value)
> {
>       value.invokeMethod("bar");
>       value.invokeMethod("bar");
> }
> 
> which would work whether bar was dynamically or statically defined.

And will uglify and pessimize all code for the benefit of the few.

>> 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.

You will be surprised.


Andrei
April 18, 2009
> Cool! I suggest the rewrite:
>
> c.unknownmethod(args) -> c.opDotExp!("unknownmethod")(args)
>
> That way you have the option of handling the method name statically or dynamically.

Careful.  If you do that, you need to make sure it's possible to invoke a given method at runtime.  Assuming getMembers is implemented (I make daily sacrifices in hopes of that), each distinct opDotExpr!(s) needs to get an entry.

Otherwise, you've ironically created something that's HARDER to programatically invoke.  :P

A few more comments.

There seem to be a few arguments being given against this.  First is one of syntax: if this gets in to the language, you won't be able to tell whether a given member access actually works or not, so it should have a unique syntax.

The problem with this is that if you do that you might as well not bother at all; dynamic dispatch objects will never be able to participate in templates.  Unless, of course, you always write templates using dynamic dispatch syntax, at which point you've just nullified the benefits of doing so since now you can't tell whether a given call will work or not until runtime.

It either goes into the language with "normal" syntax and is used sparingly, or it goes in with special syntax and is either never used (or when it is used it is limited in where it can be used) or everything switches over to the special syntax.

As much as I would prefer to see dynamic dispatch have a special syntax or marker of some sort, since I actually want to see it in the language, I have to go with the first option.

The other argument against is that there isn't a sufficient benefit to doing this.  I'd argue that there is.  Off the top of my head:

* Swizzling: given a 4D vector, there are 256 possible swizzling operations.  With dynamic dispatch, you can define them only as needed without extra syntax.

* XML-RPC: Python has the best interface for this, hands down: just connect to the service and go nuts.

* Message-passing: I had a big app a few years ago that was designed around this.  Everything went through a central messaging object. Without this sort of syntax, I and everyone else would have gone mad from continually writing `msgSink.sendMessage("blah")` everywhere when `msgSink.blah` was perfectly unambiguous.

There ARE benefits to this sort of ability.

I don't think this would be something that you'd see everywhere; it'd be relegated to a few specific types where it makes sense.  Hell, you could probably make Descent highlight such objects with a different colour so you always know.

You CAN achieve a similar effect without special syntax.  But you can do foreach without foreach.  And you don't need scope.  Or classes.  Or symbols.

The two questions are: is it useful and is it worth the effort.  The first is definitely true from my experience, and I'm not sure we can answer the second until we get some time with it to see how well it works.

Perhaps davidl could release his patch so it can be played with? :D

  -- Daniel
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18