www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Fully dynamic d by opDotExp overloading

reply davidl <davidl nospam.org> writes:
After tweaking dmd a bit litte, i get the dotexp overloading work.

The following is the test code:
import std.stdio;
class c
{

     B opDotExp(char[] methodname,...)
     {
     	writefln("god it works ", methodname);
       return new B();
     }
     void opAdd(int j)
     {

     }
     void test()
     {
     }
}

class a:c
{

}

class B
{
   int i;
   B opAssign(int k){
     i=k;
     return this;
   }
}

char[] v1;

void func(char[] v, ...){}

void main()
{
    a v=new a;
    v.test();
    v.dynamicmethod(3,4);
    //v.qq = 5;
    writefln((v.qq = 5).i);
}

it generates the output:
god it works dynamicmethod
god it works qq
5

Any comments? Do you like this feature?

-- 
使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 16 2009
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Very cool.  What I'd like to see is this, TypeInfo_Struct.getMembers and
ClassInfo.getMembers.  Once we have all three, we could do some
incredibly awesome things.

Like implement Objective C-style message passing as a library.  :D

  -- Daniel
Apr 16 2009
parent davidl <davidl nospam.org> writes:
在 Fri, 17 Apr 2009 14:44:46 +0800,Daniel Keep  
<daniel.keep.lists gmail.com> 写道:

 Very cool.  What I'd like to see is this, TypeInfo_Struct.getMembers and
 ClassInfo.getMembers.  Once we have all three, we could do some
 incredibly awesome things.

 Like implement Objective C-style message passing as a library.  :D

   -- Daniel

Thanks. I also hope for more runtime info. -- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 17 2009
prev sibling next sibling parent reply Don <nospam nospam.com> writes:
davidl wrote:
 After tweaking dmd a bit litte, i get the dotexp overloading work.
 
 class c
 {
 
     B opDotExp(char[] methodname,...)
     {
         writefln("god it works ", methodname);
       return new B();
     }

 it generates the output:
 god it works dynamicmethod
 god it works qq
 5
 
 Any comments? Do you like this feature?

That's very impressive. How did you do it? Are the syntax and semantic passes still independent?
Apr 16 2009
parent davidl <davidl nospam.org> writes:
在 Fri, 17 Apr 2009 14:52:30 +0800,Don <nospam nospam.com> 写道:

 davidl wrote:
 After tweaking dmd a bit litte, i get the dotexp overloading work.
  class c
 {
      B opDotExp(char[] methodname,...)
     {
         writefln("god it works ", methodname);
       return new B();
     }

 it generates the output:
 god it works dynamicmethod
 god it works qq
 5
  Any comments? Do you like this feature?

That's very impressive. How did you do it? Are the syntax and semantic passes still independent?

Yes, it follows the rule of rewriting the c.unknownmethod(args) to c.opDotExp("unknownmethod", args); Though it currently gets bugs of not supporting struct, opDotExp signiture must be (char[],...), and possibly break extension method. These should all be fixable. -- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 17 2009
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
davidl:
 Any comments? Do you like this feature?

It's cute. Maybe "." can be used for static methods and some other syntax for dynamic ones, like: v..dynamicmethod(x); v->dynamicmethod(x); It seems that after releasing DMD source code the head of the cat is finally starting to come out of Walter's bag :-) This is for the better. Bye, bearophile
Apr 17 2009
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
davidl wrote:
 After tweaking dmd a bit litte, i get the dotexp overloading work.
 
 The following is the test code:
 import std.stdio;
 class c
 {
 
     B opDotExp(char[] methodname,...)
     {
         writefln("god it works ", methodname);
       return new B();
     }
     void opAdd(int j)
     {
 
     }
     void test()
     {
     }
 }
 
 class a:c
 {
 
 }
 
 class B
 {
   int i;
   B opAssign(int k){
     i=k;
     return this;
   }
 }
 
 char[] v1;
 
 void func(char[] v, ...){}
 
 void main()
 {
    a v=new a;
    v.test();
    v.dynamicmethod(3,4);
    //v.qq = 5;
    writefln((v.qq = 5).i);
 }
 
 it generates the output:
 god it works dynamicmethod
 god it works qq
 5
 
 Any comments? Do you like this feature?
 

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. Andrei
Apr 17 2009
next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Fri, 17 Apr 2009 13:25:45 +0400, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:

 davidl wrote:
 After tweaking dmd a bit litte, i get the dotexp overloading work.
  The following is the test code:
 import std.stdio;
 class c
 {
      B opDotExp(char[] methodname,...)
     {
         writefln("god it works ", methodname);
       return new B();
     }
     void opAdd(int j)
     {
      }
     void test()
     {
     }
 }
  class a:c
 {
  }
  class B
 {
   int i;
   B opAssign(int k){
     i=k;
     return this;
   }
 }
  char[] v1;
  void func(char[] v, ...){}
  void main()
 {
    a v=new a;
    v.test();
    v.dynamicmethod(3,4);
    //v.qq = 5;
    writefln((v.qq = 5).i);
 }
  it generates the output:
 god it works dynamicmethod
 god it works qq
 5
  Any comments? Do you like this feature?

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

Yes, this also helps to catch errors at compile-time (like, dynObject.lenght vs dynObject.length).
Apr 17 2009
prev sibling next sibling parent BCS <ao pathlink.com> writes:
Reply to Andrei,
 Cool! I suggest the rewrite:
 
 c.unknownmethod(args) -> c.opDotExp!("unknownmethod")(args)
 

vote++;
 That way you have the option of handling the method name statically or
 dynamically.
 
 Andrei
 

Apr 17 2009
prev sibling next sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
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. This would rarely be an issue, I grant, but: Let's say you have a set of valid arguments for the opDotExp template. Why the hell aren't you writing individual methods?! So opDotExp is nearly useless if you make the method name a template argument. The *only* uses are - blacklisting arguments, with compile-time errors - requiring that arguments follow a certain (regular) pattern, with compile-time errors - a way to get the __FUNCTION__ macro that's been requested several times and not yet implemented
Apr 17 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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.
 This would rarely be an issue, I grant, but:
 
 Let's say you have a set of valid arguments for the opDotExp template. 
 Why the hell aren't you writing individual methods?!
 
 So opDotExp is nearly useless if you make the method name a template 
 argument. The *only* uses are
 - blacklisting arguments, with compile-time errors
 - requiring that arguments follow a certain (regular) pattern, with 
 compile-time errors
 - a way to get the __FUNCTION__ macro that's been requested several 
 times and not yet implemented

I don't think I quite understand. Let me repeat: passing the string as a template gives you static+dynamic. Passing the string as a runtime value gives you dynamic. To clarify: class Dynamo { Variant call(string name, Variant[] args...) { ... } Variant opDotExp(string name, T...)(T args) { return call(name, variantArray(args)); } ... } Now you can say: Dynamo d; d.foo(); string bar = chomp(readln); d.call(bar); Makes sense? Andrei
Apr 17 2009
parent Christopher Wright <dhasenan gmail.com> writes:
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.
Apr 17 2009
prev sibling parent BCS <ao pathlink.com> writes:
Reply to Christopher,

 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.
 

you're passing it as a template argument? You mean that the *callee* can be dynamic. However, the *caller* cannot. This would rarely be an issue, I grant, but:

the caller *can't* be dynamic, the calling code is known exactly at compile time. If you want to be able to call a function by name or by the normal syntax use: void CallByName(char[] name, args...) {...} void opDot(char[] name)(args...) { CallByName(name, args ...); } // one line, will get inlined if anything ever does.
 Let's say you have a set of valid arguments for the opDotExp template.
 Why the hell aren't you writing individual methods?!
 

for one: struct S(T) { T* t; void opDot!(char[] name)(U u) { mixin("t." ~name~ "(v);"); } } also haveing a large set of function where you can compute the meaning from the name: void Foo(){...} void FooBar(){...} void Bar(){...} //LogFoo //LogFooBar //LogBar void opDot(char[] name)() { static assert(name.length > 3 && name[0..3] == "Log") writelf("Calling "~ name[3..$]); mixin(name[3..$]~"();"); writelf("Called "~ name[3..$]); }
 So opDotExp is nearly useless if you make the method name a template
 argument. The *only* uses are
 - blacklisting arguments, with compile-time errors
 - requiring that arguments follow a certain (regular) pattern, with
 compile-time errors
 - a way to get the __FUNCTION__ macro that's been requested several
 times and not yet implemented

Apr 17 2009
prev sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
 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
Apr 17 2009
parent Christopher Wright <dhasenan gmail.com> writes:
Daniel Keep 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.

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

Yeah, that's one annoying thing about the proposal. There are a couple of solutions: - Let types with opDotExp manipulate their type information dynamically. This would be a *lot* of work, and it wouldn't necessarily work for everything (let's say one instance of the class has some methods available and another one has different ones available). - Allow template instantiations to be included in type information. This will require patching type info again, for instance when you compile files separately and instantiate the templates in different modules.
Apr 18 2009
prev sibling next sibling parent "Danny Wilson" <bluezenix gmail.com> writes:
Op Fri, 17 Apr 2009 08:31:02 +0200 schreef davidl <davidl nospam.org>:

 it generates the output:
 god it works dynamicmethod
 god it works qq
 5

 Any comments? Do you like this feature?

Awesome, this is exactly what I thought was the point of 'opDot' when i first skimmed over it in the docs. This is great for lots of purposes. A small nice example is: http://haxe.org/doc/advanced/xml_fast
Apr 17 2009
prev sibling next sibling parent Christopher Wright <dhasenan gmail.com> writes:
davidl wrote:
 After tweaking dmd a bit litte, i get the dotexp overloading work.

Do want. With this and extended runtime information, you can create a Variant that works exactly like the original type, allowing you to call methods on it without extracting it to the original type, just throwing an ArgumentException or MethodMissingException if you give it something bad.
Apr 17 2009
prev sibling next sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
davidl, el 17 de abril a las 14:31 me escribiste:
 After tweaking dmd a bit litte, i get the dotexp overloading work.
 
 The following is the test code:
 import std.stdio;
 class c
 {
 
     B opDotExp(char[] methodname,...)
     {
     	writefln("god it works ", methodname);
       return new B();
     }
     void opAdd(int j)
     {
 
     }
     void test()
     {
     }
 }
 
 class a:c
 {
 
 }
 
 class B
 {
   int i;
   B opAssign(int k){
     i=k;
     return this;
   }
 }
 
 char[] v1;
 
 void func(char[] v, ...){}
 
 void main()
 {
    a v=new a;
    v.test();
    v.dynamicmethod(3,4);
    //v.qq = 5;
    writefln((v.qq = 5).i);
 }
 
 it generates the output:
 god it works dynamicmethod
 god it works qq
 5
 
 Any comments? Do you like this feature?

This is awsome indeed. I'd love to see it in the specs. The suggestion of making opDotExp a template it's good one too. I guess that now that opDot is replaced by alias this, opDot can be used for this instead of opDotExp. I don't fully understand the example though. In writefln((v.qq = 5).i), how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't propagate the 5 to the new B()? Thanks for the great job. -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Que barbaridad, este pa铆s se va cada ves m谩s pa' tras, m谩s pa' tras... -- Sidharta Kiwi
Apr 17 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Denis Koroskin wrote:
 On Fri, 17 Apr 2009 18:24:04 +0400, Steven Schveighoffer <schveiguy yahoo.com>
wrote:
 
 On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella <llucax gmail.com>  
 wrote:

 I don't fully understand the example though. In writefln((v.qq = 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't  
 propagate
 the 5 to the new B()?

opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties. I for one, can't really see a huge benefit, but then again, I don't normally work with dynamic-type langauges. It looks to me like a huge hole that the compiler will ignore bugs that would have been caught if the methods were strongly typed: class c { void opDotExp(char[] methodname,...) { if(methodname == "mymethod") callMyMethod(); else throw new Exception("bad method name: " ~ methodname); } } void foo(c myc, bool rarelySetToTrue) { if(rarelySetToTrue) myc.mymethud(); // compiles, will throw runtime exception } Also, how do you overload the return value? Using this proposal, you can't have different dynamic methods that return different types. -Steve

Here is how it could be done: class C { auto opDot(string methodName, T... args)(T args) // opDotExp renamed to opDot { static if (methodName == "length") { return _length; // return type is size_t } else static if (methodName == "resize") { _resize(args); // return type is void } } } This is a great use-case for compile-time "static switch". Can we haz one, please?

I think the more urgent need is for static loops. At least we have a simple workaround for static switch. Andrei
Apr 17 2009
parent reply downs <default_357-line yahoo.de> writes:
Andrei Alexandrescu wrote:
 Denis Koroskin wrote:
 On Fri, 17 Apr 2009 18:24:04 +0400, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:

 On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella
 <llucax gmail.com>  wrote:

 I don't fully understand the example though. In writefln((v.qq = 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't 
 propagate
 the 5 to the new B()?

opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties. I for one, can't really see a huge benefit, but then again, I don't normally work with dynamic-type langauges. It looks to me like a huge hole that the compiler will ignore bugs that would have been caught if the methods were strongly typed: class c { void opDotExp(char[] methodname,...) { if(methodname == "mymethod") callMyMethod(); else throw new Exception("bad method name: " ~ methodname); } } void foo(c myc, bool rarelySetToTrue) { if(rarelySetToTrue) myc.mymethud(); // compiles, will throw runtime exception } Also, how do you overload the return value? Using this proposal, you can't have different dynamic methods that return different types. -Steve

Here is how it could be done: class C { auto opDot(string methodName, T... args)(T args) // opDotExp renamed to opDot { static if (methodName == "length") { return _length; // return type is size_t } else static if (methodName == "resize") { _resize(args); // return type is void } } } This is a great use-case for compile-time "static switch". Can we haz one, please?

I think the more urgent need is for static loops. At least we have a simple workaround for static switch. Andrei

Static loops are simple, at least in functions. import std.stdio; template Tuple(T...) { alias T Tuple; } template Repeat(T, int I) { static if (!I) alias Tuple!() Repeat; else alias Tuple!(T, Repeat!(T, I-1)) Repeat; } void main() { foreach (i, bogus; Repeat!(void, 15)) writefln(i); }
Apr 18 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
downs:
 Static loops are simple, at least in functions.
 ...
 void main() {
   foreach (i, bogus; Repeat!(void, 15))
     writefln(i);
 }

My dlibs have: Range!([start], stop[, step]) with it I think your code becomes: void main() { foreach (i; Range!(15)) putr(i); } But a static foreach (on a static data structure that has opApply) is not doable yet, I think. Bye, bearophile
Apr 18 2009
parent reply downs <default_357-line yahoo.de> writes:
bearophile wrote:
 downs:
 Static loops are simple, at least in functions.
 ...
 void main() {
   foreach (i, bogus; Repeat!(void, 15))
     writefln(i);
 }

My dlibs have: Range!([start], stop[, step]) with it I think your code becomes: void main() { foreach (i; Range!(15)) putr(i); } But a static foreach (on a static data structure that has opApply) is not doable yet, I think. Bye, bearophile

Foreach on a tuple is evaluated at compile-time.
Apr 18 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
downs:
 bearophile:
 But a static foreach (on a static data structure that has opApply) is not
doable yet, I think.

Foreach on a tuple is evaluated at compile-time.

Yes, that's the whole point of that Range!(). But you can't use that trick on an associative array, or a struct with OpApply, etc. Bye, bearophile
Apr 18 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
bearophile wrote:
 downs:
 bearophile:
 But a static foreach (on a static data structure that has opApply) is not
doable yet, I think.


Yes, that's the whole point of that Range!(). But you can't use that trick on an associative array, or a struct with OpApply, etc. Bye, bearophile

And you can't have it outside a function like you can static if. Also, there are various bugs relating to the values being iterated over sometimes kinda-but-not-really being compile-time constants. -- Daniel
Apr 18 2009
parent downs <default_357-line yahoo.de> writes:
Daniel Keep wrote:
 
 bearophile wrote:
 downs:
 bearophile:
 But a static foreach (on a static data structure that has opApply) is not
doable yet, I think.


But you can't use that trick on an associative array, or a struct with OpApply, etc. Bye, bearophile

And you can't have it outside a function like you can static if. Also, there are various bugs relating to the values being iterated over sometimes kinda-but-not-really being compile-time constants. -- Daniel

Very true, but the _index_ should always be a constant.
Apr 19 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
downs wrote:
 Static loops are simple, at least in functions.
 
 import std.stdio;
 
 template Tuple(T...) { alias T Tuple; }
 template Repeat(T, int I) { static if (!I) alias Tuple!() Repeat; else alias
Tuple!(T, Repeat!(T, I-1)) Repeat; }
 
 void main() {
   foreach (i, bogus; Repeat!(void, 15))
     writefln(i);
 }

I know, but at about the fiftienth one you get sick of it. And generating code in a static loop is much harder. Andrei
Apr 18 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu:
 I know, but at about the fiftienth one you get sick of it.

In some situations static loops can be useful, but in general isn't the compiler supposed to be able to perform loop unrolling by itself, according to compilation arguments and according to how much code is present into the loop? (if it's too much code then loop unrolling is to be avoided). Bye, bearophile
Apr 18 2009
parent BCS <none anon.com> writes:
Hello bearophile,

 Andrei Alexandrescu:
 
 I know, but at about the fiftienth one you get sick of it.
 

the compiler supposed to be able to perform loop unrolling by itself, according to compilation arguments and according to how much code is present into the loop? (if it's too much code then loop unrolling is to be avoided). Bye, bearophile

It's not just loop unrolling as some loops can't be "rolled" in the first place, like when one of the loop variables is a type or you need to static if on the index.
Apr 18 2009
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Fri, 17 Apr 2009 10:32:04 -0400, Denis Koroskin <2korden gmail.com> 
 wrote:
 
 On Fri, 17 Apr 2009 18:24:04 +0400, Steven Schveighoffer 
 <schveiguy yahoo.com> wrote:

 On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella <llucax gmail.com>
 wrote:

 I don't fully understand the example though. In writefln((v.qq = 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't
 propagate
 the 5 to the new B()?

I think it translates to opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties. I for one, can't really see a huge benefit, but then again, I don't normally work with dynamic-type langauges. It looks to me like a huge hole that the compiler will ignore bugs that would have been caught if the methods were strongly typed: class c { void opDotExp(char[] methodname,...) { if(methodname == "mymethod") callMyMethod(); else throw new Exception("bad method name: " ~ methodname); } } void foo(c myc, bool rarelySetToTrue) { if(rarelySetToTrue) myc.mymethud(); // compiles, will throw runtime exception } Also, how do you overload the return value? Using this proposal, you can't have different dynamic methods that return different types. -Steve

Here is how it could be done: class C { auto opDot(string methodName, T... args)(T args) // opDotExp renamed to opDot { static if (methodName == "length") { return _length; // return type is size_t } else static if (methodName == "resize") { _resize(args); // return type is void } } }

Look! I found a way to implement this *in the current compiler*!: class C { size_t length() { return _length; } void resize(T...)(T args) { _resize(args); } } sorry, couldn't help myself :P -Steve

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
Apr 17 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
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... }
Apr 17 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Apr 17 2009
next sibling parent reply Jason House <jason.james.house gmail.com> writes:
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
Apr 17 2009
parent BCS <none anon.com> writes:
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.
Apr 17 2009
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:gsav4n$2ij4$1 digitalmars.com...
 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

It's really late, so I can't come up with much coherent thought, but: 1. Ick. 2. Wasn't the potential for just that sort of abuse a big part of the reason for ditching #define? 3. struct F { void fooBar() { ... } } Pascalize!F f; // Can't invoke shit! // Don't ask me what point I'm trying to make with that, // I don't even know, myself.
Apr 18 2009
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 Sure, but what is the reason to need dynamic methods?  I'm just trying 
 to understand the usefulness of it.  If a method is dynamic, we lose the 
 following things:
 
 - compile-time type/signature checking
 - IDE assistance in determining which methods are available
 - ease of tracing where a method call goes.
 - not future proof -- for example, if a method name gets changed or 
 moved, the code using the method still compiles.
 
 If we lose all these things, there must be *something* we gain by doing 
 this, right?

There are people who swear by the ability of adding methods at runtime and changing the inheritance hierarchy dynamically. It makes for a very fluid environment.
 Also, what is the benefit of doing something like this versus 
 specifically calling the dispatcher instead of having the compiler 
 translate it?

Probably that's more of a "last mile" thing. Andrei
Apr 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:gsacm8$1gj9$2 digitalmars.com...
 There are people who swear by the ability of adding methods at runtime and 
 changing the inheritance hierarchy dynamically. It makes for a very fluid 
 environment.

Personally, I've always seen that as extremely sloppy and haphazard.
Apr 17 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Nick Sabalausky:
 There are people who swear by the ability of adding methods at runtime and 
 changing the inheritance hierarchy dynamically. It makes for a very fluid 
 environment.

Personally, I've always seen that as extremely sloppy and haphazard.

Adding methods at runtime is named "monkey patching", and it is considered a bad practice even in Python. In Ruby it is more common. Usually in such languages such things are less dangerous because the code contains lot of tests anyway. Some people say that a way to remove most of the downsides of monkey patching is to make it scoped, that is the changes (like a method added or replaced) to a class aren't seen globally in the whole program (like from other modules), but only in the scope where such change is done (and its subscopes). I think I have not seen languages where this is doable yet. Bye, bearophile
Apr 17 2009
next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 17/04/2009 21:34, bearophile wrote:
 Nick Sabalausky:
 There are people who swear by the ability of adding methods at
 runtime and changing the inheritance hierarchy dynamically. It
 makes for a very fluid environment.

haphazard.

Adding methods at runtime is named "monkey patching", and it is considered a bad practice even in Python. In Ruby it is more common. Usually in such languages such things are less dangerous because the code contains lot of tests anyway. Some people say that a way to remove most of the downsides of monkey patching is to make it scoped, that is the changes (like a method added or replaced) to a class aren't seen globally in the whole program (like from other modules), but only in the scope where such change is done (and its subscopes). I think I have not seen languages where this is doable yet. Bye, bearophile

just like anything in life this can be overused. This is a very useful tool in a programmer's toolbox for when you need, for example, to quickly experiment with something or do an urgent fix. It's like when you build a house you have it properly designed and have solid foundations but after the house is built you can still redesign internally without rebuilding the entire house.
Apr 17 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 17/04/2009 21:58, Steven Schveighoffer wrote:
 On Fri, 17 Apr 2009 14:49:37 -0400, Yigal Chripun <yigal100 gmail.com>
 wrote:

 On 17/04/2009 21:34, bearophile wrote:
 Nick Sabalausky:
 There are people who swear by the ability of adding methods at
 runtime and changing the inheritance hierarchy dynamically. It
 makes for a very fluid environment.

haphazard.

Adding methods at runtime is named "monkey patching", and it is considered a bad practice even in Python. In Ruby it is more common. Usually in such languages such things are less dangerous because the code contains lot of tests anyway. Some people say that a way to remove most of the downsides of monkey patching is to make it scoped, that is the changes (like a method added or replaced) to a class aren't seen globally in the whole program (like from other modules), but only in the scope where such change is done (and its subscopes). I think I have not seen languages where this is doable yet. Bye, bearophile

just like anything in life this can be overused. This is a very useful tool in a programmer's toolbox for when you need, for example, to quickly experiment with something or do an urgent fix. It's like when you build a house you have it properly designed and have solid foundations but after the house is built you can still redesign internally without rebuilding the entire house.

It's more like adding another outside door. Try doing that ;) (BTW, removing one is easy, but that kind of doesn't apply here...) changing internals in code is easy, that's why we use OOP. Changing APIs is not. -Steve

I completely disagree. here's a use case from my work: we have executables that take 20+ min to compile and link, written with C, C++, and fortran, and we also have code in JavaScript. when I work on the Javascript code it's very easy and quick to debug and test changes while the program is still running while doing the same with the C++ code is very annoying, every change I want to test requires me to wait 20+ minutes for the re-link. a *HUGE* waste of time and also it's easy to forget what you were doing while you wait. no APIs involved in this case. OOP has nothing to do with this since there are both dynamically typed and statically typed OOP languages (Smalltalk vs. D/JAVA) as well as non-OOP languages (ML vs. LISP). btw, I'm not trying to convince you that dynamic typing is necessary always a better solution. What I'm saying is that I agree with Andrei - we need to be open minded and have as many useful tools as possible in our programmer toolbox. The important thing is to choose the right tool for the job. Having only a hammer makes a person think that "when all you got is a hammer everything looks like a nail" kind of way.
Apr 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Yigal Chripun" <yigal100 gmail.com> wrote in message 
news:gsam1p$1ut7$1 digitalmars.com...
 On 17/04/2009 21:58, Steven Schveighoffer wrote:

 btw, I'm not trying to convince you that dynamic typing is necessary 
 always a better solution. What I'm saying is that I agree with Andrei - we 
 need to be open minded and have as many useful tools as possible in our 
 programmer toolbox. The important thing is to choose the right tool for 
 the job.

Typically, yes, having "as many useful tools as possible in our programmer toolbox" is great. But with opDotExp, that's not the whole story. What opDotExp is, is a tool of only occasional use that provides only a small benefit, *and* ends up destroying a much more important tool: compile-time checking on a class's members. Yea, sure I want more tools in my programmer tool box. But I don't want a minor one that's going to mess up one of my major ones just by being in there.
Apr 17 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Nick Sabalausky wrote:
 "Yigal Chripun" <yigal100 gmail.com> wrote in message 
 news:gsam1p$1ut7$1 digitalmars.com...
 On 17/04/2009 21:58, Steven Schveighoffer wrote:

 btw, I'm not trying to convince you that dynamic typing is necessary 
 always a better solution. What I'm saying is that I agree with Andrei - we 
 need to be open minded and have as many useful tools as possible in our 
 programmer toolbox. The important thing is to choose the right tool for 
 the job.

Typically, yes, having "as many useful tools as possible in our programmer toolbox" is great. But with opDotExp, that's not the whole story. What opDotExp is, is a tool of only occasional use that provides only a small benefit, *and* ends up destroying a much more important tool: compile-time checking on a class's members.

s/on a class's members/on the members of the class that actively chose that/
 Yea, sure I want more tools in my programmer tool box. But I don't want a 
 minor one that's going to mess up one of my major ones just by being in 
 there.

I don't think this argument holds. Andrei
Apr 17 2009
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:gsamrs$204u$3 digitalmars.com...
 Typically, yes, having "as many useful tools as possible in our 
 programmer toolbox" is great. But with opDotExp, that's not the whole 
 story. What opDotExp is, is a tool of only occasional use that provides 
 only a small benefit, *and* ends up destroying a much more important 
 tool: compile-time checking on a class's members.

s/on a class's members/on the members of the class that actively chose that/

Right, that's what I meant, but that's still worse than not having opDotExp at all. If someone wants to do dynamic programming, then ok, fine, even though I think they're making a mistake, they're free to go use a dynamic language. But D is supposed to be a static language. That's why I use it. I don't want that staticness to be hijackable just because some people prefer dynamicness. Obviously, if there's a dynamic feature that *doesn't* interfere with the static stuff (the phobos Variant, for example), then that's great. Toss it in and let people use it if they want. Why should I care? But with opDotExp, its mere *existence* undermines my ability to be sure that non-quoted identifiers are ok as long as they've compiled. That type of tradeoff is obviously fine when the potential benefits are significant enough (operator overloading, for instance). But from everything I've seen so far, opDotExp's benefits are trivial at best. I don't want to have to keep track of "ok, is this class using opDotExp or not, because if it is, then I need to be more careful", just for the sake of a feature that provides such a tiny and questionable benefit.
Apr 17 2009
parent Benji Smith <dlanguage benjismith.net> writes:
Danny Wilson wrote:
 Now let's go from that obvious observation to opDotExp()
 
 You know the class uses opDotExp() because it said so in the docs. 
 Examples that could really benifit from this are:
 - XMLRPC and other kinds of remoting
 - Quick access to: XML / JSON / Yaml / Config files / DB access
 - Calling DLLs without bindings
 - Lots more
 
 All these would mention it in their docs, guaranteed. Because they use 
 opDotExp it's implicitly mentioned. I don't think anyone would tell a 
 documentation generator to list all public methods except opDotExp .. 
 that would be just braindead. And you could generate the docs yourself 
 if you have to code..

Incidentally, one ugly problem with using opDotExp is that the underlying invocation might allow characters that aren't legal in D identifiers. For example, let's say I have a dynamic object wrapping a JavaScript library, and I want to access a JQuery object. JavaScript allows the '$' character to appear in identifiers, and the JQuery people cleverly used that name for one of their core objects (which, I think, acts as an ID registry, or something like that). So, this is a perfectly legal JQuery expression: var a = $("hello"); Using the opDotExp syntax, I'd ideally prefer to call it like this: auto a = js.$("hello"); But the compiler will reject that syntax, since '$' isn't a legal D identifier. Of course, in cases like that, we'll just use some sort of dynamic invocation method: auto a = js.invoke("$", "hello"); Which makes me think this whole discussion is kind of a waste of time, since every single implementation of opDotExp is going to end up delegating to a string-based dispatcher method anyhow. THAT'S the really interesting discussion. In fact, I think I'll start a new topic... --benji
Apr 27 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Fri, 17 Apr 2009 15:55:43 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Nick Sabalausky wrote:
 "Yigal Chripun" <yigal100 gmail.com> wrote in message 
 news:gsam1p$1ut7$1 digitalmars.com...
 On 17/04/2009 21:58, Steven Schveighoffer wrote:

 btw, I'm not trying to convince you that dynamic typing is necessary 
 always a better solution. What I'm saying is that I agree with 
 Andrei - we need to be open minded and have as many useful tools as 
 possible in our programmer toolbox. The important thing is to choose 
 the right tool for the job.

programmer toolbox" is great. But with opDotExp, that's not the whole story. What opDotExp is, is a tool of only occasional use that provides only a small benefit, *and* ends up destroying a much more important tool: compile-time checking on a class's members.

s/on a class's members/on the members of the class that actively chose that/

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.
 The problem with me is that it doesn't *look* different.

I understand how it could be a problem. The thing is, it is also an advantage.
  If there was 
 some way to denote "call dynamic method" instead of "call static method" 
 or some way to denote "has dynamic methods", then I'd have no problem 
 with it.  Even if you were forced to derive from a special base type in 
 order to use dynamic methods, I wouldn't mind that.

Would you like ranges that work very different from built-in arrays, and everybody to special-case around that? By the way, D's AAs suck partly because they aren't like anything. I *really* hate the way D does AAs. Andrei
Apr 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"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. 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");
Apr 17 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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?

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. ====
 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. 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. Andrei
Apr 17 2009
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
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

Apr 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
news:gsbini$dga$1 digitalmars.com...
 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?

No, only ones that absolutely need to work with dynamic classes. Which is neither all nor the majority. Though dynamic invokation can absolutely be useful at times, it is, and absolutely should remain, a fringe case.
 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.

Joking about keeping my static guarantees? When the only other thing at stake is a little bit of syntactic sugar on some occasional dynamic code? Not a chance.
Apr 17 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Nick Sabalausky wrote:
 "Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
 news:gsbini$dga$1 digitalmars.com...
 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.

accesses properties/fields like this?

No, only ones that absolutely need to work with dynamic classes. Which is neither all nor the majority. Though dynamic invokation can absolutely be useful at times, it is, and absolutely should remain, a fringe case.

But you're getting it exactly, almost rigorously, backwards. Algorithms specify an interface (e.g. implicit in the case of std.algorithm). The proposed feature allows the few to conform to the many, so the onus is exactly where it should.
 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.

Joking about keeping my static guarantees? When the only other thing at stake is a little bit of syntactic sugar on some occasional dynamic code? Not a chance.

Some people said quite the same thing about virtual methods. They said it's a really stupid idea because now you can't test code - who knows what the callee is going to do? They could throw, do the wrong thing, etc. They said virtual methods break all guarantees and undermine static typing. They also weren't kidding, and they also were "right". Well, I guess they were kinda missing the point :o). Andrei
Apr 17 2009
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Apr 17 2009
parent "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:gsbjci$f6h$1 digitalmars.com...
 Of course I agree. The thing is, if you decide to use a dynamic type, then 
 it will become like a dynamic type.

I didn't have a problem with that reasoning when Variant was added, because that was just one type. But now we're talking about opening D up to an entire class (no pun intended) of dynamic types. We've got an ointment factory and are now talking about adding a production line for flies and deliberately minimizing isolation. I use D because it's static, if we bring this level of dynamic-ism into it, then I won't be able to keep the flies out of my static ointment. "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:gsbjci$f6h$1 digitalmars.com...
 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.

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

Funny, that's very similar to my reasoning *against* allowing opDotExp. It undermines static typing expectations for the benefit of the few. It's very similar, but just on a smaller scale, to making declarations optional for the sake of those who don't like to use them.
Apr 17 2009
prev sibling next sibling parent reply Don <nospam nospam.com> writes:
Steven Schveighoffer wrote:
 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.

There is. Just mark opDot as nothrow.
 
 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

Apr 17 2009
parent Christopher Wright <dhasenan gmail.com> writes:
Don wrote:
 Steven Schveighoffer wrote:
 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.

There is. Just mark opDot as nothrow.

Not an option. I want to dispatch to methods that might throw exceptions.
Apr 18 2009
prev sibling next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 18/04/2009 04:54, Steven Schveighoffer wrote:

 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.

 -Steve

Here's an idea: Allow the use of this feature _only_ for appropriately marked types. dynamic class A {... opDotExp ...} //compiles dynamic struct B {... opDotExp ...} //compiles class A {... opDotExp ...} // compile-error: "please mark class as dynamic" struct B {... opDotExp ...} // as above now you have an easy way to know if a type is dynamic without changing the method invocation syntax. A proper IDE can easily mark those Types as different, for example, using a different color.
Apr 18 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Yigal Chripun wrote:
 On 18/04/2009 04:54, Steven Schveighoffer wrote:
 
 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.

 -Steve

Here's an idea: Allow the use of this feature _only_ for appropriately marked types. dynamic class A {... opDotExp ...} //compiles dynamic struct B {... opDotExp ...} //compiles class A {... opDotExp ...} // compile-error: "please mark class as dynamic" struct B {... opDotExp ...} // as above now you have an easy way to know if a type is dynamic without changing the method invocation syntax. A proper IDE can easily mark those Types as different, for example, using a different color.

The dynamic behavior is indicated by the use of opDotExp. The redundancy of the two notations doesn't quite sit well. Andrei
Apr 18 2009
next sibling parent BCS <none anon.com> writes:
Hello Andrei,

 The dynamic behavior is indicated by the use of opDotExp. The
 redundancy of the two notations doesn't quite sit well.
 
 Andrei
 

not exactly 1-to-1 but: abstract class C { void foo(); } // works class D { void foo(); } // fails: link error
Apr 18 2009
prev sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Sat, 18 Apr 2009 19:46:36 +0400, BCS <none anon.com> wrote:

 Hello Andrei,

 The dynamic behavior is indicated by the use of opDotExp. The
 redundancy of the two notations doesn't quite sit well.
  Andrei

not exactly 1-to-1 but: abstract class C { void foo(); } // works class D { void foo(); } // fails: link error

Bad example: class D { abstact void foo(); } // works
Apr 18 2009
parent BCS <none anon.com> writes:
Hello Denis,

 On Sat, 18 Apr 2009 19:46:36 +0400, BCS <none anon.com> wrote:
 
 not exactly 1-to-1 but:
 


So I saw, I'm just saying it's not without precedent.
Apr 18 2009
prev sibling next sibling parent Christopher Wright <dhasenan gmail.com> writes:
Steven Schveighoffer wrote:
 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

Or if the type you are dealing with is irrevocably weakly typed anyway, such as most of the use cases we've mentioned (scripting languages, database rows, Variant).
Apr 18 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 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.

class X { auto opDotExp(string fname, T...)(T args) { static if(fname == "blah") return foo(args); else static if(fname == "blither") return bar(args); else static assert(0, "Dunno how to "~fname); } } Andrei
Apr 18 2009
parent Don <nospam nospam.com> writes:
Steven Schveighoffer wrote:
 On Sat, 18 Apr 2009 14:05:30 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Steven Schveighoffer wrote:
 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.

class X { auto opDotExp(string fname, T...)(T args) { static if(fname == "blah") return foo(args); else static if(fname == "blither") return bar(args); else static assert(0, "Dunno how to "~fname); } }

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.
Apr 18 2009
prev sibling next sibling parent Yigal Chripun <yigal100 gmail.com> writes:
On 17/04/2009 22:54, Nick Sabalausky wrote:
 "Yigal Chripun"<yigal100 gmail.com>  wrote in message
 news:gsam1p$1ut7$1 digitalmars.com...
 On 17/04/2009 21:58, Steven Schveighoffer wrote:

 btw, I'm not trying to convince you that dynamic typing is necessary
 always a better solution. What I'm saying is that I agree with Andrei - we
 need to be open minded and have as many useful tools as possible in our
 programmer toolbox. The important thing is to choose the right tool for
 the job.

Typically, yes, having "as many useful tools as possible in our programmer toolbox" is great. But with opDotExp, that's not the whole story. What opDotExp is, is a tool of only occasional use that provides only a small benefit, *and* ends up destroying a much more important tool: compile-time checking on a class's members. Yea, sure I want more tools in my programmer tool box. But I don't want a minor one that's going to mess up one of my major ones just by being in there.

address the specific implementation of opDotExp. It's just that I wanted to reply to the posts that argued against dynamic typing altogether.
Apr 17 2009
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Adam D. Ruppe" <destructionator gmail.com> wrote in message 
news:mailman.1171.1239998473.22690.digitalmars-d puremagic.com...
 On Fri, Apr 17, 2009 at 03:54:47PM -0400, Nick Sabalausky wrote:
 What
 opDotExp is, is a tool of only occasional use that provides only a small
 benefit, *and* ends up destroying a much more important tool: 
 compile-time
 checking on a class's members.

Wouldn't the compile time checking remain the same on any class except the Variant (or whatever) which implements the new operator? If it is constrained to one type, the destruction seems like it would be acceptable. You can't trust much on a Variant at compile time anyway.

The problem is there would be no way to tell at a glance whether a given class uses opDotExp or not. You'd have to go look it up for every class. So, ok, we could solve that by requiring a different syntax for dynamic invokation. But we already have that: just pass a string to a dispatch function.
Apr 17 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Nick Sabalausky wrote:
 "Adam D. Ruppe" <destructionator gmail.com> wrote in message 
 news:mailman.1171.1239998473.22690.digitalmars-d puremagic.com...
 On Fri, Apr 17, 2009 at 03:54:47PM -0400, Nick Sabalausky wrote:
 What
 opDotExp is, is a tool of only occasional use that provides only a small
 benefit, *and* ends up destroying a much more important tool: 
 compile-time
 checking on a class's members.

the Variant (or whatever) which implements the new operator? If it is constrained to one type, the destruction seems like it would be acceptable. You can't trust much on a Variant at compile time anyway.

The problem is there would be no way to tell at a glance whether a given class uses opDotExp or not. You'd have to go look it up for every class. So, ok, we could solve that by requiring a different syntax for dynamic invokation. But we already have that: just pass a string to a dispatch function.

Then why overloadable operators? Just write a function call and call it a day. Also, while we're at it, let's prefix all function calls with the word "call" so it's clear that a call is going on. (Some language did that after all.) The fact of the matter is you're in this discussion only to reaffirm a preconceived opinion. Instead of reiterating your arguments, it might be of great use to listen to those made by others. Andrei
Apr 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:gsapu0$24ei$2 digitalmars.com...
 Then why overloadable operators? Just write a function call and call it a 
 day.

Overloadable operators and opDotExp both sacrifice code transparency. In that regard, they're the same. But overloadable operators provide non-trivial benefit. From what I've seen, opDotExp doesn't. The only real argument for opDotExp I've seen so far is for things like DB/DDL/haxe.xml.Fast. I've make counter-arguments to that, but I have yet to see those counter-arguments actually refuted. Instead I keep getting a bunch of "use a dynamic lang and you'll see" hand-waving. And, for the record, I have spent a good deal of time using dynamic langs.
 Also, while we're at it, let's prefix all function calls with the word 
 "call" so it's clear that a call is going on. (Some language did that 
 after all.)

Another apples-and-oranges. In C-style languages, getting rid of "call" does not sacrifice code transparency (unlike op overloading and opDotExp) since function calls can be easily identified at a glance by an identifier followed by parens (and calls/declarations can be easily identified by whether or not they're preceded by a type). As a side note, this is one of the reasons I hate D's parens-are-optional-when-there's-no-arguments feature.
 The fact of the matter is you're in this discussion only to reaffirm a 
 preconceived opinion. Instead of reiterating your arguments, it might be 
 of great use to listen to those made by others.

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.
Apr 17 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Apr 17 2009
parent reply Jason House <jason.james.house gmail.com> writes:
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.
Apr 17 2009
parent reply Adam Burton <adz21c googlemail.com> writes:
Jason House wrote:

 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.

Bare with me on this as I am making some assumptions, I don't follow D2 development very closely ... or D as much as I would like these days :-(. What about using something like '->' for dynamic calls instead of '.'? When you see '.' your safe in the knowledge that at a glance you know said method with said signature exists else the compiler will throw a paddy, when you see '->' you know that method call is evaluated at runtime. This has the added benefit that the same class can be used in compile time checking code and runtime. It actually means a case of "it's another operator" instead of something that requires thought as to how it affects the existing use of '.' (which other replies I have seen people suggesting marking the class as dynamic and what not which means more keywords?). '->' becomes an operator whos default behaviour (using a default implementation in Object) just happens to be too search the type for a matching method signature and call it. Consider below (I not looked into variadic functions so its not complete D code ... excuse my ignorance :-)): interface IServerProxy { open(); close(); void opArrow(char[] methodname, v ...) // Maybe put this in interface so we are forced to overload? otherwise IServerProxy looks like it just opens and closes which is a lie and doesn't enforce the overload of dynamic method calls } class ServerProxy : IServerProxy { public: this() { .. create some stuff ... } void opArrow(char[] methodname, v ...) { if (_validDispatches.contains(methodname)) _messagedispatcher.dispatch(methodname, v[0]); // Send a call with a payload else super.opArrow(methodname, v); } void specialmessage(char[] payload) { ... do special processing ... _messagedispatcher.dispatch("specialmessage", payload); // Send a call with a payload } void open() { ... create network connections yada yada .. } void close() { ... close network connections yada yada .. } } void main() { ServerProxy svr = new ServerProxy(); StaticAsMuchAsPoss(svr); DynamicAsMuchAsPoss(svr); AlsoShowBypassNeedToUpcast(svr); } void StaticAsMuchAsPoss(IServerProxy svr) // Plays with the best of both worlds { svr.open(); // Compile time checked valid svr.message("bob"); // Compile time checked invalid!! svr->message("bob"); // run-time checked valid svr->lalala("bob"); // run-time checked invalid!! HOWEVER through the course of the program it may become valid later on ;-) svr.close(); // Compile time checked valid } void DynamicAsMuchAsPoss(IServerProxy svr) { svr->open(); // run-time checked valid svr->kick("someone"); // run-time checked valid svr->close(); // run-time checked valid } void AlsoShowBypassNeedToUpcast(IServerProxy svr) { svr.open(); // Compile time checked valid svr.specialmessage("bob"); // Compile time checked invalid!! svr->specialmessage("bob"); // run-time checked valid svr.close(); // Compile time checked valid } As you can see StaticAsMuchAsPoss mostly makes compile time checked calls, so for the most part your typo's are all checked, apart from when it comes to dispatching messages then we go runtime (in this case its just a case of syntax sugar like the use of most operators, we could just use a stand alone method called dispatch (or add instead of opAdd) and not be dynamic, result is the same however, just it looks a lil nicer and maybe more appropriate?), so your mostly safe. DynamicAsMuchAsPoss is completely managed via the operator, so it's probably slower due to more runtime checks, since even methods that we could use the '.' for compile time checks have to be dynamically invoked, but the result is the same. AlsoShowBypassNeedToUpcast shows through this method you need not upcast to call those pesky derived methods if you don't want too (although generally I would cast as then its just the cast that's the potential danger line as my use of the derived class can be compile-time checked, that's not to say the use of the dynamic call like that is always bad though, I just wouldn't recommend it :-) ). Seems to me that using the above method seems like less hassle, '.' stays as it has always been used and D can have added dynamic abilities ... or even be used similarly to a dynamic language without breaking old code. The only real issue I see is return types. Seems to me the operator (or even opDotExp) needs some sort of built in variant type to handle being able to returning anything (base types, sturcts, classes, union etc...) (which I think would also have opArrow to do the same as Object allowing for things such as bob->callme()->callmesomethingelse() without casting to Object between each call). What do people think?
Apr 18 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Adam Burton wrote:
 Jason House wrote:
 
 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.

not a fan of dynamic typing. Andrei

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.

Bare with me on this as I am making some assumptions, I don't follow D2 development very closely ... or D as much as I would like these days :-(. 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
Apr 18 2009
parent reply Adam Burton <adz21c googlemail.com> writes:
Andrei Alexandrescu wrote:

 Adam Burton wrote:
 Jason House wrote:
 
 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.

not a fan of dynamic typing. Andrei

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.

Bare with me on this as I am making some assumptions, I don't follow D2 development very closely ... or D as much as I would like these days :-(. 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

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.
Apr 18 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Adam Burton wrote:
 Andrei Alexandrescu wrote:
 What about using something like '->' for dynamic calls instead of '.'?

"." I might as well write "bloodyMaryBloodyMaryBloodyMary". Andrei

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. Andrei
Apr 18 2009
next sibling parent Adam Burton <adz21c googlemail.com> writes:
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
 '.'?

"." I might as well write "bloodyMaryBloodyMaryBloodyMary". Andrei

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.


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.

Apr 19 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Mon, 20 Apr 2009 06:54:21 -0400, Denis Koroskin <2korden gmail.com> 
 wrote:
 
 On Mon, 20 Apr 2009 06:09:28 +0400, Steven Schveighoffer 
 <schveiguy yahoo.com> wrote:

 Yes, there are many things that opDotExp can do that opDot or alias 
 this (which is essentially opDot without any code).  Hooking every 
 function call on a type seems to be one of the two killer use cases 
 of this feature (the other being defining a large range of functions 
 from which only a small number need to exist).  But call forwarding 
 seems not to be one of them.  There are better ways to simply forward 
 a call (such as in your variant example).

 I'm pretty convinced that this is a useful feature, I still have 
 qualms about how it's really easy to define a runtime black hole 
 where the compiler happily compiles empty functions that do nothing 
 instead of complaining about calling a function that does not exist.

 Also, I don't think the requirement for this feature needs to be for 
 the arguments to be templated, it should be sufficient to have a 
 single string template argument.  This way, you can overload opDotExp 
 functions via argument lists.

That way you loose type safety of arguments.

No class C { int y; void opDotExp(string fname)(int x) { y = x; } } auto c = new C; c.foo(1); // ok c.foo("hi"); // compile error, no such function. -Steve

Good point. My take is, just have the compiler rewrite a.b(c, d, e) into a.opDot!("b")(c, d, e) and call it a day. After that, the usual language rules enter in action. Andrei
Apr 20 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 Haven't used D2 for much stuff, but does this work?  I remember reading 
 something about partial IFTI, so if you have
 
 opDotExp(string fname, T...) (T args){}
 
 and you call
 
 opDotExp!("b")(c, d, e)
 
 Does it implicitly define T?
 
 -Steve

It should, but there's a bug related to variadics (right now it only works with non-variadics); I submitted it recently. Andrei
Apr 20 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Andrei Alexandrescu wrote:
 Steven Schveighoffer wrote:
 Haven't used D2 for much stuff, but does this work?  I remember 
 reading something about partial IFTI, so if you have

 opDotExp(string fname, T...) (T args){}

 and you call

 opDotExp!("b")(c, d, e)

 Does it implicitly define T?

 -Steve

It should, but there's a bug related to variadics (right now it only works with non-variadics); I submitted it recently.

Found it. Walter seems to get genuinely interested in opDot so vote up, fixing this bug may become important starting 2.030 :o). http://d.puremagic.com/issues/show_bug.cgi?id=2615 Andrei
Apr 20 2009
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Nick Sabalausky, el 17 de abril a las 16:38 me escribiste:
 "Adam D. Ruppe" <destructionator gmail.com> wrote in message 
 news:mailman.1171.1239998473.22690.digitalmars-d puremagic.com...
 On Fri, Apr 17, 2009 at 03:54:47PM -0400, Nick Sabalausky wrote:
 What
 opDotExp is, is a tool of only occasional use that provides only a small
 benefit, *and* ends up destroying a much more important tool: 
 compile-time
 checking on a class's members.

Wouldn't the compile time checking remain the same on any class except the Variant (or whatever) which implements the new operator? If it is constrained to one type, the destruction seems like it would be acceptable. You can't trust much on a Variant at compile time anyway.

The problem is there would be no way to tell at a glance whether a given class uses opDotExp or not. You'd have to go look it up for every class. So, ok, we could solve that by requiring a different syntax for dynamic invokation. But we already have that: just pass a string to a dispatch function.

This is like foreach. In C a for loop is a for loop, you are never calling a bizarre member function when looping. When you see code using foreach, you have to go look the variable definition to see if it's a real array to see what the looping is doing. Do we need foreach? No, it's just syntax sugar. But I think it makes life much more easier... There is a lot of black magic already doing on that makes much harder to see what's really going on with a piece of code (operator overloading?). If you don't like that, you should probably stick to C =) -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05)
Apr 17 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Leandro Lucarella wrote:
 Nick Sabalausky, el 17 de abril a las 16:38 me escribiste:
 "Adam D. Ruppe" <destructionator gmail.com> wrote in message 
 news:mailman.1171.1239998473.22690.digitalmars-d puremagic.com...
 On Fri, Apr 17, 2009 at 03:54:47PM -0400, Nick Sabalausky wrote:
 What
 opDotExp is, is a tool of only occasional use that provides only a small
 benefit, *and* ends up destroying a much more important tool: 
 compile-time
 checking on a class's members.

the Variant (or whatever) which implements the new operator? If it is constrained to one type, the destruction seems like it would be acceptable. You can't trust much on a Variant at compile time anyway.

class uses opDotExp or not. You'd have to go look it up for every class. So, ok, we could solve that by requiring a different syntax for dynamic invokation. But we already have that: just pass a string to a dispatch function.

This is like foreach. In C a for loop is a for loop, you are never calling a bizarre member function when looping. When you see code using foreach, you have to go look the variable definition to see if it's a real array to see what the looping is doing. Do we need foreach? No, it's just syntax sugar. But I think it makes life much more easier... There is a lot of black magic already doing on that makes much harder to see what's really going on with a piece of code (operator overloading?). If you don't like that, you should probably stick to C =)

It's more than just convenience; it's integration. Uniform form allows generic algorithms to operate on a variety of types. Somehow this argument keeps on being ignored in the discussion. Andrei
Apr 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:gsaqe6$25kf$1 digitalmars.com...
 Leandro Lucarella wrote:
 This is like foreach. In C a for loop is a for loop, you are never 
 calling
 a bizarre member function when looping. When you see code using foreach,
 you have to go look the variable definition to see if it's a real array 
 to
 see what the looping is doing.

 Do we need foreach? No, it's just syntax sugar. But I think it makes life
 much more easier...


 There is a lot of black magic already doing on that makes much harder to
 see what's really going on with a piece of code (operator overloading?).
 If you don't like that, you should probably stick to C =)


There a point I keep bringing up that keeps getting ignored: the code-obscuring "black magic" that we already have provides real non-trivial benefits. I have yet to see an equally compelling case for the ability to call a dynamic method without an explicit dispatcher.
 It's more than just convenience; it's integration. Uniform form allows 
 generic algorithms to operate on a variety of types. Somehow this argument 
 keeps on being ignored in the discussion.

That's not ignored at all, in fact I've already addressed it twice before: A reflection API that supports method invokation allows for generic calling of both static and dynamic functions.
Apr 17 2009
parent Leandro Lucarella <llucax gmail.com> writes:
Nick Sabalausky, el 17 de abril a las 17:45 me escribiste:
 There is a lot of black magic already doing on that makes much harder to
 see what's really going on with a piece of code (operator overloading?).
 If you don't like that, you should probably stick to C =)


There a point I keep bringing up that keeps getting ignored: the code-obscuring "black magic" that we already have provides real non-trivial benefits. I have yet to see an equally compelling case for the ability to call a dynamic method without an explicit dispatcher.

What do you call "non-trivial"? Because I can see people seeing foreach a trivial benefit. After all you can allways write the for loop explicitly calling some methods.
 It's more than just convenience; it's integration. Uniform form allows 
 generic algorithms to operate on a variety of types. Somehow this argument 
 keeps on being ignored in the discussion.

That's not ignored at all, in fact I've already addressed it twice before: A reflection API that supports method invokation allows for generic calling of both static and dynamic functions.

The code for that would be so ugly I that nobody will agree to use it... I will be just a non-feature. Are you suggesting writting all generic code using something like traits(invoke, obj, "some_function", args) instead of obj.some_function(args) to be able to be use some dynamic method? -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- - is there a perl version of php?... cause i have cgi-bin access with perl and want to use php scripts.
Apr 17 2009
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"bearophile" <bearophileHUGS lycos.com> wrote in message 
news:gsai34$1p9k$1 digitalmars.com...
 Nick Sabalausky:
 There are people who swear by the ability of adding methods at runtime 
 and
 changing the inheritance hierarchy dynamically. It makes for a very 
 fluid
 environment.

Personally, I've always seen that as extremely sloppy and haphazard.

Adding methods at runtime is named "monkey patching", and it is considered a bad practice even in Python.

Interesting, I didn't know that.
 Usually in such languages such things are less dangerous because the code 
 contains lot of tests anyway.

See, that just sounds to me like the dynamic-ness is just creating extra work for less payoff. I'd rather have my compiler automatically guarantee correctness (when possible) than have to manually create even more tests than I'm already creating and *hope* that they catch all the problems.
 Some people say that a way to remove most of the downsides of monkey 
 patching is to make it scoped, that is the changes (like a method added or 
 replaced) to a class aren't seen globally in the whole program (like from 
 other modules), but only in the scope where such change is done (and its 
 subscopes). I think I have not seen languages where this is doable yet.

That would certainly be better. It sounds very much like extension methods, which I'm a big fan of (but I have yet to see polymorphic extension methods, that would be nice to have). Although I think I would still prefer extension methods (polymorphic or otherwise) be defined/declared statically.
Apr 17 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Nick Sabalausky:
 I'd rather have my compiler automatically guarantee 
 correctness (when possible) than have to manually create even more tests 
 than I'm already creating and *hope* that they catch all the problems.

This is a very old discussion about the pro/cons of dynamic/static typing :-) (Also note that in such discussions what is often missing is a really powerful and flexible static type system, like Haskell one :-) ) Anyway, regarding what you say here, note that writing tests in python can be very fast, for example if you use doctests :-) http://docs.python.org/library/doctest.html In Haskell you have other good solutions as QuickCheck that invent tests for you: http://www.cs.chalmers.se/~rjmh/QuickCheck/ In my D code I write almost as many tests as I write in Python (about 0.8-1.5 lines of tests for 1 line of D code, while in Python I write about 1-2.5 lines of tests for each line of code). The D compiler is able to catch some bugs the PythoVM isn't able to, but such errors are usually easy to find and fix in Python code, and such tests are quick to write. But then you have to write tests for the logic of method/functions/classes, and they require some time to be written in both languages. The end result is that writing tests in Python is usually not as bad as you think. Bye, bearophile
Apr 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"bearophile" <bearophileHUGS lycos.com> wrote in message 
news:gsam1l$1usu$1 digitalmars.com...
 The end result is that writing tests in Python is usually not as bad as 
 you think.

Maybe so, but I don't see any real benefit from it.
Apr 17 2009
parent bearophile <bearophileHUGS lycos.com> writes:
Nick Sabalausky:
 Maybe so, but I don't see any real benefit from it.

Are you asking me suggestions to help you see such benefits? :-) Well, find some tasks typically done in such languages, do some of them for few days, trying to follow the typical ways to solve them, and you will probably see the benefits. Bye, bearophile
Apr 17 2009
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Nick Sabalausky wrote:
 "bearophile" <bearophileHUGS lycos.com> wrote in message 
 news:gsai34$1p9k$1 digitalmars.com...
 Nick Sabalausky:
 There are people who swear by the ability of adding methods at runtime 
 and
 changing the inheritance hierarchy dynamically. It makes for a very 
 fluid
 environment.


a bad practice even in Python.

Interesting, I didn't know that.
 Usually in such languages such things are less dangerous because the code 
 contains lot of tests anyway.

See, that just sounds to me like the dynamic-ness is just creating extra work for less payoff. I'd rather have my compiler automatically guarantee correctness (when possible) than have to manually create even more tests than I'm already creating and *hope* that they catch all the problems.

Assuming that you are testing the logic of your application, you will trivially check things like accessing "legnth" rather than "length" -- under the assumption that these two methods would do different things. You would spend approximately no additional testing effort on opDotExp. This doesn't hold if you are not writing tests.
Apr 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Christopher Wright" <dhasenan gmail.com> wrote in message 
news:gsb05g$2ini$3 digitalmars.com...
 Assuming that you are testing the logic of your application, you will 
 trivially check things like accessing "legnth" rather than "length" -- 
 under the assumption that these two methods would do different things. You 
 would spend approximately no additional testing effort on opDotExp.

 This doesn't hold if you are not writing tests.

I don't think I understand what you're trying to say. With static languages, I have never written, nor would I ever need to write, a test that checks for the behavior when accessing an object's "legnth" instead of "length".
Apr 17 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
Nick Sabalausky wrote:
 "Christopher Wright" <dhasenan gmail.com> wrote in message 
 news:gsb05g$2ini$3 digitalmars.com...
 Assuming that you are testing the logic of your application, you will 
 trivially check things like accessing "legnth" rather than "length" -- 
 under the assumption that these two methods would do different things. You 
 would spend approximately no additional testing effort on opDotExp.

 This doesn't hold if you are not writing tests.

I don't think I understand what you're trying to say. With static languages, I have never written, nor would I ever need to write, a test that checks for the behavior when accessing an object's "legnth" instead of "length".

And let's say your object suddenly gets a "legnth" field because it's from a library and you start using a newer version of the library. Either that field does the same thing -- in which case it's not a bug to use the wrong one -- or it does something different, in which case your code has a logic error caused by a typo. Testing the logic of your code will catch the latter error and not the former. But the former isn't an error, if it has the same result.
Apr 18 2009
parent BCS <none anon.com> writes:
Hello Christopher,

 
 Testing the logic of your code will catch the latter error and not the
 former. But the former isn't an error, if it has the same result.
 

IIRC dynamic language do gobs of TDD/unittests because they have no choice for just this reason. Any other approach and you have no clue what you'll get. Given a choice between an option that lets in static checking and one that forces the use of runtime testing/checking, I'll go for the static choice every time. (sorry if I'm getting OT)
Apr 18 2009
prev sibling next sibling parent Don <nospam nospam.com> writes:
Steven Schveighoffer wrote:
 On Fri, 17 Apr 2009 11:27:57 -0400, davidl <davidl nospam.org> wrote:
 
 鍦 Fri, 17 Apr 2009 23:20:53 +0800锛宒avidl <davidl nospam.org> 鍐欓亾:

 鍦 Fri, 17 Apr 2009 22:24:04 +0800锛孲teven Schveighoffer 
 <schveiguy yahoo.com> 鍐欓亾:

 On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella 
 <llucax gmail.com> wrote:

 I don't fully understand the example though. In writefln((v.qq = 
 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't 
 propagate
 the 5 to the new B()?

I think it translates to opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties. I for one, can't really see a huge benefit, but then again, I don't normally work with dynamic-type langauges. It looks to me like a huge hole that the compiler will ignore bugs that would have been caught if the methods were strongly typed:

Actually this can help ddl project to work more nicely. Consider you can call plugin code directly without static bindings. And I believe delphi COM variant use some similar trick. It allows you call COM object without static bindings. Sometimes it's troublesome to get the static bindings, and you only need one piece of little function(you won't need the whole interface).

DDBI can also benefit from it. Consider the use case: myRow.Col1 = "abc";

Yes, this case does make sense, but I would still probably rather write a statically-typed wrapper. Plus it's not terribly hard to write: myRow.set("Col1", "abc"); I'm not yet convinced, but it remains to be seen if there is some killer functionality that cannot be had without it. -Steve

I believe it is entirely syntax sugar. It's no different to: foo(char [] funcname, T...) (T args){} But it allows you to eliminate the foo!("funcname")(args) with funcname(args). Which is a _very_ significant improvement. BTW, I think it _must_ be done as a template, as Andrei suggested. In the original, you have serious problems with delegates, for example, since the function being called has one more parameter (the function name) than it pretends to have.
Apr 17 2009
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"davidl" <davidl nospam.org> wrote in message 
news:op.usje9ia3j5j59l my-tomato...
 The benefit is you don't need to write the call function,

...But you do have to write the opDotExp() function. How is that less work than just writing a dispatch function?
 you don't need to write the string quote.

I think I'd prefer that. If I put something in quotes, that tells me that typos might not get detected until runtime. But if I don't use quotes, and it compiles, then I know it's ok. With opDotExp, that certainty goes right out the window. All of a sudden I never know if an identifier following a dot compiled because it's ok, or because the error detection has been deferred. I'd feel like I was working in a dynamic language and I *HATE* working with dynamic languages. It's like trying to construct a building on a patch of ground that you know at any moment could change into a lake, sand, cliffside, or simply cease to exist without any warning. Additionally, here's an example from Haxe's xml.Fast: page.node.html.node.head.node.title.x.addChild(Xml.createPCData("Hello")); Think fast without any close inspection: What's the path being used? Umm... Ok, without opDotExp, that would be: page.node("html").node("head").node("title").x.addChild(Xml.createPCData("Hello")); That's a hell of a lot easier to read. Very easy now to see, at a mere glance, the path is "html/head/title". Of course, you could adjust the API for the Haxe/opDotExp version to be more like: page.html.head.title.x.addChild(Xml.createPCData("Hello")); But now (in addition to still not having the certainty of "if an unquoted identifier compiles, it must be ok"), you've opened yourself up to a world of naming collision issues.
Apr 17 2009
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Steven Schveighoffer" <schveiguy yahoo.com> wrote in message 
news:op.usjnzajzeav7ka steves.networkengines.com...
 On Fri, 17 Apr 2009 14:32:07 -0400, Nick Sabalausky <a a.a> wrote:

 My main concern that I've read so far is how if a class has a dynamic 
 method dispatcher that's callable like a method, you can't rely on the 
 compiler to help you typecheck (or spellcheck) the *non-dynamic* methods, 
 because it will just default to sending incorrectly typed data or 
 misspelled methods to the dynamic dispatcher.

That is a *very* good point, that hadn't even occured to me.
 I think dynamic methods  have a very limited use, and probably aren't 
 worth polluting the D  language for a few rare cases.

Agreed.
 When you know the API ahead of time,  you're almost always better off to 
 have statically typed objects.  When  you don't know it ahead of time, 
 well, I prefer the uglyness of seeing the  quoted strings to having the 
 compiler just start trusting everything I do  ;)

Agreed.
Apr 17 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Nick Sabalausky wrote:
 "Steven Schveighoffer" <schveiguy yahoo.com> wrote in message 
 news:op.usjnzajzeav7ka steves.networkengines.com...
 On Fri, 17 Apr 2009 14:32:07 -0400, Nick Sabalausky <a a.a> wrote:

 My main concern that I've read so far is how if a class has a dynamic 
 method dispatcher that's callable like a method, you can't rely on the 
 compiler to help you typecheck (or spellcheck) the *non-dynamic* methods, 
 because it will just default to sending incorrectly typed data or 
 misspelled methods to the dynamic dispatcher.

That is a *very* good point, that hadn't even occured to me.
 I think dynamic methods  have a very limited use, and probably aren't 
 worth polluting the D  language for a few rare cases.

Agreed.
 When you know the API ahead of time,  you're almost always better off to 
 have statically typed objects.  When  you don't know it ahead of time, 
 well, I prefer the uglyness of seeing the  quoted strings to having the 
 compiler just start trusting everything I do  ;)

Agreed.

I think there's merit in binding via strings. It makes for very flexible code that is future-proof, dynamic-linking-friendly, and hot-swappable without recompiling (e.g. you don't need to recompile because you now implement an interface etc.) Reflection is very useful as well. If anything, this agreed-fest shows that the rift between static typing and dynamic typing is alive and well. I've seen many discussions in which people were mystified how anyone gets anything done in a statically-typed OO language. (In fairness, static typing and OO have at best a tense marriage.) But anyway, my point is that it's good to be open-minded. If this conversation does nothing but leave us firmly with our heels in static-land, then we haven't gained anything. If we weren't used to static types we wouldn't be here. I think D can and should allow string lookup for its methods. It's a low-complexity proposition that adds a very interesting tool to D's arsenal. I suggested Walter since a long time ago to support opDot!(string). He implemented the useless version (sigh) which was arguably much simpler and offered a cheap way to experiment. So I'm very happy it's back on the table, and with an implementation to boot. Congratulations David. Andrei
Apr 17 2009
next sibling parent reply Don <nospam nospam.com> writes:
Andrei Alexandrescu wrote:
 Nick Sabalausky wrote:
 "Steven Schveighoffer" <schveiguy yahoo.com> wrote in message 
 news:op.usjnzajzeav7ka steves.networkengines.com...
 On Fri, 17 Apr 2009 14:32:07 -0400, Nick Sabalausky <a a.a> wrote:

 My main concern that I've read so far is how if a class has a dynamic 
 method dispatcher that's callable like a method, you can't rely on 
 the compiler to help you typecheck (or spellcheck) the *non-dynamic* 
 methods, because it will just default to sending incorrectly typed 
 data or misspelled methods to the dynamic dispatcher.

That is a *very* good point, that hadn't even occured to me.
 I think dynamic methods  have a very limited use, and probably aren't 
 worth polluting the D  language for a few rare cases.

Agreed.
 When you know the API ahead of time,  you're almost always better off 
 to have statically typed objects.  When  you don't know it ahead of 
 time, well, I prefer the uglyness of seeing the  quoted strings to 
 having the compiler just start trusting everything I do  ;)

Agreed.

I think there's merit in binding via strings. It makes for very flexible code that is future-proof, dynamic-linking-friendly, and hot-swappable without recompiling (e.g. you don't need to recompile because you now implement an interface etc.)

Does that still apply to the template version you proposed?
Reflection is very useful as well.
 
 If anything, this agreed-fest shows that the rift between static typing 
 and dynamic typing is alive and well. I've seen many discussions in 
 which people were mystified how anyone gets anything done in a 
 statically-typed OO language. (In fairness, static typing and OO have at 
 best a tense marriage.)

 But anyway, my point is that it's good to be open-minded. If this 
 conversation does nothing but leave us firmly with our heels in 
 static-land, then we haven't gained anything. If we weren't used to 
 static types we wouldn't be here. I think D can and should allow string 
 lookup for its methods. It's a low-complexity proposition that adds a 
 very interesting tool to D's arsenal. I suggested Walter since a long 
 time ago to support opDot!(string). He implemented the useless version 
 (sigh) which was arguably much simpler and offered a cheap way to 
 experiment. So I'm very happy it's back on the table, and with an 
 implementation to boot. Congratulations David.
 
 
 Andrei

Apr 17 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Don wrote:
 Andrei Alexandrescu wrote:
 I think there's merit in binding via strings. It makes for very 
 flexible code that is future-proof, dynamic-linking-friendly, and 
 hot-swappable without recompiling (e.g. you don't need to recompile 
 because you now implement an interface etc.) 

Does that still apply to the template version you proposed?

Of course. The template version includes the version that only does dynamic lookup: you can do static, and you can do dynamic. The static part allows you e.g. to optimize some cases if you so wish. With the runtime string you can only do dynamic. Again, making it a template in this case is a win-win thing. There's nothing inferior about it. Andrei
Apr 17 2009
parent Don <nospam nospam.com> writes:
Andrei Alexandrescu wrote:
 Don wrote:
 Andrei Alexandrescu wrote:
 I think there's merit in binding via strings. It makes for very 
 flexible code that is future-proof, dynamic-linking-friendly, and 
 hot-swappable without recompiling (e.g. you don't need to recompile 
 because you now implement an interface etc.) 

Does that still apply to the template version you proposed?

Of course. The template version includes the version that only does dynamic lookup: you can do static, and you can do dynamic. The static part allows you e.g. to optimize some cases if you so wish. With the runtime string you can only do dynamic.

I was thinking that, for example, in a DLL, the DLL needs to include a compiled version of each function name it uses; in the non-template version there are no new compiled version. I haven't thought through the implications of this. I guess that it's really no different to foo!("funcname")(args), though. Makes __traits(membersOf) rather interesting -- not necessarily a pure function, might change with time!
 
 Again, making it a template in this case is a win-win thing. There's 
 nothing inferior about it.

I have no doubt it's better for D. I think it's the only option which is viable in a statically-typed language.
 
 
 Andrei

Apr 17 2009
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Fri, 17 Apr 2009 15:08:12 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 I guess I don't mind the dynamic lookup of methods, but I really don't 
 like the proposal to make it look exactly like a statically typed call.  
 To "hide" the fact that you are doing a dynamic lookup makes me worry 
 about losing the invariants that I come to enjoy with statically typed 
 methods, that is, if I see x.method(a, b, c), it means that the compiler 
 has checked that I called that method correctly with the correctly typed 
 information.

Yah, but that feeling of losing control often marks a paradigm shift (I hate that term, but incidentally it's exactly what I mean here; I guess there is a legit use for it after all). Some people were completely pissed that ++i does not necessarily mean one machine operation, but instead an arbitrary function call. Then STL came and knocked everybody's socks off.
 I use C#'s runtime introspection all the time, and it makes for some 
 really awesome code (things I wish D could do), but I still have to do 
 things like
 
 Type[] argTypes = ...;
 object[] args = ...;
 x.GetType().GetMethod("myMethod", argTypes).Invoke(x, args);
 
 To have that simply cut down to:
 
 x.myMethod(a, b, c);
 
 is a nifty experiment, but now I lost all ability to know how the 
 compiler is interpreting that.  I bet D can do a much better job at 
 runtime type information than C# due to the template system being so 
 powerful, but I still want to know that I'm dynamically doing something 
 versus statically.

I don't think you need to worry. Such a style would belong to a handful of types, e.g. Any. It's not a big deal that what you're doing with a value depends on its type.
 Something like:
 
 void foo(object x)
 {
    x.invoke("myMethod", a, b, c);
 }
 
 where invoke is some method that uses the classinfo of x to look up the 
 method would be freaking awesome ;)

But that doesn't quite unify things. Andrei
Apr 17 2009
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:gsak2p$1s8a$1 digitalmars.com...
 I think there's merit in binding via strings. It makes for very flexible 
 code that is future-proof, dynamic-linking-friendly, and hot-swappable 
 without recompiling (e.g. you don't need to recompile because you now 
 implement an interface etc.) Reflection is very useful as well.

 I think D can and should allow string lookup for its methods. It's a 
 low-complexity proposition that adds a very interesting tool to D's 
 arsenal.

That's a separate issue. I absolutely agree with the usefulness of being able to invoke static methods via a string identifier at runtime. But I think opDotExp is an extremely flawed way to do it. A much better way would be through a reflection mechanism: class Foo { void bar() {} } auto foo = new Foo(); traits(foo).func("bar").invoke(); That way, you can have the benefits of runtime-string-identifier-invocation (and have it on *every* class/method), but without completely loosing compile-time checking on the members of every class which is capable of using it.
 If anything, this agreed-fest shows that the rift between static typing 
 and dynamic typing is alive and well. I've seen many discussions in which 
 people were mystified how anyone gets anything done in a statically-typed 
 OO language.

...Doesn't necessarily mean both sides have valid points. Just that there's disagreement. Outside of diplomacy, it doesn't make any sense to agree with something you disagree with, or disagree with something you agree with, just for the sake a closing a rift. Better to just continue debating the issues with logical arguments. If nothing gets accomplished, well ok, fine, but so what? That's a lot better than coming to hasty-but-wrong agreement just for the sake of agreement. I guess what I'm saying is, people coming to an agreement is all fine and dandy, but the most important thing is to arrive at the *correct* agreement. If one group of people insist that "2+2=0" and another group insists that "2+2=10", then it's far better to keep analyzing and debating until "2+2=4" is discovered and proven than to say "let's all be happy!" and hastily agree that "2+2=5". Again, outside of diplomacy, people disagreeing is not the real problem, the real problem is that the best answer either hasn't been found or hasn't been unquestionably shown to be best.
Apr 17 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Nick Sabalausky wrote:
 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
 news:gsak2p$1s8a$1 digitalmars.com...
 I think there's merit in binding via strings. It makes for very flexible 
 code that is future-proof, dynamic-linking-friendly, and hot-swappable 
 without recompiling (e.g. you don't need to recompile because you now 
 implement an interface etc.) Reflection is very useful as well.

 I think D can and should allow string lookup for its methods. It's a 
 low-complexity proposition that adds a very interesting tool to D's 
 arsenal.

That's a separate issue. I absolutely agree with the usefulness of being able to invoke static methods via a string identifier at runtime. But I think opDotExp is an extremely flawed way to do it. A much better way would be through a reflection mechanism: class Foo { void bar() {} } auto foo = new Foo(); traits(foo).func("bar").invoke(); That way, you can have the benefits of runtime-string-identifier-invocation (and have it on *every* class/method), but without completely loosing compile-time checking on the members of every class which is capable of using it.

I was expecting this objection. I think it's not based because unifying syntax is a major part of adding such a feature. If we get to dynamic invocation but we can't go the last mile, we failed. Look at IDispatch-based programming in C++ vs. interpreted languages.
 If anything, this agreed-fest shows that the rift between static typing 
 and dynamic typing is alive and well. I've seen many discussions in which 
 people were mystified how anyone gets anything done in a statically-typed 
 OO language.

...Doesn't necessarily mean both sides have valid points. Just that there's disagreement. Outside of diplomacy, it doesn't make any sense to agree with something you disagree with, or disagree with something you agree with, just for the sake a closing a rift. Better to just continue debating the issues with logical arguments. If nothing gets accomplished, well ok, fine, but so what? That's a lot better than coming to hasty-but-wrong agreement just for the sake of agreement.

Of course. One thing that suggests the righteousness is not necessarily on your side is that plenty knowledgeable, intelligent, credible people use and advocate dynamic typing. Asserting that they simply miss the point is a bit tenuous.
 I guess what I'm saying is, people coming to an agreement is all fine and 
 dandy, but the most important thing is to arrive at the *correct* agreement. 
 If one group of people insist that "2+2=0" and another group insists that 
 "2+2=10", then it's far better to keep analyzing and debating until "2+2=4" 
 is discovered and proven than to say "let's all be happy!" and hastily agree 
 that "2+2=5". Again, outside of diplomacy, people disagreeing is not the 
 real problem, the real problem is that the best answer either hasn't been 
 found or hasn't been unquestionably shown to be best. 

I'm with you, and in wake of my frequent postings I guess it's pretty known by now that nothing makes me more annoying than knowing 100% I'm right :o). Certainly I'm not one who "agrees to disagree" and I'm glad you aren't either. Andrei
Apr 17 2009
next sibling parent Don <nospam nospam.com> writes:
Andrei Alexandrescu wrote:
 Nick Sabalausky wrote:
 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
 news:gsak2p$1s8a$1 digitalmars.com...
 I think there's merit in binding via strings. It makes for very 
 flexible code that is future-proof, dynamic-linking-friendly, and 
 hot-swappable without recompiling (e.g. you don't need to recompile 
 because you now implement an interface etc.) Reflection is very 
 useful as well.

 I think D can and should allow string lookup for its methods. It's a 
 low-complexity proposition that adds a very interesting tool to D's 
 arsenal.

That's a separate issue. I absolutely agree with the usefulness of being able to invoke static methods via a string identifier at runtime. But I think opDotExp is an extremely flawed way to do it. A much better way would be through a reflection mechanism: class Foo { void bar() {} } auto foo = new Foo(); traits(foo).func("bar").invoke(); That way, you can have the benefits of runtime-string-identifier-invocation (and have it on *every* class/method), but without completely loosing compile-time checking on the members of every class which is capable of using it.

I was expecting this objection. I think it's not based because unifying syntax is a major part of adding such a feature. If we get to dynamic invocation but we can't go the last mile, we failed. Look at IDispatch-based programming in C++ vs. interpreted languages.

I'd even say that this feature is 100% syntax sugar. It doesn't add anything we can't do already (in an ugly way).
Apr 17 2009
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:gsamou$204u$1 digitalmars.com...
 Nick Sabalausky wrote:
 class Foo
 {
     void bar() {}
 }

 auto foo = new Foo();
 traits(foo).func("bar").invoke();

 That way, you can have the benefits of 
 runtime-string-identifier-invocation (and have it on *every* 
 class/method), but without completely loosing compile-time checking on 
 the members of every class which is capable of using it.

I was expecting this objection. I think it's not based because unifying syntax is a major part of adding such a feature. If we get to dynamic invocation but we can't go the last mile, we failed. Look at IDispatch-based programming in C++ vs. interpreted languages.

Can you clarify what you mean here?
 ...Doesn't necessarily mean both sides have valid points. Just that 
 there's disagreement. Outside of diplomacy, it doesn't make any sense to 
 agree with something you disagree with, or disagree with something you 
 agree with, just for the sake a closing a rift. Better to just continue 
 debating the issues with logical arguments. If nothing gets accomplished, 
 well ok, fine, but so what? That's a lot better than coming to 
 hasty-but-wrong agreement just for the sake of agreement.

Of course. One thing that suggests the righteousness is not necessarily on your side is that plenty knowledgeable, intelligent, credible people use and advocate dynamic typing. Asserting that they simply miss the point is a bit tenuous.

No one's infallible no matter how smart or capable (and there's been plenty of examples of supposedy brilliant people doing crazy things like joining wacko cults, etc). I'd be wary of treading so close to [inverse] ad hominem territory. (Besides, aren't there not plenty of "knowledgeable...etc" people on the static-typing side? Like you and Walter? ;) )
 I guess what I'm saying is, people coming to an agreement is all fine and 
 dandy, but the most important thing is to arrive at the *correct* 
 agreement. If one group of people insist that "2+2=0" and another group 
 insists that "2+2=10", then it's far better to keep analyzing and 
 debating until "2+2=4" is discovered and proven than to say "let's all be 
 happy!" and hastily agree that "2+2=5". Again, outside of diplomacy, 
 people disagreeing is not the real problem, the real problem is that the 
 best answer either hasn't been found or hasn't been unquestionably shown 
 to be best.

I'm with you, and in wake of my frequent postings I guess it's pretty known by now that nothing makes me more annoying than knowing 100% I'm right :o). Certainly I'm not one who "agrees to disagree" and I'm glad you aren't either.

:)
Apr 17 2009
prev sibling parent reply Don <nospam nospam.com> writes:
davidl wrote:
 在 Sat, 18 Apr 2009 03:45:43 +0800,Nick Sabalausky <a a.a> 写道:
 
 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message
 news:gsak2p$1s8a$1 digitalmars.com...
 I think there's merit in binding via strings. It makes for very flexible
 code that is future-proof, dynamic-linking-friendly, and hot-swappable
 without recompiling (e.g. you don't need to recompile because you now
 implement an interface etc.) Reflection is very useful as well.

 I think D can and should allow string lookup for its methods. It's a
 low-complexity proposition that adds a very interesting tool to D's
 arsenal.

That's a separate issue. I absolutely agree with the usefulness of being able to invoke static methods via a string identifier at runtime. But I think opDotExp is an extremely flawed way to do it. A much better way would be through a reflection mechanism:

The opDot func can be extremely restrictive by looking up a former added table which are function fingerprints registered by a method call like dynamo.addMethod("myfunc", &myfunc); dynamo.Lenght or dynamo.mymethud would just result runtime exception, because you didn't register these functions.

The problem is a lack of notification at compile time. Runtime exceptions are the problem, not the solution. By the way, if the opDot is a template (per Andrei's suggestion), then if it is marked as nothrow, you have a guarantee that if it compiles, it's valid. nothrow opDot(char [])(args...) doesn't have any of the problems which Nick et. al have mentioned.
Apr 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Don" <nospam nospam.com> wrote in message 
news:gsbovk$nbj$1 digitalmars.com...
 davidl wrote:
 在 Sat, 18 Apr 2009 03:45:43 +0800,Nick Sabalausky <a a.a> 写道:

 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message
 news:gsak2p$1s8a$1 digitalmars.com...
 I think there's merit in binding via strings. It makes for very 
 flexible
 code that is future-proof, dynamic-linking-friendly, and hot-swappable
 without recompiling (e.g. you don't need to recompile because you now
 implement an interface etc.) Reflection is very useful as well.

 I think D can and should allow string lookup for its methods. It's a
 low-complexity proposition that adds a very interesting tool to D's
 arsenal.

That's a separate issue. I absolutely agree with the usefulness of being able to invoke static methods via a string identifier at runtime. But I think opDotExp is an extremely flawed way to do it. A much better way would be through a reflection mechanism:

The opDot func can be extremely restrictive by looking up a former added table which are function fingerprints registered by a method call like dynamo.addMethod("myfunc", &myfunc); dynamo.Lenght or dynamo.mymethud would just result runtime exception, because you didn't register these functions.

The problem is a lack of notification at compile time. Runtime exceptions are the problem, not the solution. By the way, if the opDot is a template (per Andrei's suggestion), then if it is marked as nothrow, you have a guarantee that if it compiles, it's valid. nothrow opDot(char [])(args...) doesn't have any of the problems which Nick et. al have mentioned.

Ok, now *that* is something I can be happy with.
Apr 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Nick Sabalausky" <a a.a> wrote in message 
news:gsbru0$rb6$1 digitalmars.com...
 "Don" <nospam nospam.com> wrote in message 
 news:gsbovk$nbj$1 digitalmars.com...
 The problem is a lack of notification at compile time. Runtime exceptions 
 are the problem, not the solution.

 By the way, if the opDot is a template (per Andrei's suggestion), then if 
 it is marked as nothrow, you have a guarantee that if it compiles, it's 
 valid.
 nothrow opDot(char [])(args...) doesn't have any of the problems which 
 Nick et. al have mentioned.

Ok, now *that* is something I can be happy with.

In fact, now that I think about it, as long as the member name was required to be a template parameter, I'd be perfectly happy with it (and wouldn't even want nothrow to be required). I think that's slightly different from what Andrei's advocated though. Unless I misread something, Andrei, you want it to be possible for the member name to be *either* a template or non-template param, right?
Apr 17 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Nick Sabalausky wrote:
 "Nick Sabalausky" <a a.a> wrote in message 
 news:gsbru0$rb6$1 digitalmars.com...
 "Don" <nospam nospam.com> wrote in message 
 news:gsbovk$nbj$1 digitalmars.com...
 The problem is a lack of notification at compile time. Runtime exceptions 
 are the problem, not the solution.

 By the way, if the opDot is a template (per Andrei's suggestion), then if 
 it is marked as nothrow, you have a guarantee that if it compiles, it's 
 valid.
 nothrow opDot(char [])(args...) doesn't have any of the problems which 
 Nick et. al have mentioned.


In fact, now that I think about it, as long as the member name was required to be a template parameter, I'd be perfectly happy with it (and wouldn't even want nothrow to be required). I think that's slightly different from what Andrei's advocated though. Unless I misread something, Andrei, you want it to be possible for the member name to be *either* a template or non-template param, right?

I think there's a confusion somewhere. Are you sure you know what you don't like? :o) It's simple, really. First off, there's no way you can pass a variable string as a method name with a regular syntax: string foo = "bar"'; Dynamic d; d.foo(); // passes "foo", not "bar" I hope this clarifies that confusion. If you want to invoke a method known as a string variable, opDot or whatever has nothing to do with it. You don't need any change to the language at all, because you'd write: string foo = "bar"; d.call(foo); // call method bar What opDot does is to allow the regular member syntax while still allowing the callee to look up the name statically or dynamically (assuming the method name is passed into opDot as a template parameter). Note, again, that opDot has everything to do with the syntax and next to nothing to do with the semantics, which is realizable without any change in the language. And that's how the cookie crumbles. I understand you don't like that, but at least we should be clear on where the feature starts and where it ends. Andrei
Apr 18 2009
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:gsbv56$13c2$1 digitalmars.com...
 I think there's a confusion somewhere. Are you sure you know what you 
 don't like? :o)

Over here it's 3:30 in the morning at the moment: I don't know a damn thing about anything right now ;) Least of which being why I'm even bothering to make such a completely pointless post or why I'm sitting here reading posts and complaining about the time instead of just going to bed... But I definitely want to read that over later this afternoon, maybe I'll have some better thoughts about the whole thing at that point. I tried to just now, and it's just random letters to me :) I'm sure it'll make perfect sense this afternoon though.
Apr 18 2009
prev sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-04-18 03:23:21 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 If you want to invoke a method known as a string variable, opDot or 
 whatever has nothing to do with it. You don't need any change to the 
 language at all, because you'd write:
 
 string foo = "bar";
 d.call(foo); // call method bar
 
 What opDot does is to allow the regular member syntax while still 
 allowing the callee to look up the name statically or dynamically 
 (assuming the method name is passed into opDot as a template 
 parameter).   Note, again, that opDot has everything to do with the 
 syntax and next to nothing to do with the semantics, which is 
 realizable without any change in the language. And that's how the 
 cookie crumbles. I understand you don't like that, but at least we 
 should be clear on where the feature starts and where it ends.

Andrei, I think you, and perhaps everyone here, are overlooking one small but important detail. opDotExp, if a template like you're adovcating, undermines future runtime dynamic call capabilities (which are part of most runtime reflection systems). If you're going to have something such as d.invoke("foo"); available on any type (using some yet-to-see runtime reflection), it will work for a non-template opDotExp (invoke would just forward to opDotExp with the string "foo" if it doesn't find the member through reflection), but it cannot work for an opDotExp template using "foo" as a template argument since string "foo" is a runtime argument. (In fact, I don't see how any template can be callable from runtime reflection.) It ensues that if later we add runtime reflection to D, dynamic calls won't work for template opDotExp. So I'm not really convinced that template is the way to go, even though it would allow great things. Almost all you can do with a template, you already can do by adding members using a mixin. And adding members using a mixin will also work with runtime reflection, unlike templated opDotExp. The only use case left unadressed by mixins and runtime opDotExp is the function with an infinite number of members which want compile time dispatching. Perhaps we need both template and runtime opDotExp... Anyway, I like the general concept so I hope we'll get it, template or not. I just feel the point above has been neglected in the discussion. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Apr 18 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 On 2009-04-18 03:23:21 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:
 
 If you want to invoke a method known as a string variable, opDot or 
 whatever has nothing to do with it. You don't need any change to the 
 language at all, because you'd write:

 string foo = "bar";
 d.call(foo); // call method bar

 What opDot does is to allow the regular member syntax while still 
 allowing the callee to look up the name statically or dynamically 
 (assuming the method name is passed into opDot as a template 
 parameter).   Note, again, that opDot has everything to do with the 
 syntax and next to nothing to do with the semantics, which is 
 realizable without any change in the language. And that's how the 
 cookie crumbles. I understand you don't like that, but at least we 
 should be clear on where the feature starts and where it ends.

Andrei, I think you, and perhaps everyone here, are overlooking one small but important detail. opDotExp, if a template like you're adovcating, undermines future runtime dynamic call capabilities (which are part of most runtime reflection systems). If you're going to have something such as d.invoke("foo"); available on any type (using some yet-to-see runtime reflection), it will work for a non-template opDotExp (invoke would just forward to opDotExp with the string "foo" if it doesn't find the member through reflection), but it cannot work for an opDotExp template using "foo" as a template argument since string "foo" is a runtime argument. (In fact, I don't see how any template can be callable from runtime reflection.) It ensues that if later we add runtime reflection to D, dynamic calls won't work for template opDotExp. So I'm not really convinced that template is the way to go, even though it would allow great things. Almost all you can do with a template, you already can do by adding members using a mixin. And adding members using a mixin will also work with runtime reflection, unlike templated opDotExp. The only use case left unadressed by mixins and runtime opDotExp is the function with an infinite number of members which want compile time dispatching. Perhaps we need both template and runtime opDotExp... Anyway, I like the general concept so I hope we'll get it, template or not. I just feel the point above has been neglected in the discussion.

I'm confused. Isn't it clear that at the moment we "have" the ability to pass a function name as a runtime string? What we're lacking is the ability to implement that using reflection, but that's an entirely separated discussion! And no, you're not supposed to forward from invoke(string, Variant[]...) to opDotExp - it's precisely the other way around! I'm not sure where I'm wrong in explaining this, it looks like I'm unable to remove a very persisting confusion. So let me restate: opDotExp taking a runtime string does not EXPAND your options, it severely LIMITS them. It's very simple: with the former you have strictly LESS options and no NOTHING in terms of added power. It's simple, I swear. Andrei
Apr 18 2009
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Andrei Alexandrescu wrote:
 Michel Fortin wrote:
 ...

 Andrei, I think you, and perhaps everyone here, are overlooking one
 small but important detail.

 opDotExp, if a template like you're adovcating, undermines future
 runtime dynamic call capabilities (which are part of most runtime
 reflection systems).

 If you're going to have something such as

     d.invoke("foo");

 available on any type (using some yet-to-see runtime reflection), it
 will work for a non-template opDotExp (invoke would just forward to
 opDotExp with the string "foo" if it doesn't find the member through
 reflection), but it cannot work for an opDotExp template using "foo"
 as a template argument since string "foo" is a runtime argument. (In
 fact, I don't see how any template can be callable from runtime
 reflection.)

 It ensues that if later we add runtime reflection to D, dynamic calls
 won't work for template opDotExp.

 So I'm not really convinced that template is the way to go, even
 though it would allow great things. Almost all you can do with a
 template, you already can do by adding members using a mixin. And
 adding members using a mixin will also work with runtime reflection,
 unlike templated opDotExp. The only use case left unadressed by mixins
 and runtime opDotExp is the function with an infinite number of
 members which want compile time dispatching.

 Perhaps we need both template and runtime opDotExp...

 Anyway, I like the general concept so I hope we'll get it, template or
 not. I just feel the point above has been neglected in the discussion.

I'm confused. Isn't it clear that at the moment we "have" the ability to pass a function name as a runtime string? What we're lacking is the ability to implement that using reflection, but that's an entirely separated discussion! And no, you're not supposed to forward from invoke(string, Variant[]...) to opDotExp - it's precisely the other way around! I'm not sure where I'm wrong in explaining this, it looks like I'm unable to remove a very persisting confusion. So let me restate: opDotExp taking a runtime string does not EXPAND your options, it severely LIMITS them. It's very simple: with the former you have strictly LESS options and no NOTHING in terms of added power. It's simple, I swear. Andrei

A related issue is that obj.opDotExp!"foo"(...) cannot be reflected over because template instantiations aren't part of the typeinfo. Which means if you DO have any methods accessed via opDotExp, you CAN'T invoke them dynamically at runtime, but you COULD if it took a string argument instead of a string template argument. Just sayin' :) -- Daniel
Apr 18 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
davidl wrote:
 鍦 Sun, 19 Apr 2009 03:15:02 +0800锛孌aniel Keep 
 <daniel.keep.lists gmail.com> 鍐欓亾:
 
 Andrei Alexandrescu wrote:
 Michel Fortin wrote:
 ...

 Andrei, I think you, and perhaps everyone here, are overlooking one
 small but important detail.

 opDotExp, if a template like you're adovcating, undermines future
 runtime dynamic call capabilities (which are part of most runtime
 reflection systems).

 If you're going to have something such as

     d.invoke("foo");

 available on any type (using some yet-to-see runtime reflection), it
 will work for a non-template opDotExp (invoke would just forward to
 opDotExp with the string "foo" if it doesn't find the member through
 reflection), but it cannot work for an opDotExp template using "foo"
 as a template argument since string "foo" is a runtime argument. (In
 fact, I don't see how any template can be callable from runtime
 reflection.)

 It ensues that if later we add runtime reflection to D, dynamic calls
 won't work for template opDotExp.

 So I'm not really convinced that template is the way to go, even
 though it would allow great things. Almost all you can do with a
 template, you already can do by adding members using a mixin. And
 adding members using a mixin will also work with runtime reflection,
 unlike templated opDotExp. The only use case left unadressed by mixins
 and runtime opDotExp is the function with an infinite number of
 members which want compile time dispatching.

 Perhaps we need both template and runtime opDotExp...

 Anyway, I like the general concept so I hope we'll get it, template or
 not. I just feel the point above has been neglected in the discussion.

I'm confused. Isn't it clear that at the moment we "have" the ability to pass a function name as a runtime string? What we're lacking is the ability to implement that using reflection, but that's an entirely separated discussion! And no, you're not supposed to forward from invoke(string, Variant[]...) to opDotExp - it's precisely the other way around! I'm not sure where I'm wrong in explaining this, it looks like I'm unable to remove a very persisting confusion. So let me restate: opDotExp taking a runtime string does not EXPAND your options, it severely LIMITS them. It's very simple: with the former you have strictly LESS options and no NOTHING in terms of added power. It's simple, I swear. Andrei

A related issue is that obj.opDotExp!"foo"(...) cannot be reflected over because template instantiations aren't part of the typeinfo. Which means if you DO have any methods accessed via opDotExp, you CAN'T invoke them dynamically at runtime, but you COULD if it took a string argument instead of a string template argument.

Umm, actually you can... but with bad duplication in code: void Invoke(methodname, ...) { if (methodname = "compile_time_dispatched_method") // these sort of comparison exists in template opDot in a static manner. { opDot("compile_time_dispatched_method")(args); } } Yes, we get duplication here. I don't have an idea to solve the duplication elegantly yet. Maybe string mixins?
 Just sayin' :)


What you want is: void opDot(string name, T...)(T args) { invoke(name, variantArray(args)); } void invoke(string name, Variant[] args) { ... } So it's precisely the other way around. I agree you _could_ call things the other way, but it goes against the natural way. Andrei
Apr 18 2009
prev sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-04-18 11:19:38 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 I'm confused. Isn't it clear that at the moment we "have" the ability 
 to pass a function name as a runtime string?

Indeed, you can pass the template argument as a runtime argument to another function. No misunderstanding here.
 What we're lacking is the ability to implement that using reflection, 
 but that's an entirely separated discussion!

Runtime reflection lacking indeed, but wether runtime reflection is an entirely separated discussion depends on if what we're doing will get in the way of implementing it.
 And no, you're not supposed to forward from invoke(string, 
 Variant[]...) to opDotExp - it's precisely the other way around!

Wrong. Whether it's one way or another entirely depends on what your goals are. When writing in D and interacting with a scripting language, or remote objects, you can write a function call that will use opDotExp to forward the call to the script object. A template fulfill this goal pretty well. You may want the reverse too: call a D function from a script, or a remote machine, via some runtime reflection mecanism. That's where we have a problem: if the script calls function "foo", and the object implements function "foo" using opDotExp (because the object is itself a proxy to something else for instance), then it just won't work. Also, if, for example, you use a templated opDotExp to implement a bunch of related methods (like those "xyzw", "xwyz", etc.), then you're making those inaccessible from reflection. If on the other hand you do the same using a mixin adding true members to your class or struct, then the compiler can include them in the reflection data, and they'll be accessible to the outside world. So, a runtime opDotExp is superior for some use cases because it works for function calls comming from the outside (via runtime or compile-time reflection), and you may need that if you want to expose some D code via reflection. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Apr 18 2009
next sibling parent Don <nospam nospam.com> writes:
Michel Fortin wrote:
 On 2009-04-18 11:19:38 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:
 
 I'm confused. Isn't it clear that at the moment we "have" the ability 
 to pass a function name as a runtime string?

Indeed, you can pass the template argument as a runtime argument to another function. No misunderstanding here.
 What we're lacking is the ability to implement that using reflection, 
 but that's an entirely separated discussion!

Runtime reflection lacking indeed, but wether runtime reflection is an entirely separated discussion depends on if what we're doing will get in the way of implementing it.
 And no, you're not supposed to forward from invoke(string, 
 Variant[]...) to opDotExp - it's precisely the other way around!

Wrong. Whether it's one way or another entirely depends on what your goals are. When writing in D and interacting with a scripting language, or remote objects, you can write a function call that will use opDotExp to forward the call to the script object. A template fulfill this goal pretty well. You may want the reverse too: call a D function from a script, or a remote machine, via some runtime reflection mecanism. That's where we have a problem: if the script calls function "foo", and the object implements function "foo" using opDotExp (because the object is itself a proxy to something else for instance), then it just won't work. Also, if, for example, you use a templated opDotExp to implement a bunch of related methods (like those "xyzw", "xwyz", etc.), then you're making those inaccessible from reflection. If on the other hand you do the same using a mixin adding true members to your class or struct, then the compiler can include them in the reflection data, and they'll be accessible to the outside world. So, a runtime opDotExp is superior for some use cases because it works for function calls comming from the outside (via runtime or compile-time reflection), and you may need that if you want to expose some D code via reflection.

Is the syntax sugar as important in that direction? I mean, this proposal doesn't make any difference other than to syntax sugar. You can already have b = a.swizzle!("xwyz"); with the exact problems you mention. So it's not as if templated opDotExp creates new problems for function calls from the outside; it just doesn't solve the existing ones. It seems pretty clear that dynamic programming languages and template value arguments don't get on well at all.
Apr 18 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 On 2009-04-18 11:19:38 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:
 
 I'm confused. Isn't it clear that at the moment we "have" the ability 
 to pass a function name as a runtime string?

Indeed, you can pass the template argument as a runtime argument to another function. No misunderstanding here.
 What we're lacking is the ability to implement that using reflection, 
 but that's an entirely separated discussion!

Runtime reflection lacking indeed, but wether runtime reflection is an entirely separated discussion depends on if what we're doing will get in the way of implementing it.
 And no, you're not supposed to forward from invoke(string, 
 Variant[]...) to opDotExp - it's precisely the other way around!

Wrong. Whether it's one way or another entirely depends on what your goals are.

Wrong. It's impossible to pass a dynamic string as a static string, so the street is one way. Andrei
Apr 18 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-04-18 17:48:33 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 Michel Fortin wrote:
 On 2009-04-18 11:19:38 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:
 
 I'm confused. Isn't it clear that at the moment we "have" the ability 
 to pass a function name as a runtime string?

Indeed, you can pass the template argument as a runtime argument to another function. No misunderstanding here.
 What we're lacking is the ability to implement that using reflection, 
 but that's an entirely separated discussion!

Runtime reflection lacking indeed, but wether runtime reflection is an entirely separated discussion depends on if what we're doing will get in the way of implementing it.
 And no, you're not supposed to forward from invoke(string, 
 Variant[]...) to opDotExp - it's precisely the other way around!

Wrong. Whether it's one way or another entirely depends on what your goals are.

Wrong. It's impossible to pass a dynamic string as a static string, so the street is one way.

Indeed it's impossible using a template which requires a static string. And that's exactly the problem: some times you're supposed to make it work both ways, as I've explained in the rest of my post you've cut down. Did you read it? I won't repeat everything, but here's the important part: not having it go both ways *is* an important drawback. And it doesn't go both ways only if opDotExp is a template. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Apr 18 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 On 2009-04-18 17:48:33 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:
 
 Michel Fortin wrote:
 On 2009-04-18 11:19:38 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:

 I'm confused. Isn't it clear that at the moment we "have" the 
 ability to pass a function name as a runtime string?

Indeed, you can pass the template argument as a runtime argument to another function. No misunderstanding here.
 What we're lacking is the ability to implement that using 
 reflection, but that's an entirely separated discussion!

Runtime reflection lacking indeed, but wether runtime reflection is an entirely separated discussion depends on if what we're doing will get in the way of implementing it.
 And no, you're not supposed to forward from invoke(string, 
 Variant[]...) to opDotExp - it's precisely the other way around!

Wrong. Whether it's one way or another entirely depends on what your goals are.

Wrong. It's impossible to pass a dynamic string as a static string, so the street is one way.

Indeed it's impossible using a template which requires a static string. And that's exactly the problem: some times you're supposed to make it work both ways, as I've explained in the rest of my post you've cut down. Did you read it? I won't repeat everything, but here's the important part: not having it go both ways *is* an important drawback. And it doesn't go both ways only if opDotExp is a template.

I did, but sorry, it doesn't make sense and does nothing but continue the terrible confusion going in this thread. 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. Andrei
Apr 18 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
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/
Apr 19 2009
next sibling parent Christopher Wright <dhasenan gmail.com> writes:
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.
Apr 19 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Apr 19 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
Andrei Alexandrescu wrote:
 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.

The utility is when you are looking for methods to invoke via runtime reflection, you can determine that a given function is one of these runtime opDotExp equivalents. I don't know how useful this is, in point of fact. Unless there's a standard system in D for integrating with scripting languages, anyway.
Apr 19 2009
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Christopher Wright wrote:
 Andrei Alexandrescu wrote:
 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.

The utility is when you are looking for methods to invoke via runtime reflection, you can determine that a given function is one of these runtime opDotExp equivalents. I don't know how useful this is, in point of fact. Unless there's a standard system in D for integrating with scripting languages, anyway.

Oddly enough, I got it now :o). Andrei
Apr 19 2009
prev sibling next sibling parent Michel Fortin <michel.fortin michelf.com> writes:
Andrei Alexandrescu wrote:

 Christopher Wright wrote:
 
 Andrei Alexandrescu wrote:
 
 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.

The utility is when you are looking for methods to invoke via runtime reflection, you can determine that a given function is one of these runtime opDotExp equivalents. I don't know how useful this is, in point of fact. Unless there's a standard system in D for integrating with scripting languages, anyway.

Oddly enough, I got it now :o).

Thanks Cristopher. :-) Another interesting use of this, beside integration with scripting languages, would be to replicate the concept of distributed objects in Cocoa. <http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/DistrObjects/Concepts/architecture.html> -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Apr 19 2009
prev sibling parent reply BCS <none anon.com> writes:
Hello Christopher,

 The utility is when you are looking for methods to invoke via runtime
 reflection, you can determine that a given function is one of these
 runtime opDotExp equivalents.

So it is being argued that there should be a standard way to do a run time function invocation system? I'll buy that.
 
 I don't know how useful this is, in point of fact. Unless there's a
 standard system in D for integrating with scripting languages, anyway.
 

Maybe there should be.
Apr 19 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
BCS wrote:
 Hello Christopher,
 
 The utility is when you are looking for methods to invoke via runtime
 reflection, you can determine that a given function is one of these
 runtime opDotExp equivalents.

So it is being argued that there should be a standard way to do a run time function invocation system? I'll buy that.
 I don't know how useful this is, in point of fact. Unless there's a
 standard system in D for integrating with scripting languages, anyway.

Maybe there should be.

But there isn't, so this is YAGNI. And if there were, then this particular issue would immediately be solved.
Apr 20 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-04-20 07:25:43 -0400, Christopher Wright <dhasenan gmail.com> said:

 BCS wrote:
 Hello Christopher,
 
 The utility is when you are looking for methods to invoke via runtime
 reflection, you can determine that a given function is one of these
 runtime opDotExp equivalents.

So it is being argued that there should be a standard way to do a run time function invocation system? I'll buy that.
 I don't know how useful this is, in point of fact. Unless there's a
 standard system in D for integrating with scripting languages, anyway.

Maybe there should be.

But there isn't, so this is YAGNI. And if there were, then this particular issue would immediately be solved.

The thing is that with compile-time reflection you can already build your own runtime-reflection system (with some limitations). -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Apr 20 2009
parent Christopher Wright <dhasenan gmail.com> writes:
Michel Fortin wrote:
 On 2009-04-20 07:25:43 -0400, Christopher Wright <dhasenan gmail.com> said:
 
 BCS wrote:
 Hello Christopher,

 The utility is when you are looking for methods to invoke via runtime
 reflection, you can determine that a given function is one of these
 runtime opDotExp equivalents.

So it is being argued that there should be a standard way to do a run time function invocation system? I'll buy that.
 I don't know how useful this is, in point of fact. Unless there's a
 standard system in D for integrating with scripting languages, anyway.

Maybe there should be.

But there isn't, so this is YAGNI. And if there were, then this particular issue would immediately be solved.

The thing is that with compile-time reflection you can already build your own runtime-reflection system (with some limitations).

You're telling me? http://felt-project.org/reflect/trunk
Apr 20 2009
prev sibling parent BCS <none anon.com> writes:
Hello Michel,

 On 2009-04-18 17:48:33 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> said:
 
 Michel Fortin wrote:
 
 On 2009-04-18 11:19:38 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> said:
 
 I'm confused. Isn't it clear that at the moment we "have" the
 ability to pass a function name as a runtime string?
 

another function. No misunderstanding here.
 What we're lacking is the ability to implement that using
 reflection, but that's an entirely separated discussion!
 

an entirely separated discussion depends on if what we're doing will get in the way of implementing it.
 And no, you're not supposed to forward from invoke(string,
 Variant[]...) to opDotExp - it's precisely the other way around!
 

goals are.

so the street is one way.

string. And that's exactly the problem: some times you're supposed to make it work both ways, as I've explained in the rest of my post you've cut down. Did you read it? I won't repeat everything, but here's the important part: not having it go both ways *is* an important drawback. And it doesn't go both ways only if opDotExp is a template.

If opDotExp is a template you can do: struct S { void NonStaticVersion(char[] name, args...) { ...} void opDotExp(char[] name, T...)(T t) { NonStaticVersion(name, t); } } if it is not a tamplate: struct S { void StaticVersion(char[] name, T ... )(T t) { ...} void opDotExp(char[] name, T...) { StaticVersion!( /******** what goes here?? *********/ )(T); } } you are either wrong, arguing something completely different than what everyone thinks you are or figured out how to do something everyone else thinks is impossible.
Apr 18 2009
prev sibling parent Christopher Wright <dhasenan gmail.com> writes:
Andrei Alexandrescu wrote:
 I think there's merit in binding via strings. It makes for very flexible 
 code that is future-proof, dynamic-linking-friendly, and hot-swappable 
 without recompiling (e.g. you don't need to recompile because you now 
 implement an interface etc.) Reflection is very useful as well.

I don't think you can implement an interface with opDotExp, since it has a different signature.
Apr 17 2009
prev sibling parent reply Don <nospam nospam.com> writes:
Nick Sabalausky wrote:
 "Steven Schveighoffer" <schveiguy yahoo.com> wrote in message 
 news:op.usjnzajzeav7ka steves.networkengines.com...
 On Fri, 17 Apr 2009 14:32:07 -0400, Nick Sabalausky <a a.a> wrote:

 My main concern that I've read so far is how if a class has a dynamic 
 method dispatcher that's callable like a method, you can't rely on the 
 compiler to help you typecheck (or spellcheck) the *non-dynamic* methods, 
 because it will just default to sending incorrectly typed data or 
 misspelled methods to the dynamic dispatcher.

That is a *very* good point, that hadn't even occured to me.

That's the VB/PHP nightmare -- allow any garbage to compile. Yet, if the function name is in a template (which I believe it has to be), the problem isn't as bad: you can detect it in many cases. The only use case I can really see for this is when an extremely large number of functions need to be created. Swizzling an array was one example that was mentioned previously: float4 f, g; g = f.wzyx * f.zzxy; If you were to create all possibilities, it's 4^4 = 256 function overloads. Which is too many to document, but is still feasible to create with a mixin. If it was instead 8^8 functions, it'd really be a lot easier on the poor compiler's symbol table to have the functions generated only on demand. Still, g = f.swizzle!("wzyx"); (or Andrei-style g = f.swizzle!"wzyx"; ) isn't so terrible. D is already incredibly more flexible and powerful than (say) C++, so there's not going to be any use cases which go from terrible to perfect.
 
 I think dynamic methods  have a very limited use, and probably aren't 
 worth polluting the D  language for a few rare cases.

Agreed.

If there were a few use cases which were sufficiently important, it might be possible to create a solution for them which didn't allow so much undesirable behaviour.
 
 When you know the API ahead of time,  you're almost always better off to 
 have statically typed objects.  When  you don't know it ahead of time, 
 well, I prefer the uglyness of seeing the  quoted strings to having the 
 compiler just start trusting everything I do  ;)

Agreed.

want to move in the wrong direction.
Apr 17 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Denis Koroskin wrote:
 On Fri, 17 Apr 2009 23:23:52 +0400, Don <nospam nospam.com> wrote:
 
 Yeah, The ideal language would detect ALL bugs at compile time. We don't  
 want to move in the wrong direction.

I would consider it a "wrong direction". You won't use it *that* frequent, only in those places where it trully fits. For example, a Variant class and scripting languages bindings would greatly benefit for having this functionality.

Yah, for example Variant could define opDotExp to look whether the currently stored type supports the name. With the appropriate compile-time introspection it could make that quite fast too. Pretty awesome if you ask me. I'm very excited about this. Andrei
Apr 17 2009
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Nick Sabalausky wrote:
 "davidl" <davidl nospam.org> wrote in message 
 news:op.usje9ia3j5j59l my-tomato...
 The benefit is you don't need to write the call function,

...But you do have to write the opDotExp() function. How is that less work than just writing a dispatch function?

It's more work, but it lets you do other interesting things. Prime example is one I mentioned earlier. Let's say we also get full RTTI implemented soon (either in a library [I'm working on it] or in the compiler). Then you can write a Variant type that you can call arbitrary methods on. Neat, huh? Let's say you integrate D with a scripting language where you can add methods to an object at arbitrary times. Instead of writing: scriptObj.invokeMethod("methodname", arguments); You can instead write: scriptObj.methodname(arguments); This is a library type which has no business knowing about any methods that you have defined on scriptObj. Moreover, the alternative is to write this: template ScriptMethod(string name) { enum ScriptMethod = "void " ~ name ~ "(...) { return _vm.invokeMethod(_obj, \"" ~ name ~ \"" ~, _arguments, _argptr); }"; } class ScriptObjWrapper { // repeat this a few dozen times for each method mixin (ScriptMethod!("methodname")); } It's syntactic sugar, but it's really neat.
Apr 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Christopher Wright" <dhasenan gmail.com> wrote in message 
news:gsaver$2ini$1 digitalmars.com...
 Nick Sabalausky wrote:
 "davidl" <davidl nospam.org> wrote in message 
 news:op.usje9ia3j5j59l my-tomato...
 The benefit is you don't need to write the call function,

...But you do have to write the opDotExp() function. How is that less work than just writing a dispatch function?

It's more work, but it lets you do other interesting things. Prime example is one I mentioned earlier. Let's say we also get full RTTI implemented soon (either in a library [I'm working on it] or in the compiler). Then you can write a Variant type that you can call arbitrary methods on. Neat, huh? Let's say you integrate D with a scripting language where you can add methods to an object at arbitrary times. Instead of writing: scriptObj.invokeMethod("methodname", arguments); You can instead write: scriptObj.methodname(arguments); This is a library type which has no business knowing about any methods that you have defined on scriptObj. Moreover, the alternative is to write this: template ScriptMethod(string name) { enum ScriptMethod = "void " ~ name ~ "(...) { return _vm.invokeMethod(_obj, \"" ~ name ~ \"" ~, _arguments, _argptr); }"; } class ScriptObjWrapper { // repeat this a few dozen times for each method mixin (ScriptMethod!("methodname")); } It's syntactic sugar, but it's really neat.

Brainfuck and AppleScript are really neat, but I wouldn't want to write an app in either of them ;) But anyway, like I've said before, syntactic sugar is fine, but this is syntactic sugar that undermines the programmer's ability to rely on compile-time checking of class members. "But only for the classes that use it." No, it undermines that trust across the board because it's non-obvious what classes are using it, so you'd have to always be on guard. "Operator overloading blah blah blah" That only causes problems when it's used *and* abused by a class, while this would cause problems *any* time it's used. Etc etc...
Apr 17 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
Nick Sabalausky wrote:
 But anyway, like I've said before, syntactic sugar is fine, but this is 
 syntactic sugar that undermines the programmer's ability to rely on 
 compile-time checking of class members. "But only for the classes that use 
 it." No, it undermines that trust across the board because it's non-obvious 
 what classes are using it, so you'd have to always be on guard. "Operator 
 overloading blah blah blah" That only causes problems when it's used *and* 
 abused by a class, while this would cause problems *any* time it's used. Etc 
 etc...

Compile time checking is not undermined at all, even for classes that use this functionality. opDotExp just makes it easier to create classes with a large, possibly infinite set of members. There are three possible cases: 1. The class does not use opDotExp. Compile time member checking is not undermined. 2. The body of opDotExp triggers an error at compile time if it receives an unexpected name. Compile time member checking is not undermined, although the specific error message may have changed. 3. The body of opDotExp accepts all names. Compile time member checking is not undermined, we just have a class that has every legal identifier as a member. -- Rainer Deyke - rainerd eldwood.com
Apr 17 2009
parent "Nick Sabalausky" <a a.a> writes:
"Rainer Deyke" <rainerd eldwood.com> wrote in message 
news:gsbodm$mjc$1 digitalmars.com...
 Nick Sabalausky wrote:
 But anyway, like I've said before, syntactic sugar is fine, but this is
 syntactic sugar that undermines the programmer's ability to rely on
 compile-time checking of class members. "But only for the classes that 
 use
 it." No, it undermines that trust across the board because it's 
 non-obvious
 what classes are using it, so you'd have to always be on guard. "Operator
 overloading blah blah blah" That only causes problems when it's used 
 *and*
 abused by a class, while this would cause problems *any* time it's used. 
 Etc
 etc...

Compile time checking is not undermined at all, even for classes that use this functionality. opDotExp just makes it easier to create classes with a large, possibly infinite set of members. There are three possible cases: 1. The class does not use opDotExp. Compile time member checking is not undermined. 2. The body of opDotExp triggers an error at compile time if it receives an unexpected name. Compile time member checking is not undermined, although the specific error message may have changed. 3. The body of opDotExp accepts all names. Compile time member checking is not undermined, we just have a class that has every legal identifier as a member.

If the member-name parameter to opDotExp was *required* to be a template paramater, then I agree, and I would have no objections to having that. But it should be pointed out that that would not actually be dynamic invokation, since you wouldn't be able to invoke a member whose name is only known at run-time. (But I would love for that to be possible through a reflection API.)
Apr 17 2009
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Steven Schveighoffer" <schveiguy yahoo.com> wrote in message 
news:op.usjevzhseav7ka steves.networkengines.com...
 On Fri, 17 Apr 2009 11:27:57 -0400, davidl <davidl nospam.org> wrote:
 DDBI can also benefit from it.

 Consider the use case:
 myRow.Col1 = "abc";

Yes, this case does make sense, but I would still probably rather write a statically-typed wrapper. Plus it's not terribly hard to write: myRow.set("Col1", "abc");

I think some sort of paramaterized property would be far better (I *think* we can do these, right?): myRow.col("Col1") = "abc"; myRow.col["Col1"] = "abc";
 I'm not yet convinced, but it remains to be seen if there is some killer 
 functionality that cannot be had without it.

Same here. So far, it just doesn't seem worth the loss of knowing at a glance what is and what might not be verified at compile-time. If I wanted to be throwing compile-time guarantees out the window, I'd go use PHP.
Apr 17 2009
parent reply Leandro Lucarella <llucax gmail.com> writes:
Nick Sabalausky, el 17 de abril a las 14:45 me escribiste:
 I'm not yet convinced, but it remains to be seen if there is some killer 
 functionality that cannot be had without it.

Same here. So far, it just doesn't seem worth the loss of knowing at a glance what is and what might not be verified at compile-time. If I wanted to be throwing compile-time guarantees out the window, I'd go use PHP.

Being able to do it doesn't mean you have to use it always. You don't loose anything if you don't use it =) -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- The diference is simple: hackers build things, crackers break them.
Apr 17 2009
next sibling parent reply Don <nospam nospam.com> writes:
Leandro Lucarella wrote:
 Nick Sabalausky, el 17 de abril a las 14:45 me escribiste:
 I'm not yet convinced, but it remains to be seen if there is some killer 
 functionality that cannot be had without it.

glance what is and what might not be verified at compile-time. If I wanted to be throwing compile-time guarantees out the window, I'd go use PHP.

Being able to do it doesn't mean you have to use it always. You don't loose anything if you don't use it =)

Correction: You don't lose anything if *nobody you interact with* uses it. A feature that is wide open to abuse is dangerous, even if you'll never abuse it yourself. The feature has to be worth using.
Apr 17 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Don wrote:
 Leandro Lucarella wrote:
 Nick Sabalausky, el 17 de abril a las 14:45 me escribiste:
 I'm not yet convinced, but it remains to be seen if there is some 
 killer functionality that cannot be had without it.

a glance what is and what might not be verified at compile-time. If I wanted to be throwing compile-time guarantees out the window, I'd go use PHP.

Being able to do it doesn't mean you have to use it always. You don't loose anything if you don't use it =)

Correction: You don't lose anything if *nobody you interact with* uses it. A feature that is wide open to abuse is dangerous, even if you'll never abuse it yourself. The feature has to be worth using.

On the other hand, using the feature requires some due diligence. It doesn't strike me as a dangerous feature that's also the path of last resistance, like casts. Andrei
Apr 17 2009
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Leandro Lucarella:
 Being able to do it doesn't mean you have to use it always. You don't
 loose anything if you don't use it =)

It adds complexity to the language. More things to know and remember and understand, and so on. Generally adding complexity has a cost. Bye, bearophile
Apr 17 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
bearophile wrote:
 Leandro Lucarella:
 Being able to do it doesn't mean you have to use it always. You don't
 loose anything if you don't use it =)

It adds complexity to the language. More things to know and remember and understand, and so on. Generally adding complexity has a cost. Bye, bearophile

I agree. I think in this case the complexity addition is well worth the benefits. Andrei
Apr 17 2009
prev sibling next sibling parent "Nick Sabalausky" <a a.a> writes:
"davidl" <davidl nospam.org> wrote in message 
news:op.usjfexkpj5j59l my-tomato...
在 Fri, 17 Apr 2009 23:34:37 +0800,Steven Schveighoffer 
<schveiguy yahoo.com> 写道:

 Actually in a lot cases, you don't have the time to write the static 
 wrapper.
 Also for a COM object, you don't want to call it in such ways:

 comobj.callfunc("ComObjFunc", 3,4,5);

 You probably want to call it only by comobj.ComObjFunc(3,4,5);

 Yes static wrapper can solve these all. Problem is not in all cases you 
 want to make a static binding for it.

Solution: Create a dispatch function like callfunc above, then use a mixin to generate static callfunc wrappers for any functions that you already know you want. That way, you can have your trivial-to-create static bindings and sidestep them too. ;)
Apr 17 2009
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Steven Schveighoffer, el 17 de abril a las 11:27 me escribiste:
The problem is you're dealing with the class which is overloaded its opDot. You
know the risk before hand, you are not going to overload for every classes. 
Here, you seem to little bit overrate this feature. :)
If you want things checked, then you probabely need to go back to static. This
dynamic stuff is used for dynamic things only, and as long as you have to do 
it in the dynamic way that means you have no easy way or even impossible to
check it at compiletime and you accept the potential risk like the example you 
posted.

Sure, but what is the reason to need dynamic methods? I'm just trying to understand the usefulness of it.

RPC is an example that comes into mind There is plenty of magic you can do with dynamic methods. Just try a dynamic language and see =) -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Corr铆 muchas carreras, tratando de alcanzarte a vos. Pero corr铆a s贸lo y siempre sal铆 煤ltimo.
Apr 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Leandro Lucarella" <llucax gmail.com> wrote in message 
news:20090417191634.GA15139 homero.springfield.home...
 Steven Schveighoffer, el 17 de abril a las 11:27 me escribiste:
 Sure, but what is the reason to need dynamic methods?  I'm just trying to 
 understand the usefulness of it.

RPC is an example that comes into mind There is plenty of magic you can do with dynamic methods. Just try a dynamic language and see =)

But is there any that can't be done with a dispatch function? Besides, as I see it, the opDotExp-style syntax for calling a dynamic method is more limited because unless you're using a scripting language, you can't do: auto foo = new Foo(); char[] func = /* Get from user input */; foo.func(); // Call function the user specified But you can do that with either a dispatch method or a reflection API that supports invokation.
Apr 17 2009
parent reply Leandro Lucarella <llucax gmail.com> writes:
Nick Sabalausky, el 17 de abril a las 16:48 me escribiste:
 "Leandro Lucarella" <llucax gmail.com> wrote in message 
 news:20090417191634.GA15139 homero.springfield.home...
 Steven Schveighoffer, el 17 de abril a las 11:27 me escribiste:
 Sure, but what is the reason to need dynamic methods?  I'm just trying to 
 understand the usefulness of it.

RPC is an example that comes into mind There is plenty of magic you can do with dynamic methods. Just try a dynamic language and see =)

But is there any that can't be done with a dispatch function?

You can write anything even with brainfuck. There are no discussion about what's possible and what's not, we are just talking about syntax. I hope the "it can be done" argument stop poping out because we all agree that it's possible to do everything right now. The question is *how* you can do it.
 Besides, as I see it, the opDotExp-style syntax for calling a dynamic method 
 is more limited because unless you're using a scripting language, you can't 
 do:
 
 auto foo = new Foo();
 char[] func = /* Get from user input */;
 foo.func(); // Call function the user specified

So?
 But you can do that with either a dispatch method or a reflection API that 
 supports invokation.

I think the reflection API is needed too, but that's another topic. Both would be great. -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Cada movimiento que no se hace, es un movimiento que se pierde. Y cada movimiento que se pierde, se transforma en una mochila. Y las mochilas nos alejan, de nuestros amigos y nuestras amigas. Y nuestros amigos se transforman, en enemigos y en enemigas. Cada movimiento que se hace, es una mochila que se deja.
Apr 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
 "Leandro Lucarella" <llucax gmail.com> wrote in message 
news:20090417231958.GB27625 homero.springfield.home...
 Nick Sabalausky, el 17 de abril a las 16:48 me escribiste:
 "Leandro Lucarella" <llucax gmail.com> wrote in message
 news:20090417191634.GA15139 homero.springfield.home...
 Steven Schveighoffer, el 17 de abril a las 11:27 me escribiste:
 Sure, but what is the reason to need dynamic methods?  I'm just trying 
 to
 understand the usefulness of it.

RPC is an example that comes into mind There is plenty of magic you can do with dynamic methods. Just try a dynamic language and see =)

But is there any that can't be done with a dispatch function?

You can write anything even with brainfuck. There are no discussion about what's possible and what's not, we are just talking about syntax. I hope the "it can be done" argument stop poping out because we all agree that it's possible to do everything right now. The question is *how* you can do it.

Please, please, please, let's not delve into the "everything can be done" argument. It only holds for a limited domain (theoretical turing machines and turing computable problems, ie theoretical computability), which this discussion is already well outside of. You can't rewrite Firefox in brainfuck in 3 hours. You can't write a worthwhile embedded OS entirely in JavaScript. You can't write a gameboy app to compute the movements of all the particles in the universe until the end of time and have it run to completion in three seconds. You can't. You can't. You can't. Can't can't can't can't can't. Of course there are things that can't be done. Obviously we're all well aware that computability has nothing to do with this discussion, so I really don't see why you've brought it up. As you said, the question is how we compute whatever we're computing and how we write the code to do so. And there absolutely are indeed certain how's under certain conditions that *cannot* be done. And you did say "There is plenty of magic you can do with dynamic methods", right? You don't want me to misuse the "it's possible to do everything" argument to respond "If there's plenty of magic you can do with dynamic methods, and everything is possible, then I can do all the same magic without anything dynamic", do you? So please, please, please, let's never, ever, ever, ever, ever get into the "everything can be done" again unless we really are entirely within the bounds of theoretical computability. So now, let's try this again: What is this usefulness you speak of that traditional dynamic methods and/or opDotSrc dynamic methods have that is more useful than a dispatch method? Or, were you already counting dispatch methods as a form of dynamic method?
 Besides, as I see it, the opDotExp-style syntax for calling a dynamic 
 method
 is more limited because unless you're using a scripting language, you 
 can't
 do:

 auto foo = new Foo();
 char[] func = /* Get from user input */;
 foo.func(); // Call function the user specified

So?

I was merely refuting your impliction (or maybe I misunderstood you?) that opDotExp dynamic methods were more useful.
 But you can do that with either a dispatch method or a reflection API 
 that
 supports invokation.

I think the reflection API is needed too, but that's another topic. Both would be great.

Ok, glad we at least agree on the need for the reflection stuff :)
Apr 17 2009
parent reply Leandro Lucarella <llucax gmail.com> writes:
Nick Sabalausky, el 18 de abril a las 02:19 me escribiste:
  "Leandro Lucarella" <llucax gmail.com> wrote in message 
 news:20090417231958.GB27625 homero.springfield.home...
 Nick Sabalausky, el 17 de abril a las 16:48 me escribiste:
 "Leandro Lucarella" <llucax gmail.com> wrote in message
 news:20090417191634.GA15139 homero.springfield.home...
 Steven Schveighoffer, el 17 de abril a las 11:27 me escribiste:
 Sure, but what is the reason to need dynamic methods?  I'm just trying 
 to
 understand the usefulness of it.

RPC is an example that comes into mind There is plenty of magic you can do with dynamic methods. Just try a dynamic language and see =)

But is there any that can't be done with a dispatch function?

You can write anything even with brainfuck. There are no discussion about what's possible and what's not, we are just talking about syntax. I hope the "it can be done" argument stop poping out because we all agree that it's possible to do everything right now. The question is *how* you can do it.

Please, please, please, let's not delve into the "everything can be done" argument. It only holds for a limited domain (theoretical turing machines and turing computable problems, ie theoretical computability), which this discussion is already well outside of. You can't rewrite Firefox in brainfuck in 3 hours. You can't write a worthwhile embedded OS entirely in JavaScript. You can't write a gameboy app to compute the movements of all the particles in the universe until the end of time and have it run to completion in three seconds. You can't. You can't. You can't. Can't can't can't can't can't. Of course there are things that can't be done. Obviously we're all well aware that computability has nothing to do with this discussion, so I really don't see why you've brought it up. As you said, the question is how we compute whatever we're computing and how we write the code to do so. And there absolutely are indeed certain how's under certain conditions that *cannot* be done. And you did say "There is plenty of magic you can do with dynamic methods", right? You don't want me to misuse the "it's possible to do everything" argument to respond "If there's plenty of magic you can do with dynamic methods, and everything is possible, then I can do all the same magic without anything dynamic", do you? So please, please, please, let's never, ever, ever, ever, ever get into the "everything can be done" again unless we really are entirely within the bounds of theoretical computability.

Nice speech.
 So now, let's try this again:
 What is this usefulness you speak of that traditional dynamic methods and/or 
 opDotSrc dynamic methods have that is more useful than a dispatch method? 

Uniform (and better) syntax. It's just about that. -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- JUNTAN FIRMAS Y HUELLAS POR EL CACHORRO CONDENADO A MUERTE... -- Cr贸nica TV
Apr 18 2009
parent reply SandeepK <sandeep.iitkgpNOSPAM gmail.com> writes:
Leandro Lucarella Wrote:

 So now, let's try this again:
 What is this usefulness you speak of that traditional dynamic methods and/or 
 opDotSrc dynamic methods have that is more useful than a dispatch method? 

Uniform (and better) syntax. It's just about that.

I too am having difficulty in understanding the benefit of this particular proposal. If I understand it right, the string essentially is still static and hence known at compile time? In which case I fail to see the advantage of using this opDotSrc method instead of defining a function. Anyways, I have wanted similar functionality for a different purpose. In graphics is common to do swizzle on vector. A float4 vector has xyzw components and some specialized languages like shaders allow you to do shuffle and replicate the components. float4 vec = float4(1.0f,2.0f,3.0f,4.0f); vec = vec.wzyx; //vec is now (4.0f, 3.0f, 2.0f, 1.0f); vec = vec.xxyy; //vec is now (4.0f, 4.0f, 3.0f, 3.0f); There is no good syntax to implement the swizzles in C++. You can make all of them member functions but the number of possibilities is so huge that is ridiculous to do it by hand. With something like this proposal, it should be easy to implement. Also, *if* there was a default opDotExp dispatcher (which automatically dispatches to the regular member functions) wouldn't it be easier to build interceptor classes? Or to implement class specific tracing? All calls to an object go through an interceptor object which can print debug trace information for example. For the default generated opDotExp the compiler can still do static type checking since it knows opDotExp will forward calls to the defined memeber functions.
Apr 18 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
SandeepK:
 I too am having difficulty in understanding the benefit of this particular
proposal.

Read the thread, some of the answers give several use cases.
If I understand it right, the string essentially is still static and hence
known at compile time?<

It can be unknown at compile-time.
 Anyways, I have wanted similar functionality for a different purpose. In
graphics is common to do swizzle on vector. A float4 vector has xyzw components
and some specialized languages like shaders allow you to do shuffle and
replicate the components.
 float4 vec = float4(1.0f,2.0f,3.0f,4.0f);
 vec = vec.wzyx; 

Note that using dynamic access may be a bit slow for that. On the other hand you can do that in D1 creating a small compile-time function that computes the cartesian product to create the strings of the names of those function methods (to be used as attributes), that you can add with a mixin (and a static foreach, if necessary). I have shown such code here time ago, don't ask me to write it again :-) This is a starting point: const string chars = "xyzw"; foreach (x; Range!(4)) foreach (y; Range!(4)) foreach (y; Range!(4)) foreach (w; Range!(4)) chars[x] ~ chars[y] ~ chars[w] ~ chars[w]; Bye, bearophile
Apr 18 2009
parent reply Don <nospam nospam.com> writes:
bearophile wrote:
 SandeepK:
 I too am having difficulty in understanding the benefit of this particular
proposal.

Read the thread, some of the answers give several use cases.
 If I understand it right, the string essentially is still static and hence
known at compile time?<

It can be unknown at compile-time.

How on earth could that happen? It's always known at compile time.
 
 
 Anyways, I have wanted similar functionality for a different purpose. In
graphics is common to do swizzle on vector. A float4 vector has xyzw components
and some specialized languages like shaders allow you to do shuffle and
replicate the components.
 float4 vec = float4(1.0f,2.0f,3.0f,4.0f);
 vec = vec.wzyx; 

Note that using dynamic access may be a bit slow for that.

I don't see _anything_ dynamic in this proposal, actually.
 
 On the other hand you can do that in D1 creating a small compile-time function
that computes the cartesian product to create the strings of the names of those
function methods (to be used as attributes), that you can add with a mixin (and
a static foreach, if necessary).
 I have shown such code here time ago, don't ask me to write it again :-) This
is a starting point:

Yes, but the syntax isn't quite as nice as with this proposal.
Apr 18 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Don wrote:
 I don't see _anything_ dynamic in this proposal, actually.

Yes. The amount of confusion in this thread is staggering. I think the right question to ask is, "what does opDotExp bring, that wasn't there before?" Is it reflection of any kind? No. Is it the ability to encode operation names as strings, be they static or dynamic? No. Is it the ability to syntactically replace a statically-provided method name with an equally statically-known string? That's pretty much it. Andrei
Apr 18 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu:
 Yes. The amount of confusion in this thread is staggering.

I think I have misunderstood about the whole thread then. If the string isn't determined at run time, then this thing isn't useful for my purposes, and it's not close to the object-C as I was talking about, and it's not similar to __getattr__ of Python, etc. Bye, bearophile
Apr 18 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
bearophile wrote:
 Andrei Alexandrescu:
 Yes. The amount of confusion in this thread is staggering.

I think I have misunderstood about the whole thread then. If the string isn't determined at run time, then this thing isn't useful for my purposes, and it's not close to the object-C as I was talking about, and it's not similar to __getattr__ of Python, etc. Bye, bearophile

In the syntax a.b how would either of a and b be identified at runtime? I mean, you write the code somewhere and it gets compiled. It's not like you're reading "a.b" from the console and then call some eval() function against them. Andrei
Apr 18 2009
next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 18/04/2009 21:16, Andrei Alexandrescu wrote:
 bearophile wrote:
 Andrei Alexandrescu:
 Yes. The amount of confusion in this thread is staggering.

I think I have misunderstood about the whole thread then. If the string isn't determined at run time, then this thing isn't useful for my purposes, and it's not close to the object-C as I was talking about, and it's not similar to __getattr__ of Python, etc. Bye, bearophile

In the syntax a.b how would either of a and b be identified at runtime? I mean, you write the code somewhere and it gets compiled. It's not like you're reading "a.b" from the console and then call some eval() function against them. Andrei

what prevents D from having an eval function? suppose someone modifies the DMD front-end to compile a string with the source code of a function in-memory, than this is processed by something based on DDL and what you get is an API call that takes source code in a string and returns a function pointer.
Apr 18 2009
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Yigal Chripun wrote:
 On 18/04/2009 21:16, Andrei Alexandrescu wrote:
 bearophile wrote:
 Andrei Alexandrescu:
 Yes. The amount of confusion in this thread is staggering.

I think I have misunderstood about the whole thread then. If the string isn't determined at run time, then this thing isn't useful for my purposes, and it's not close to the object-C as I was talking about, and it's not similar to __getattr__ of Python, etc. Bye, bearophile

In the syntax a.b how would either of a and b be identified at runtime? I mean, you write the code somewhere and it gets compiled. It's not like you're reading "a.b" from the console and then call some eval() function against them. Andrei

what prevents D from having an eval function?

Sure it is possible. It has zero relevance to the topic at hand. Andrei
Apr 18 2009
prev sibling parent reply BCS <none anon.com> writes:
Hello Yigal,

 On 18/04/2009 21:16, Andrei Alexandrescu wrote:
 
 In the syntax
 
 a.b
 
 how would either of a and b be identified at runtime? I mean, you
 write the code somewhere and it gets compiled. It's not like you're
 reading "a.b" from the console and then call some eval() function
 against them.
 
 Andrei
 

suppose someone modifies the DMD front-end to compile a string with the source code of a function in-memory, than this is processed by something based on DDL and what you get is an API call that takes source code in a string and returns a function pointer.

Even then it is *still* going to be compile time. Just a compile time running at runtime... Ooohh, my heads's going to start hearing here in a bit.
Apr 18 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 19/04/2009 01:22, BCS wrote:
 Hello Yigal,

 On 18/04/2009 21:16, Andrei Alexandrescu wrote:

 In the syntax

 a.b

 how would either of a and b be identified at runtime? I mean, you
 write the code somewhere and it gets compiled. It's not like you're
 reading "a.b" from the console and then call some eval() function
 against them.

 Andrei

suppose someone modifies the DMD front-end to compile a string with the source code of a function in-memory, than this is processed by something based on DDL and what you get is an API call that takes source code in a string and returns a function pointer.

Even then it is *still* going to be compile time. Just a compile time running at runtime... Ooohh, my heads's going to start hearing here in a bit.

*runtime* string and get back a pointer to a function that you can call. in fact Some Lisp compilers implement eval() exactly like that. you're thinking in C++ terms. try thinking in Lisp...
Apr 18 2009
parent reply BCS <none anon.com> writes:
Hello Yigal,

 On 19/04/2009 01:22, BCS wrote:
 
 Hello Yigal,
 
 On 18/04/2009 21:16, Andrei Alexandrescu wrote:
 
 In the syntax
 
 a.b
 
 how would either of a and b be identified at runtime? I mean, you
 write the code somewhere and it gets compiled. It's not like you're
 reading "a.b" from the console and then call some eval() function
 against them.
 
 Andrei
 

suppose someone modifies the DMD front-end to compile a string with the source code of a function in-memory, than this is processed by something based on DDL and what you get is an API call that takes source code in a string and returns a function pointer.

running at runtime... Ooohh, my heads's going to start hearing here in a bit.

*runtime* string and get back a pointer to a function that you can call.

That is exactly what I was thinking of and something I have wanted for some time. But for that to work you need to *compile* the string to a function so there is still a compile time inside that function call. That library function you refer to, for the topic at hand, is no different than DMD and the normal compile process. To give a concrete example: suppose the eval function was done by copying the string to a file, tacking in includes as needed, shelling out to DMD to make a DLL/SO and then loading that file back into the same process. In that case there would could be a compile time resolution. In what way would puling the whole thing inside the first process be different?
 in fact Some Lisp compilers implement eval() exactly like that.

The only way I can see that making a difference is if you want that eval function to be able to take code that calls function that don't exist yet and I don't even want to think about what it would take to implement that.
 you're thinking in C++ terms. try thinking in Lisp...

(BTW I may have done more Lisp/schema than C++, saying more about by C++ history than anything else.)
Apr 19 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 19/04/2009 23:33, BCS wrote:
 Hello Yigal,

 On 19/04/2009 01:22, BCS wrote:

 Hello Yigal,

 On 18/04/2009 21:16, Andrei Alexandrescu wrote:

 In the syntax

 a.b

 how would either of a and b be identified at runtime? I mean, you
 write the code somewhere and it gets compiled. It's not like you're
 reading "a.b" from the console and then call some eval() function
 against them.

 Andrei

suppose someone modifies the DMD front-end to compile a string with the source code of a function in-memory, than this is processed by something based on DDL and what you get is an API call that takes source code in a string and returns a function pointer.

running at runtime... Ooohh, my heads's going to start hearing here in a bit.

*runtime* string and get back a pointer to a function that you can call.

That is exactly what I was thinking of and something I have wanted for some time. But for that to work you need to *compile* the string to a function so there is still a compile time inside that function call. That library function you refer to, for the topic at hand, is no different than DMD and the normal compile process. To give a concrete example: suppose the eval function was done by copying the string to a file, tacking in includes as needed, shelling out to DMD to make a DLL/SO and then loading that file back into the same process. In that case there would could be a compile time resolution. In what way would puling the whole thing inside the first process be different?

the code is getting compiled. But in the above scheme there isn't any real difference between run-time and compile-time and this distinction has lost its meaning. compare the following: process A: 1) use runtime dispatch based on run-time user input 2) generate source code for the above case "on the fly" 2) compile the above code and call it process B: 1) generete templated source code at runtime 2) call run-time compiler module on the above code 3) compiler instantiates the template based on the input at *compile-time* (i.e. compile time dispatch) 4) call the templated instance the above are identical even if implemented differently since that compile-time above is actually part of the run-time. the only difference is the style of coding and I'd argue that the second process is unnecessarily more complex without any gains. if nothing else than the source code generated will be shorter since you wouldn't need to stick "static" everywhere.
 in fact Some Lisp compilers implement eval() exactly like that.

The only way I can see that making a difference is if you want that eval function to be able to take code that calls function that don't exist yet and I don't even want to think about what it would take to implement that.
 you're thinking in C++ terms. try thinking in Lisp...

(BTW I may have done more Lisp/schema than C++, saying more about by C++ history than anything else.)

Apr 19 2009
parent reply BCS <none anon.com> writes:
Hello Yigal,

 everything you said is true. there is some sort of a compile-time
 since
 the code is getting compiled. But in the above scheme there isn't any
 real difference between run-time and compile-time and this distinction
 has lost its meaning.
 compare the following:
 process A:
 1) use runtime dispatch based on run-time user input
 2) generate source code for the above case "on the fly"
 2) compile the above code and call it
 process B:
 1) generete templated source code at runtime
 2) call run-time compiler module on the above code
 3) compiler instantiates the template based on the input at
 *compile-time* (i.e. compile time dispatch)
 4) call the templated instance
 the above are identical even if implemented differently since that
 compile-time above is actually part of the run-time.
 the only difference is the style of coding and I'd argue that the
 second
 process is unnecessarily more complex without any gains.
 if nothing else than the source code generated will be shorter since
 you
 wouldn't need to stick "static" everywhere.

I think you understand my point about what is possible but I'm not sure you are spotting what I'm saying regarding what should be done. I'll try and cleanly state what I'm asserting: a number of red hearings seem to be floating about based on cool uses for truly runtime dynamic function lookup (that we just agreed doesn't exist for the given syntax as for that case, the binding happens at sort sort of compile time). I'm calling them red herrings because those uses can be done with other functionalties that already exist. If we throw those out, the only new functionality remanding that any of this thread has provided is allowing the following syntax: a.b(args); to be used when the type of 'a' doesn't define a 'b'. What a lot of debate seems to be about is if that should be translated to: a.opDotExp("b", args); or a.opDotExp!("b", typeof(args))(args); but that's an easy question (go with option 2) if you note that the transformation is always at /some sort/ of compile time. If you want this and the non-static (a.k.a. the non compile time binding (where you thinking of static like static member and not static if/assert?)) form you can implement the non-static version using existing devices and make the above a trivial shell around. Trying to go the other way doesn't work as there is no way to make static decisions (static if or template specialization) in a purely runtime mode using runtime values. [ot] the annoying thing about this whole thread is that I'd bet a small amount of money that I could convince anyone here of my point in about 5 minuets if I had me, them and a chalkboard in the same room. I think almost all the confusion is not because people would disagree with what I'm trying to say but because there are about 5 different and almost unconnected things people are arguing about: a sort of "A is true"/"No, B is true" where A != B and A != !B and !A != B ... Written language has the advantage that you can be very precise and thought out but has zero real time feedback like you get in a face-to-face and an insanely long delay for what feedback it does have.
Apr 19 2009
next sibling parent BCS <none anon.com> writes:
Hello BCS,

That didn't sound like I intended it to so...

Clarification: I think most of us could convince most of the rest of us of 
the our point given face time because I don't think there are near as many 
opposing views as it seems. (That is with points of operation, not with issues 
of syntax and or inclusion of features for which there will never be agreement 
;)

 [ot] the annoying thing about this whole thread is that I'd bet a
 small amount of money that I could convince anyone here of my point in
 about 5 minuets if I had me, them and a chalkboard in the same room.

About 3 minutes me to figure out what they are arguing and about 2 minutes to show how what I'm arguing is orthogonal to it.
 I think almost all the confusion is not because people would disagree
 with what I'm trying to say but because there are about 5 different
 and almost unconnected things people are arguing about: a sort of "A
 is true"/"No, B is true" where A != B and A != !B and !A != B ...
 
 Written language has the advantage that you can be very precise and
 thought out but has zero real time feedback like you get in a
 face-to-face and an insanely long delay for what feedback it does
 have.
 

Apr 19 2009
prev sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 20/04/2009 01:13, BCS wrote:
 Hello Yigal,

 everything you said is true. there is some sort of a compile-time
 since
 the code is getting compiled. But in the above scheme there isn't any
 real difference between run-time and compile-time and this distinction
 has lost its meaning.
 compare the following:
 process A:
 1) use runtime dispatch based on run-time user input
 2) generate source code for the above case "on the fly"
 2) compile the above code and call it
 process B:
 1) generete templated source code at runtime
 2) call run-time compiler module on the above code
 3) compiler instantiates the template based on the input at
 *compile-time* (i.e. compile time dispatch)
 4) call the templated instance
 the above are identical even if implemented differently since that
 compile-time above is actually part of the run-time.
 the only difference is the style of coding and I'd argue that the
 second
 process is unnecessarily more complex without any gains.
 if nothing else than the source code generated will be shorter since
 you
 wouldn't need to stick "static" everywhere.

I think you understand my point about what is possible but I'm not sure you are spotting what I'm saying regarding what should be done. I'll try and cleanly state what I'm asserting: a number of red hearings seem to be floating about based on cool uses for truly runtime dynamic function lookup (that we just agreed doesn't exist for the given syntax as for that case, the binding happens at sort sort of compile time). I'm calling them red herrings because those uses can be done with other functionalties that already exist. If we throw those out, the only new functionality remanding that any of this thread has provided is allowing the following syntax: a.b(args); to be used when the type of 'a' doesn't define a 'b'. What a lot of debate seems to be about is if that should be translated to: a.opDotExp("b", args); or a.opDotExp!("b", typeof(args))(args); but that's an easy question (go with option 2) if you note that the transformation is always at /some sort/ of compile time. If you want this and the non-static (a.k.a. the non compile time binding (where you thinking of static like static member and not static if/assert?)) form you can implement the non-static version using existing devices and make the above a trivial shell around. Trying to go the other way doesn't work as there is no way to make static decisions (static if or template specialization) in a purely runtime mode using runtime values.

I was meaning static as in "static if". I agree with what you've written here. I think my point in this sub-thread is a bit side-tracked from the main topic. again, what you said is correct, but since in our example we are discussing compile-time as part of run-time, that means there's no difference between static if and regular if (both are executed at run-time). therefore, you can simplify code by re-factoring it to be regular run-time instead of complicated compile-time that is compiled at run-time. you gain nothing by using compile-time techniques here. in other words, if I'm generating code at *run-time*, there's no point in providing a *compile-time* trivial shell as you mention above. I think my main point was to answer Andrei's post - that an eval() function in a compiled language is possible and that nothing about a compiled language implies that we shouldn't be able to have this.
Apr 19 2009
parent BCS <none anon.com> writes:
Hello Yigal,

 I was meaning static as in "static if".
 I agree with what you've written here. I think my point in this
 sub-thread is a bit side-tracked from the main topic.

there seems to be a lot of that in this thread
 again, what you said is correct, but since in our example we are
 discussing compile-time as part of run-time, that means there's no
 difference between static if and regular if (both are executed at
 run-time).

But one is interpreted and the other manifests as machine code, one can happen before the types of variable are resolved the other after, one only evaluates once during the runtime-compiletime and the other runs every time the function is actually called. Seems like a big enough difference to me.
 therefore, you can simplify code by re-factoring it to be regular
 run-time instead of complicated compile-time that is compiled at
 run-time. you gain nothing by using compile-time techniques here.

Bare in mind that I'm a compile time techniques junky but I think there is a LOT to be gained (if not in what can be done, it's already Turing compleat after all, then in how and how fast things can be done)
 in other words, if I'm generating code at *run-time*, there's no point
 in providing a *compile-time* trivial shell as you mention above.
 

I see what your saying, but based on that you can ague that given a pre compile time code generator (lex/yacc?) compile time code generation (Boost::spirit, my dparse or templates in general) is pointless. Using compile time stuff in runtime generated code could be handy where it makes the generation of the code simpler (think swaths of boilerplate)
 I think my main point was to answer Andrei's post - that an eval()
 function in a compiled language is possible and that nothing about a
 compiled language implies that we shouldn't be able to have this.

My point is that there is no reason that having eval should make a difference in how opDotExp works because there is something akin to a compile time between text and evaluation anyway.
Apr 20 2009
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
davidl wrote:
 鍦 Sun, 19 Apr 2009 02:16:30 +0800锛孉ndrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> 鍐欓亾:
 
 bearophile wrote:
 Andrei Alexandrescu:
 Yes. The amount of confusion in this thread is staggering.

string isn't determined at run time, then this thing isn't useful for my purposes, and it's not close to the object-C as I was talking about, and it's not similar to __getattr__ of Python, etc. Bye, bearophile

In the syntax a.b how would either of a and b be identified at runtime? I mean, you write the code somewhere and it gets compiled. It's not like you're reading "a.b" from the console and then call some eval() function against them.

Depends on the compiler semantic. The compiler can feed the opDot with the variable b if it's a string. The compiler can intepret the opDot that way.

I agree it "can". I don't agree it "can if it's not out of its mind". That lookup method is extremely unhygienic. Andrei
Apr 18 2009
prev sibling parent Christopher Wright <dhasenan gmail.com> writes:
bearophile wrote:
 Andrei Alexandrescu:
 Yes. The amount of confusion in this thread is staggering.

I think I have misunderstood about the whole thread then. If the string isn't determined at run time, then this thing isn't useful for my purposes, and it's not close to the object-C as I was talking about, and it's not similar to __getattr__ of Python, etc. Bye, bearophile

There are two issues here: - The ability to invoke a method where you did not explicitly create a method with that name. - A standardized way of looking up a method to invoke based on a string, where that string may be created at runtime. You can already do this, but there's no standard interface for it. If there were an interface somewhere, like IDispatch { Variant invoke(string, ...); }, that would be nice. Though it doesn't work with structs. In the former case, the method will not exist unless you call it -- for example, swizzling. This is powerful; it means you can use templates to generate the function body. The latter case doesn't give you very much. I'd like to see it in the standard library, but it wouldn't be called before issuing a MethodMissingException or the like because it isn't applicable.
Apr 18 2009
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella <llucax gmail.com>  
wrote:

 I don't fully understand the example though. In writefln((v.qq = 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't propagate
 the 5 to the new B()?

I think it translates to opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties. I for one, can't really see a huge benefit, but then again, I don't normally work with dynamic-type langauges. It looks to me like a huge hole that the compiler will ignore bugs that would have been caught if the methods were strongly typed: class c { void opDotExp(char[] methodname,...) { if(methodname == "mymethod") callMyMethod(); else throw new Exception("bad method name: " ~ methodname); } } void foo(c myc, bool rarelySetToTrue) { if(rarelySetToTrue) myc.mymethud(); // compiles, will throw runtime exception } Also, how do you overload the return value? Using this proposal, you can't have different dynamic methods that return different types. -Steve
Apr 17 2009
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Fri, Apr 17, 2009 at 03:54:47PM -0400, Nick Sabalausky wrote:
 What 
 opDotExp is, is a tool of only occasional use that provides only a small 
 benefit, *and* ends up destroying a much more important tool: compile-time 
 checking on a class's members.

Wouldn't the compile time checking remain the same on any class except the Variant (or whatever) which implements the new operator? If it is constrained to one type, the destruction seems like it would be acceptable. You can't trust much on a Variant at compile time anyway. -- Adam D. Ruppe http://arsdnet.net
Apr 17 2009
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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. The obvious downside is duplication of features, but this might be able to please everyone.
 
 Andrei

-- Adam D. Ruppe http://arsdnet.net
Apr 18 2009
parent reply BCS <none anon.com> writes:
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.
 

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.
Apr 18 2009
parent reply Adam Burton <adz21c googlemail.com> writes:
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.
 

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.

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 

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.
Apr 19 2009
next sibling parent Adam Burton <adz21c googlemail.com> writes:
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.
 

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.

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

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
Apr 19 2009
prev sibling parent reply BCS <none anon.com> writes:
Hello Adam,

 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.
 

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.

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

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.

I see your point but disagree. I see it more as a "what to do if the code author doesn't have full API knowledge?" problem. Templates allow the author of the consuming code to go with a "I'll just assume this can be done and let the compiler check it" approach and the dynamic option allows the author of the producer to go with a "Do whatever you want and if I didn't say what to do with it use this code to figure it out" approach. (In the above, you seeme to be working with the assumption of the non static opDotExp form. I, BTW, see no use for it as it adds no new functionality to D where as the static opDotExp(char[],T...)(T t) form adds a new ability) [ the rest of the post dealt with implications of non-static forms of opDotExp ]
Apr 19 2009
parent reply Adam Burton <adz21c googlemail.com> writes:
BCS wrote:

 Hello Adam,
 
 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.
 

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.

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

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.

I see your point but disagree. I see it more as a "what to do if the code author doesn't have full API knowledge?" problem. Templates allow the author of the consuming code to go with a "I'll just assume this can be done and let the compiler check it" approach and the dynamic option allows the author of the producer to go with a "Do whatever you want and if I didn't say what to do with it use this code to figure it out" approach.

you add the dynamic calls inside the template you lose some of the compile- time checking (and as mentioned in another post how would you use interface constraint with that?) and therefore your template ends up relying on "Do whatever you want and if I didn't say what to do with it use this code to figure it out" for some portions of the code, so you get a weird hybrid that has stepped on templates more than it has dynamic.
 (In the above, you seeme to be working with the assumption of the non
 static opDotExp form. I, BTW, see no use for it as it adds no new
 functionality to D where as the static opDotExp(char[],T...)(T t) form
 adds a new ability)

where someone writes a "struct Wrapper(T)" in just a few lines of code to wrap a type and add logging (I been bit busy to read rest of the threads so I only been replying to stuff following the trail leading up to my post for the most part, till now)? If so then yea that does look nice and seems like something I would use. It also doesn't contain the holes my proposal is attempting to solve since the function name is still evaluated at compile time, so removing 'open' from 'ServerProxy' wouldn't magically turn into a runtime error as the static assert will kick in. I also agree that the none static version of opDotExp doesn't bring new functionality to the table that is worth having. Personally in them cases I would prefer to use dispatch(method, params) directly, it feels less obscure, non-static opDotExp just seems like a bit of syntax sugar with more issues than it is worth. My concern was if that functionality was implemented then using '.' for the dynamic runtime calls means is it would cause more problems than it is worth, so my proposal isn't a proposal to add the feature (since I doubt I would use it) it's one that says if you are going to then I think this method of implementing it is less intrusive.
 [ the rest of the post dealt with implications of non-static forms of
 [ opDotExp

so big with lots of sub-threads I am a little confused as to whether people are or whether they have settled to just non-static. Also for me the use of the word dynamic from the beginning and the subject of the thread meant runtime so I wouldn't really consider static opDotExp dynamic, just clever compile-time trickery :-P so talk about that has gone off topic from the subject imo) and if that is the case then the proposal still stands as a method of implementing it. If however people are not arguing for it and have settled on static opDotExp then I quite gladdly remove the proposal :-). Adam
Apr 19 2009
parent BCS <none anon.com> writes:
Hello Adam,

 BCS wrote:
 
 (In the above, you seeme to be working with the assumption of the non
 static opDotExp form. I, BTW, see no use for it as it adds no new
 functionality to D where as the static opDotExp(char[],T...)(T t)
 form adds a new ability)
 

example where someone writes a "struct Wrapper(T)" in just a few lines of code to wrap a type and add logging (I been bit busy to read rest of the threads so I only been replying to stuff following the trail leading up to my post for the most part, till now)? If so then yea that does look nice and seems like something I would use. It also doesn't contain the holes my proposal is attempting to solve since the function name is still evaluated at compile time, so removing 'open' from 'ServerProxy' wouldn't magically turn into a runtime error as the static assert will kick in.

OK, it seems that we agree on the one point I'm still interested in running with. I must have been reading the rest of that from the wrong viewpoint. I'll leave it at that.
Apr 19 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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 '.'?

"." I might as well write "bloodyMaryBloodyMaryBloodyMary". Andrei

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 can only see usages of either when you want to bind to dynamic data (e.g. fields in a database row) or when you want to define a vast number of functions where only a few will be used (i.e. Pascalize or swizzle example). I'm not saying there aren't such usages, I just can't think of any myself. -Steve
Apr 18 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
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  
 '.'?

"." I might as well write "bloodyMaryBloodyMaryBloodyMary". Andrei

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.
Apr 19 2009
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
Adam Burton wrote:

 What about using something like '->' for dynamic calls instead of '.'?  
 When
 you see '.' your safe in the knowledge that at a glance you know said  
 method
 with said signature exists else the compiler will throw a paddy, when you
 see '->' you know that method call is evaluated at runtime. This has the
 added benefit that the same class can be used in compile time checking  
 code
 and runtime.

One use I can see for this is the other opDotExp use that's been mentioned in this thread, namely: struct s { opDotExp( string s ) { ... } } s obj; auto command = readInput(); obj->command( ); // calls obj.opDotExp( command ) For message-passing, one could argue that <- would be better than ->, though there are ambiguity problems with that. -- Simen
Apr 19 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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  
 '.'?

"." I might as well write "bloodyMaryBloodyMaryBloodyMary". Andrei

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
Apr 19 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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  
 '.'?

from "." I might as well write "bloodyMaryBloodyMaryBloodyMary". Andrei

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
Apr 19 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
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  
 '.'?

from "." I might as well write "bloodyMaryBloodyMaryBloodyMary". Andrei

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); } }
Apr 19 2009
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 19 Apr 2009 10:42:19 -0400, Denis Koroskin <2korden gmail.com>  
wrote:

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

from "." I might as well write "bloodyMaryBloodyMaryBloodyMary". Andrei

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); } }

Yes, there are many things that opDotExp can do that opDot or alias this (which is essentially opDot without any code). Hooking every function call on a type seems to be one of the two killer use cases of this feature (the other being defining a large range of functions from which only a small number need to exist). But call forwarding seems not to be one of them. There are better ways to simply forward a call (such as in your variant example). I'm pretty convinced that this is a useful feature, I still have qualms about how it's really easy to define a runtime black hole where the compiler happily compiles empty functions that do nothing instead of complaining about calling a function that does not exist. Also, I don't think the requirement for this feature needs to be for the arguments to be templated, it should be sufficient to have a single string template argument. This way, you can overload opDotExp functions via argument lists. And BTW, the answer to your question above I think: mixin("return inner."~name~"(args)"); Andrei demonstrated this usage in his Pascalize example. -Steve
Apr 19 2009
parent BCS <none anon.com> writes:
Hello Steven,


 Also, I don't think the requirement for this feature needs to be for
 the  arguments to be templated, it should be sufficient to have a
 single string  template argument.  This way, you can overload opDotExp
 functions via  argument lists.

split the difference and allow either (but not both): R opDotExp(char[] name)(ArgList) R opDotExp(char[] name, T...)(T)
Apr 19 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 20 Apr 2009 06:09:28 +0400, Steven Schveighoffer <schveiguy yahoo.com>
wrote:

 Yes, there are many things that opDotExp can do that opDot or alias this  
 (which is essentially opDot without any code).  Hooking every function  
 call on a type seems to be one of the two killer use cases of this  
 feature (the other being defining a large range of functions from which  
 only a small number need to exist).  But call forwarding seems not to be  
 one of them.  There are better ways to simply forward a call (such as in  
 your variant example).

 I'm pretty convinced that this is a useful feature, I still have qualms  
 about how it's really easy to define a runtime black hole where the  
 compiler happily compiles empty functions that do nothing instead of  
 complaining about calling a function that does not exist.

 Also, I don't think the requirement for this feature needs to be for the  
 arguments to be templated, it should be sufficient to have a single  
 string template argument.  This way, you can overload opDotExp functions  
 via argument lists.

That way you loose type safety of arguments.
 And BTW, the answer to your question above I think:

 mixin("return inner."~name~"(args)");

 Andrei demonstrated this usage in his Pascalize example.

 -Steve

Oh, right, I saw his post but forgot that solution. Thanks you!
Apr 20 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 20 Apr 2009 06:54:21 -0400, Denis Koroskin <2korden gmail.com>  
wrote:

 On Mon, 20 Apr 2009 06:09:28 +0400, Steven Schveighoffer  
 <schveiguy yahoo.com> wrote:

 Yes, there are many things that opDotExp can do that opDot or alias  
 this (which is essentially opDot without any code).  Hooking every  
 function call on a type seems to be one of the two killer use cases of  
 this feature (the other being defining a large range of functions from  
 which only a small number need to exist).  But call forwarding seems  
 not to be one of them.  There are better ways to simply forward a call  
 (such as in your variant example).

 I'm pretty convinced that this is a useful feature, I still have qualms  
 about how it's really easy to define a runtime black hole where the  
 compiler happily compiles empty functions that do nothing instead of  
 complaining about calling a function that does not exist.

 Also, I don't think the requirement for this feature needs to be for  
 the arguments to be templated, it should be sufficient to have a single  
 string template argument.  This way, you can overload opDotExp  
 functions via argument lists.

That way you loose type safety of arguments.

No class C { int y; void opDotExp(string fname)(int x) { y = x; } } auto c = new C; c.foo(1); // ok c.foo("hi"); // compile error, no such function. -Steve
Apr 20 2009
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 20 Apr 2009 09:47:53 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Mon, 20 Apr 2009 06:54:21 -0400, Denis Koroskin <2korden gmail.com>  
 wrote:

 On Mon, 20 Apr 2009 06:09:28 +0400, Steven Schveighoffer  
 <schveiguy yahoo.com> wrote:

 Yes, there are many things that opDotExp can do that opDot or alias  
 this (which is essentially opDot without any code).  Hooking every  
 function call on a type seems to be one of the two killer use cases  
 of this feature (the other being defining a large range of functions  
 from which only a small number need to exist).  But call forwarding  
 seems not to be one of them.  There are better ways to simply forward  
 a call (such as in your variant example).

 I'm pretty convinced that this is a useful feature, I still have  
 qualms about how it's really easy to define a runtime black hole  
 where the compiler happily compiles empty functions that do nothing  
 instead of complaining about calling a function that does not exist.

 Also, I don't think the requirement for this feature needs to be for  
 the arguments to be templated, it should be sufficient to have a  
 single string template argument.  This way, you can overload opDotExp  
 functions via argument lists.

That way you loose type safety of arguments.

class C { int y; void opDotExp(string fname)(int x) { y = x; } } auto c = new C; c.foo(1); // ok c.foo("hi"); // compile error, no such function. -Steve

Good point. My take is, just have the compiler rewrite a.b(c, d, e) into a.opDot!("b")(c, d, e) and call it a day. After that, the usual language rules enter in action.

Haven't used D2 for much stuff, but does this work? I remember reading something about partial IFTI, so if you have opDotExp(string fname, T...) (T args){} and you call opDotExp!("b")(c, d, e) Does it implicitly define T? -Steve
Apr 20 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Fri, 17 Apr 2009 18:24:04 +0400, Steven Schveighoffer <schveiguy yahoo.com>
wrote:

 On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella <llucax gmail.com>  
 wrote:

 I don't fully understand the example though. In writefln((v.qq = 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't  
 propagate
 the 5 to the new B()?

I think it translates to opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties. I for one, can't really see a huge benefit, but then again, I don't normally work with dynamic-type langauges. It looks to me like a huge hole that the compiler will ignore bugs that would have been caught if the methods were strongly typed: class c { void opDotExp(char[] methodname,...) { if(methodname == "mymethod") callMyMethod(); else throw new Exception("bad method name: " ~ methodname); } } void foo(c myc, bool rarelySetToTrue) { if(rarelySetToTrue) myc.mymethud(); // compiles, will throw runtime exception } Also, how do you overload the return value? Using this proposal, you can't have different dynamic methods that return different types. -Steve

Here is how it could be done: class C { auto opDot(string methodName, T... args)(T args) // opDotExp renamed to opDot { static if (methodName == "length") { return _length; // return type is size_t } else static if (methodName == "resize") { _resize(args); // return type is void } } } This is a great use-case for compile-time "static switch". Can we haz one, please?
Apr 17 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 17 Apr 2009 10:32:04 -0400, Denis Koroskin <2korden gmail.com>  
wrote:

 On Fri, 17 Apr 2009 18:24:04 +0400, Steven Schveighoffer  
 <schveiguy yahoo.com> wrote:

 On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella <llucax gmail.com>
 wrote:

 I don't fully understand the example though. In writefln((v.qq = 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't
 propagate
 the 5 to the new B()?

I think it translates to opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties. I for one, can't really see a huge benefit, but then again, I don't normally work with dynamic-type langauges. It looks to me like a huge hole that the compiler will ignore bugs that would have been caught if the methods were strongly typed: class c { void opDotExp(char[] methodname,...) { if(methodname == "mymethod") callMyMethod(); else throw new Exception("bad method name: " ~ methodname); } } void foo(c myc, bool rarelySetToTrue) { if(rarelySetToTrue) myc.mymethud(); // compiles, will throw runtime exception } Also, how do you overload the return value? Using this proposal, you can't have different dynamic methods that return different types. -Steve

Here is how it could be done: class C { auto opDot(string methodName, T... args)(T args) // opDotExp renamed to opDot { static if (methodName == "length") { return _length; // return type is size_t } else static if (methodName == "resize") { _resize(args); // return type is void } } }

Look! I found a way to implement this *in the current compiler*!: class C { size_t length() { return _length; } void resize(T...)(T args) { _resize(args); } } sorry, couldn't help myself :P -Steve
Apr 17 2009
prev sibling next sibling parent davidl <davidl nospam.org> writes:
在 Fri, 17 Apr 2009 21:44:09 +0800,Leandro Lucarella <llucax gmail.com>  
写道:

 davidl, el 17 de abril a las 14:31 me escribiste:
 After tweaking dmd a bit litte, i get the dotexp overloading work.

 The following is the test code:
 import std.stdio;
 class c
 {

     B opDotExp(char[] methodname,...)
     {
     	writefln("god it works ", methodname);
       return new B();
     }
     void opAdd(int j)
     {

     }
     void test()
     {
     }
 }

 class a:c
 {

 }

 class B
 {
   int i;
   B opAssign(int k){
     i=k;
     return this;
   }
 }

 char[] v1;

 void func(char[] v, ...){}

 void main()
 {
    a v=new a;
    v.test();
    v.dynamicmethod(3,4);
    //v.qq = 5;
    writefln((v.qq = 5).i);
 }

 it generates the output:
 god it works dynamicmethod
 god it works qq
 5

 Any comments? Do you like this feature?

This is awsome indeed. I'd love to see it in the specs. The suggestion of making opDotExp a template it's good one too. I guess that now that opDot is replaced by alias this, opDot can be used for this instead of opDotExp. I don't fully understand the example though. In writefln((v.qq = 5).i), how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't propagate the 5 to the new B()? Thanks for the great job.

Thanks, the example here v.qq returns a B object, and then the assignment kicks in, therefore it calls B.opAssign(5), thus the i member of that B instance is 5. -- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 17 2009
prev sibling next sibling parent davidl <davidl nospam.org> writes:
在 Fri, 17 Apr 2009 22:24:04 +0800,Steven Schveighoffer  
<schveiguy yahoo.com> 写道:

 On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella <llucax gmail.com>  
 wrote:

 I don't fully understand the example though. In writefln((v.qq = 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't  
 propagate
 the 5 to the new B()?

I think it translates to opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties.

The opDotExp overload func is supposed to deal with that, because it's in your hand to deal with the dynamic properties. The example here is illustrating the dynamic properties.
 I for one, can't really see a huge benefit, but then again, I don't  
 normally work with dynamic-type langauges.  It looks to me like a huge  
 hole that the compiler will ignore bugs that would have been caught if  
 the methods were strongly typed:

 class c
 {
    void opDotExp(char[] methodname,...)
    {
       if(methodname == "mymethod")
          callMyMethod();
       else
          throw new Exception("bad method name: " ~ methodname);
    }
 }

 void foo(c myc, bool rarelySetToTrue)
 {
    if(rarelySetToTrue)
      myc.mymethud(); // compiles, will throw runtime exception
 }

The problem is you're dealing with the class which is overloaded its opDot. You know the risk before hand, you are not going to overload for every classes. Here, you seem to little bit overrate this feature. :) If you want things checked, then you probabely need to go back to static. This dynamic stuff is used for dynamic things only, and as long as you have to do it in the dynamic way that means you have no easy way or even impossible to check it at compiletime and you accept the potential risk like the example you posted.
 Also, how do you overload the return value?  Using this proposal, you  
 can't have different dynamic methods that return different types.

Umm, maybe make the compiler to pass the return type into the opDot would allow the opDot func to decide which overload func to call. -- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 17 2009
prev sibling next sibling parent davidl <davidl nospam.org> writes:
在 Fri, 17 Apr 2009 22:24:04 +0800,Steven Schveighoffer  
<schveiguy yahoo.com> 写道:

 On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella <llucax gmail.com>  
 wrote:

 I don't fully understand the example though. In writefln((v.qq = 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't  
 propagate
 the 5 to the new B()?

I think it translates to opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties. I for one, can't really see a huge benefit, but then again, I don't normally work with dynamic-type langauges. It looks to me like a huge hole that the compiler will ignore bugs that would have been caught if the methods were strongly typed:

Actually this can help ddl project to work more nicely. Consider you can call plugin code directly without static bindings. And I believe delphi COM variant use some similar trick. It allows you call COM object without static bindings. Sometimes it's troublesome to get the static bindings, and you only need one piece of little function(you won't need the whole interface). -- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 17 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 17 Apr 2009 11:11:55 -0400, davidl <davidl nospam.org> wrote:

 鍦 Fri, 17 Apr 2009 22:24:04 +0800锛孲teven Schveighoffer  
 <schveiguy yahoo.com> 鍐欓亾:

 On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella  
 <llucax gmail.com> wrote:

 I don't fully understand the example though. In writefln((v.qq = 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't  
 propagate
 the 5 to the new B()?

I think it translates to opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties.

The opDotExp overload func is supposed to deal with that, because it's in your hand to deal with the dynamic properties. The example here is illustrating the dynamic properties.

except, you can't define a property like: void prop(int x) { _prop = x ^ ~0; } Using a dynamic method.
 I for one, can't really see a huge benefit, but then again, I don't  
 normally work with dynamic-type langauges.  It looks to me like a huge  
 hole that the compiler will ignore bugs that would have been caught if  
 the methods were strongly typed:

 class c
 {
    void opDotExp(char[] methodname,...)
    {
       if(methodname == "mymethod")
          callMyMethod();
       else
          throw new Exception("bad method name: " ~ methodname);
    }
 }

 void foo(c myc, bool rarelySetToTrue)
 {
    if(rarelySetToTrue)
      myc.mymethud(); // compiles, will throw runtime exception
 }

The problem is you're dealing with the class which is overloaded its opDot. You know the risk before hand, you are not going to overload for every classes. Here, you seem to little bit overrate this feature. :) If you want things checked, then you probabely need to go back to static. This dynamic stuff is used for dynamic things only, and as long as you have to do it in the dynamic way that means you have no easy way or even impossible to check it at compiletime and you accept the potential risk like the example you posted.

Sure, but what is the reason to need dynamic methods? I'm just trying to understand the usefulness of it. If a method is dynamic, we lose the following things: - compile-time type/signature checking - IDE assistance in determining which methods are available - ease of tracing where a method call goes. - not future proof -- for example, if a method name gets changed or moved, the code using the method still compiles. If we lose all these things, there must be *something* we gain by doing this, right? Also, what is the benefit of doing something like this versus specifically calling the dispatcher instead of having the compiler translate it?
 Also, how do you overload the return value?  Using this proposal, you  
 can't have different dynamic methods that return different types.

Umm, maybe make the compiler to pass the return type into the opDot would allow the opDot func to decide which overload func to call.

Walter already has issues overloading on return type, I'm not sure this is any different. -Steve
Apr 17 2009
prev sibling next sibling parent davidl <davidl nospam.org> writes:
在 Fri, 17 Apr 2009 23:20:53 +0800,davidl <davidl nospam.org> 写道:

 在 Fri, 17 Apr 2009 22:24:04 +0800,Steven Schveighoffer  
 <schveiguy yahoo.com> 写道:

 On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella  
 <llucax gmail.com> wrote:

 I don't fully understand the example though. In writefln((v.qq = 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't  
 propagate
 the 5 to the new B()?

I think it translates to opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties. I for one, can't really see a huge benefit, but then again, I don't normally work with dynamic-type langauges. It looks to me like a huge hole that the compiler will ignore bugs that would have been caught if the methods were strongly typed:

Actually this can help ddl project to work more nicely. Consider you can call plugin code directly without static bindings. And I believe delphi COM variant use some similar trick. It allows you call COM object without static bindings. Sometimes it's troublesome to get the static bindings, and you only need one piece of little function(you won't need the whole interface).

DDBI can also benefit from it. Consider the use case: myRow.Col1 = "abc"; -- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 17 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 17 Apr 2009 11:27:57 -0400, davidl <davidl nospam.org> wrote:

 鍦 Fri, 17 Apr 2009 23:20:53 +0800锛宒avidl <davidl nospam.org> 鍐欓亾:

 鍦 Fri, 17 Apr 2009 22:24:04 +0800锛孲teven Schveighoffer  
 <schveiguy yahoo.com> 鍐欓亾:

 On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella  
 <llucax gmail.com> wrote:

 I don't fully understand the example though. In writefln((v.qq =  
 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't  
 propagate
 the 5 to the new B()?

I think it translates to opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties. I for one, can't really see a huge benefit, but then again, I don't normally work with dynamic-type langauges. It looks to me like a huge hole that the compiler will ignore bugs that would have been caught if the methods were strongly typed:

Actually this can help ddl project to work more nicely. Consider you can call plugin code directly without static bindings. And I believe delphi COM variant use some similar trick. It allows you call COM object without static bindings. Sometimes it's troublesome to get the static bindings, and you only need one piece of little function(you won't need the whole interface).

DDBI can also benefit from it. Consider the use case: myRow.Col1 = "abc";

Yes, this case does make sense, but I would still probably rather write a statically-typed wrapper. Plus it's not terribly hard to write: myRow.set("Col1", "abc"); I'm not yet convinced, but it remains to be seen if there is some killer functionality that cannot be had without it. -Steve
Apr 17 2009
prev sibling next sibling parent davidl <davidl nospam.org> writes:
在 Fri, 17 Apr 2009 23:27:38 +0800,Steven Schveighoffer  
<schveiguy yahoo.com> 写道:

 On Fri, 17 Apr 2009 11:11:55 -0400, davidl <davidl nospam.org> wrote:

 在 Fri, 17 Apr 2009 22:24:04 +0800,Steven Schveighoffer  
 <schveiguy yahoo.com> 写道:

 On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella  
 <llucax gmail.com> wrote:

 I don't fully understand the example though. In writefln((v.qq =  
 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't  
 propagate
 the 5 to the new B()?

I think it translates to opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties.

The opDotExp overload func is supposed to deal with that, because it's in your hand to deal with the dynamic properties. The example here is illustrating the dynamic properties.

except, you can't define a property like: void prop(int x) { _prop = x ^ ~0; } Using a dynamic method.

But if you want a dynamic property, you won't want to do it in that way. You actually make the opDot to do: void opDot(char[] methodname, ...) { if (methodname = "prop") { // deal with the vararg to get the int x // assign it to _prop, then you're done. // or you can call the prop func here by reconstructing the vararg. } } Notice that if statement can be done by a delegate. Thus when you add a property you simply add your delegate func to the class. And opDot call those delegates one by one.
 I for one, can't really see a huge benefit, but then again, I don't  
 normally work with dynamic-type langauges.  It looks to me like a huge  
 hole that the compiler will ignore bugs that would have been caught if  
 the methods were strongly typed:

 class c
 {
    void opDotExp(char[] methodname,...)
    {
       if(methodname == "mymethod")
          callMyMethod();
       else
          throw new Exception("bad method name: " ~ methodname);
    }
 }

 void foo(c myc, bool rarelySetToTrue)
 {
    if(rarelySetToTrue)
      myc.mymethud(); // compiles, will throw runtime exception
 }

The problem is you're dealing with the class which is overloaded its opDot. You know the risk before hand, you are not going to overload for every classes. Here, you seem to little bit overrate this feature. :) If you want things checked, then you probabely need to go back to static. This dynamic stuff is used for dynamic things only, and as long as you have to do it in the dynamic way that means you have no easy way or even impossible to check it at compiletime and you accept the potential risk like the example you posted.

Sure, but what is the reason to need dynamic methods? I'm just trying to understand the usefulness of it. If a method is dynamic, we lose the following things: - compile-time type/signature checking - IDE assistance in determining which methods are available - ease of tracing where a method call goes. - not future proof -- for example, if a method name gets changed or moved, the code using the method still compiles.

Yes, these problems can happen across plugins/host, database design/db apps. Host can't validate its plugins. Host doesn't know what plugins can provide. DB apps which I know don't have much fancy technics to be future proof (what if my db design changes? those SQL won't immediately fail at compile time, only when you run it.)
 If we lose all these things, there must be *something* we gain by doing  
 this, right?

 Also, what is the benefit of doing something like this versus  
 specifically calling the dispatcher instead of having the compiler  
 translate it?

The benefit is you don't need to write the call function, you don't need to write the string quote. Take a look at my other posts about the usecases of this proposal(ddl, ddbi).
 Also, how do you overload the return value?  Using this proposal, you  
 can't have different dynamic methods that return different types.

Umm, maybe make the compiler to pass the return type into the opDot would allow the opDot func to decide which overload func to call.

Walter already has issues overloading on return type, I'm not sure this is any different. -Steve

Actually different, Walter needs to modify the overload mechanism in the current compiler for different function finger prints. What I need to do is sending the return type to the opDot method. Because the overloading is done by the opDot func at runtime, so I don't have Walter's difficulty in dealing with overloading :D -- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 17 2009
prev sibling next sibling parent davidl <davidl nospam.org> writes:
在 Fri, 17 Apr 2009 23:34:37 +0800,Steven Schveighoffer  
<schveiguy yahoo.com> 写道:

 On Fri, 17 Apr 2009 11:27:57 -0400, davidl <davidl nospam.org> wrote:

 在 Fri, 17 Apr 2009 23:20:53 +0800,davidl <davidl nospam.org> 写道:

 在 Fri, 17 Apr 2009 22:24:04 +0800,Steven Schveighoffer  
 <schveiguy yahoo.com> 写道:

 On Fri, 17 Apr 2009 09:44:09 -0400, Leandro Lucarella  
 <llucax gmail.com> wrote:

 I don't fully understand the example though. In writefln((v.qq =  
 5).i),
 how is that B.i is assigned to 5 if the opDotExp("qq", 5) don't  
 propagate
 the 5 to the new B()?

I think it translates to opDotExp("qq") = 5 Without knowing the signature of qq, how is the compiler supposed to infer that it is a property? In fact, I think this might be a limitation of this syntax, you can't define dynamic properties. I for one, can't really see a huge benefit, but then again, I don't normally work with dynamic-type langauges. It looks to me like a huge hole that the compiler will ignore bugs that would have been caught if the methods were strongly typed:

Actually this can help ddl project to work more nicely. Consider you can call plugin code directly without static bindings. And I believe delphi COM variant use some similar trick. It allows you call COM object without static bindings. Sometimes it's troublesome to get the static bindings, and you only need one piece of little function(you won't need the whole interface).

DDBI can also benefit from it. Consider the use case: myRow.Col1 = "abc";

Yes, this case does make sense, but I would still probably rather write a statically-typed wrapper. Plus it's not terribly hard to write: myRow.set("Col1", "abc"); I'm not yet convinced, but it remains to be seen if there is some killer functionality that cannot be had without it. -Steve

Actually in a lot cases, you don't have the time to write the static wrapper. Also for a COM object, you don't want to call it in such ways: comobj.callfunc("ComObjFunc", 3,4,5); You probably want to call it only by comobj.ComObjFunc(3,4,5); Yes static wrapper can solve these all. Problem is not in all cases you want to make a static binding for it. -- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 17 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 17 Apr 2009 14:32:07 -0400, Nick Sabalausky <a a.a> wrote:

 "davidl" <davidl nospam.org> wrote in message
 news:op.usje9ia3j5j59l my-tomato...
 The benefit is you don't need to write the call function,

...But you do have to write the opDotExp() function. How is that less work than just writing a dispatch function?
 you don't need to write the string quote.

I think I'd prefer that. If I put something in quotes, that tells me that typos might not get detected until runtime. But if I don't use quotes, and it compiles, then I know it's ok. With opDotExp, that certainty goes right out the window. All of a sudden I never know if an identifier following a dot compiled because it's ok, or because the error detection has been deferred. I'd feel like I was working in a dynamic language and I *HATE* working with dynamic languages. It's like trying to construct a building on a patch of ground that you know at any moment could change into a lake, sand, cliffside, or simply cease to exist without any warning. Additionally, here's an example from Haxe's xml.Fast: page.node.html.node.head.node.title.x.addChild(Xml.createPCData("Hello")); Think fast without any close inspection: What's the path being used? Umm... Ok, without opDotExp, that would be: page.node("html").node("head").node("title").x.addChild(Xml.createPCData("Hello")); That's a hell of a lot easier to read. Very easy now to see, at a mere glance, the path is "html/head/title". Of course, you could adjust the API for the Haxe/opDotExp version to be more like: page.html.head.title.x.addChild(Xml.createPCData("Hello")); But now (in addition to still not having the certainty of "if an unquoted identifier compiles, it must be ok"), you've opened yourself up to a world of naming collision issues.

This is what I was talking about, but didn't thoroughly explain when I said "ease of tracing where a method call goes." Thanks for explaining this better. I do see usefulness in cases like setting a field in a database row (which should be relatively painless, path-wise). However, my preference is to create a wrapper class around the row to have the compiler to type check my calls. In that case, the typechecked version would just call a dispatch routine anyways (which can be ugly because it's hidden). I think dynamic methods are just asking for trouble in the form of latent bugs. My main concern that I've read so far is how if a class has a dynamic method dispatcher that's callable like a method, you can't rely on the compiler to help you typecheck (or spellcheck) the *non-dynamic* methods, because it will just default to sending incorrectly typed data or misspelled methods to the dynamic dispatcher. I think dynamic methods have a very limited use, and probably aren't worth polluting the D language for a few rare cases. When you know the API ahead of time, you're almost always better off to have statically typed objects. When you don't know it ahead of time, well, I prefer the uglyness of seeing the quoted strings to having the compiler just start trusting everything I do ;) -Steve
Apr 17 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 17 Apr 2009 14:49:37 -0400, Yigal Chripun <yigal100 gmail.com>  
wrote:

 On 17/04/2009 21:34, bearophile wrote:
 Nick Sabalausky:
 There are people who swear by the ability of adding methods at
 runtime and changing the inheritance hierarchy dynamically. It
 makes for a very fluid environment.

haphazard.

Adding methods at runtime is named "monkey patching", and it is considered a bad practice even in Python. In Ruby it is more common. Usually in such languages such things are less dangerous because the code contains lot of tests anyway. Some people say that a way to remove most of the downsides of monkey patching is to make it scoped, that is the changes (like a method added or replaced) to a class aren't seen globally in the whole program (like from other modules), but only in the scope where such change is done (and its subscopes). I think I have not seen languages where this is doable yet. Bye, bearophile

just like anything in life this can be overused. This is a very useful tool in a programmer's toolbox for when you need, for example, to quickly experiment with something or do an urgent fix. It's like when you build a house you have it properly designed and have solid foundations but after the house is built you can still redesign internally without rebuilding the entire house.

It's more like adding another outside door. Try doing that ;) (BTW, removing one is easy, but that kind of doesn't apply here...) changing internals in code is easy, that's why we use OOP. Changing APIs is not. -Steve
Apr 17 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 17 Apr 2009 15:08:12 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Nick Sabalausky wrote:
 "Steven Schveighoffer" <schveiguy yahoo.com> wrote in message  
 news:op.usjnzajzeav7ka steves.networkengines.com...
 On Fri, 17 Apr 2009 14:32:07 -0400, Nick Sabalausky <a a.a> wrote:

 My main concern that I've read so far is how if a class has a dynamic  
 method dispatcher that's callable like a method, you can't rely on the  
 compiler to help you typecheck (or spellcheck) the *non-dynamic*  
 methods, because it will just default to sending incorrectly typed  
 data or misspelled methods to the dynamic dispatcher.

 I think dynamic methods  have a very limited use, and probably aren't  
 worth polluting the D  language for a few rare cases.

 When you know the API ahead of time,  you're almost always better off  
 to have statically typed objects.  When  you don't know it ahead of  
 time, well, I prefer the uglyness of seeing the  quoted strings to  
 having the compiler just start trusting everything I do  ;)


I think there's merit in binding via strings. It makes for very flexible code that is future-proof, dynamic-linking-friendly, and hot-swappable without recompiling (e.g. you don't need to recompile because you now implement an interface etc.) Reflection is very useful as well. If anything, this agreed-fest shows that the rift between static typing and dynamic typing is alive and well. I've seen many discussions in which people were mystified how anyone gets anything done in a statically-typed OO language. (In fairness, static typing and OO have at best a tense marriage.) But anyway, my point is that it's good to be open-minded. If this conversation does nothing but leave us firmly with our heels in static-land, then we haven't gained anything. If we weren't used to static types we wouldn't be here. I think D can and should allow string lookup for its methods. It's a low-complexity proposition that adds a very interesting tool to D's arsenal. I suggested Walter since a long time ago to support opDot!(string). He implemented the useless version (sigh) which was arguably much simpler and offered a cheap way to experiment. So I'm very happy it's back on the table, and with an implementation to boot. Congratulations David.

I guess I don't mind the dynamic lookup of methods, but I really don't like the proposal to make it look exactly like a statically typed call. To "hide" the fact that you are doing a dynamic lookup makes me worry about losing the invariants that I come to enjoy with statically typed methods, that is, if I see x.method(a, b, c), it means that the compiler has checked that I called that method correctly with the correctly typed information. I use C#'s runtime introspection all the time, and it makes for some really awesome code (things I wish D could do), but I still have to do things like Type[] argTypes = ...; object[] args = ...; x.GetType().GetMethod("myMethod", argTypes).Invoke(x, args); To have that simply cut down to: x.myMethod(a, b, c); is a nifty experiment, but now I lost all ability to know how the compiler is interpreting that. I bet D can do a much better job at runtime type information than C# due to the template system being so powerful, but I still want to know that I'm dynamically doing something versus statically. Something like: void foo(object x) { x.invoke("myMethod", a, b, c); } where invoke is some method that uses the classinfo of x to look up the method would be freaking awesome ;) -Steve
Apr 17 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Fri, 17 Apr 2009 23:23:52 +0400, Don <nospam nospam.com> wrote:

 Yeah, The ideal language would detect ALL bugs at compile time. We don't  
 want to move in the wrong direction.

I would consider it a "wrong direction". You won't use it *that* frequent, only in those places where it trully fits. For example, a Variant class and scripting languages bindings would greatly benefit for having this functionality.
Apr 17 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Fri, 17 Apr 2009 23:36:06 +0400, Denis Koroskin <2korden gmail.com> wrote:

 On Fri, 17 Apr 2009 23:23:52 +0400, Don <nospam nospam.com> wrote:

 Yeah, The ideal language would detect ALL bugs at compile time. We don't
 want to move in the wrong direction.

I would consider it a "wrong direction".

Err.. Should read: I woudn't consider it a "wrong direction".
Apr 17 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 17 Apr 2009 15:55:43 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Nick Sabalausky wrote:
 "Yigal Chripun" <yigal100 gmail.com> wrote in message  
 news:gsam1p$1ut7$1 digitalmars.com...
 On 17/04/2009 21:58, Steven Schveighoffer wrote:

 btw, I'm not trying to convince you that dynamic typing is necessary  
 always a better solution. What I'm saying is that I agree with Andrei  
 - we need to be open minded and have as many useful tools as possible  
 in our programmer toolbox. The important thing is to choose the right  
 tool for the job.

programmer toolbox" is great. But with opDotExp, that's not the whole story. What opDotExp is, is a tool of only occasional use that provides only a small benefit, *and* ends up destroying a much more important tool: compile-time checking on a class's members.

s/on a class's members/on the members of the class that actively chose that/

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? The problem with me is that it doesn't *look* different. If there was some way to denote "call dynamic method" instead of "call static method" or some way to denote "has dynamic methods", then I'd have no problem with it. Even if you were forced to derive from a special base type in order to use dynamic methods, I wouldn't mind that. -Steve
Apr 17 2009
prev sibling next sibling parent davidl <davidl nospam.org> writes:
在 Sat, 18 Apr 2009 03:26:13 +0800,Steven Schveighoffer  
<schveiguy yahoo.com> 写道:

 On Fri, 17 Apr 2009 15:08:12 -0400, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 Nick Sabalausky wrote:
 "Steven Schveighoffer" <schveiguy yahoo.com> wrote in message  
 news:op.usjnzajzeav7ka steves.networkengines.com...
 On Fri, 17 Apr 2009 14:32:07 -0400, Nick Sabalausky <a a.a> wrote:

 My main concern that I've read so far is how if a class has a dynamic  
 method dispatcher that's callable like a method, you can't rely on  
 the compiler to help you typecheck (or spellcheck) the *non-dynamic*  
 methods, because it will just default to sending incorrectly typed  
 data or misspelled methods to the dynamic dispatcher.

 I think dynamic methods  have a very limited use, and probably aren't  
 worth polluting the D  language for a few rare cases.

 When you know the API ahead of time,  you're almost always better off  
 to have statically typed objects.  When  you don't know it ahead of  
 time, well, I prefer the uglyness of seeing the  quoted strings to  
 having the compiler just start trusting everything I do  ;)


I think there's merit in binding via strings. It makes for very flexible code that is future-proof, dynamic-linking-friendly, and hot-swappable without recompiling (e.g. you don't need to recompile because you now implement an interface etc.) Reflection is very useful as well. If anything, this agreed-fest shows that the rift between static typing and dynamic typing is alive and well. I've seen many discussions in which people were mystified how anyone gets anything done in a statically-typed OO language. (In fairness, static typing and OO have at best a tense marriage.) But anyway, my point is that it's good to be open-minded. If this conversation does nothing but leave us firmly with our heels in static-land, then we haven't gained anything. If we weren't used to static types we wouldn't be here. I think D can and should allow string lookup for its methods. It's a low-complexity proposition that adds a very interesting tool to D's arsenal. I suggested Walter since a long time ago to support opDot!(string). He implemented the useless version (sigh) which was arguably much simpler and offered a cheap way to experiment. So I'm very happy it's back on the table, and with an implementation to boot. Congratulations David.

I guess I don't mind the dynamic lookup of methods, but I really don't like the proposal to make it look exactly like a statically typed call. To "hide" the fact that you are doing a dynamic lookup makes me worry about losing the invariants that I come to enjoy with statically typed methods, that is, if I see x.method(a, b, c), it means that the compiler has checked that I called that method correctly with the correctly typed information. I use C#'s runtime introspection all the time, and it makes for some really awesome code (things I wish D could do), but I still have to do things like Type[] argTypes = ...; object[] args = ...; x.GetType().GetMethod("myMethod", argTypes).Invoke(x, args); To have that simply cut down to: x.myMethod(a, b, c);

That's exactly what opDot can be capable of accomplishing. GetMethod is something which feeds the classobject with the method name the opDot woud like to handle and the fingerprint of the function.
 is a nifty experiment, but now I lost all ability to know how the  
 compiler is interpreting that.  I bet D can do a much better job at  
 runtime type information than C# due to the template system being so  
 powerful, but I still want to know that I'm dynamically doing something  
 versus statically.

 Something like:

 void foo(object x)
 {
     x.invoke("myMethod", a, b, c);
 }

 where invoke is some method that uses the classinfo of x to look up the  
 method would be freaking awesome ;)

This can also be done in D2 with my opDot proposal.
 -Steve

-- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 17 2009
prev sibling next sibling parent davidl <davidl nospam.org> writes:
在 Sat, 18 Apr 2009 03:45:43 +0800,Nick Sabalausky <a a.a> 写道:

 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message
 news:gsak2p$1s8a$1 digitalmars.com...
 I think there's merit in binding via strings. It makes for very flexible
 code that is future-proof, dynamic-linking-friendly, and hot-swappable
 without recompiling (e.g. you don't need to recompile because you now
 implement an interface etc.) Reflection is very useful as well.

 I think D can and should allow string lookup for its methods. It's a
 low-complexity proposition that adds a very interesting tool to D's
 arsenal.

That's a separate issue. I absolutely agree with the usefulness of being able to invoke static methods via a string identifier at runtime. But I think opDotExp is an extremely flawed way to do it. A much better way would be through a reflection mechanism:

The opDot func can be extremely restrictive by looking up a former added table which are function fingerprints registered by a method call like dynamo.addMethod("myfunc", &myfunc); dynamo.Lenght or dynamo.mymethud would just result runtime exception, because you didn't register these functions.
 class Foo
 {
     void bar() {}
 }

 auto foo = new Foo();
 traits(foo).func("bar").invoke();

 That way, you can have the benefits of  
 runtime-string-identifier-invocation
 (and have it on *every* class/method), but without completely loosing
 compile-time checking on the members of every class which is capable of
 using it.

 If anything, this agreed-fest shows that the rift between static typing
 and dynamic typing is alive and well. I've seen many discussions in  
 which
 people were mystified how anyone gets anything done in a  
 statically-typed
 OO language.

...Doesn't necessarily mean both sides have valid points. Just that there's disagreement. Outside of diplomacy, it doesn't make any sense to agree with something you disagree with, or disagree with something you agree with, just for the sake a closing a rift. Better to just continue debating the issues with logical arguments. If nothing gets accomplished, well ok, fine, but so what? That's a lot better than coming to hasty-but-wrong agreement just for the sake of agreement. I guess what I'm saying is, people coming to an agreement is all fine and dandy, but the most important thing is to arrive at the *correct* agreement. If one group of people insist that "2+2=0" and another group insists that "2+2=10", then it's far better to keep analyzing and debating until "2+2=4" is discovered and proven than to say "let's all be happy!" and hastily agree that "2+2=5". Again, outside of diplomacy, people disagreeing is not the real problem, the real problem is that the best answer either hasn't been found or hasn't been unquestionably shown to be best.

-- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 17 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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?

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?

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
Apr 17 2009
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
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.
Apr 17 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
Apr 17 2009
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
Nick Sabalausky wrote:
 If the member-name parameter to opDotExp was *required* to be a template
 paramater, then I agree, and I would have no objections to having that.  
 But
 it should be pointed out that that would not actually be dynamic  
 invokation,
 since you wouldn't be able to invoke a member whose name is only known at
 run-time. (But I would love for that to be possible through a reflection
 API.)

As we can all clearly see, in obj.someDynamicFunction( "Foo!" ); "someDynamicFunction" is definately known at compile-time, and not something you could change at runtime. Thus, making the member name parameter a template parameter seems the most logical solution. The fact you can't invoke arbitrary members chosen at runtime with it has little to do with opDotExp and much to do with how the D language is. If you want true dynamic invocation you need a separate function anyway, or you'd end up with code like this: string s = "someDynamicFunction"; obj.opDotExp( s ); Now, you could simply alias "opDotExp" to "invoke" or whatever you please, but having invoke be a separate function does not seem like a horrible trade-off to me. -- Simen
Apr 18 2009
prev sibling next sibling parent "Danny Wilson" <bluezenix gmail.com> writes:
Op Sat, 18 Apr 2009 09:24:39 +0200 schreef Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org>:

  now you have an easy way to know if a type is dynamic without changing  
 the method invocation syntax. A proper IDE can easily mark those Types  
 as different, for example, using a different color.

The dynamic behavior is indicated by the use of opDotExp. The redundancy of the two notations doesn't quite sit well. Andrei

Not necessarily. dynamic class A { void opDotExp... // compiles } class A { void opDotExp... // error } class A { void nothrow opDotExp... // compiles } But yea its still redundant ofcourse. The 'proper' IDE could color classes which have an opDotExp (thats allowed to throw) differently just aswell.
Apr 18 2009
prev sibling next sibling parent "Danny Wilson" <bluezenix gmail.com> writes:
Op Sat, 18 Apr 2009 00:22:25 +0200 schreef Christopher Wright  
<dhasenan gmail.com>:

 Let's say you integrate D with a scripting language where you can add  
 methods to an object at arbitrary times. Instead of writing:
 scriptObj.invokeMethod("methodname", arguments);

I thought that would be the obvious application, and decided to mention the haXe instead :-) Guess it's only obvious for someone used to dynamic languages...
Apr 18 2009
prev sibling next sibling parent "Danny Wilson" <bluezenix gmail.com> writes:
Op Fri, 17 Apr 2009 22:31:04 +0200 schreef Nick Sabalausky <a a.a>:

 But with opDotExp, its mere *existence* undermines my ability to be sure
 that non-quoted identifiers are ok as long as they've compiled. That  
 type of
 tradeoff is obviously fine when the potential benefits are significant
 enough (operator overloading, for instance). But from everything I've  
 seen
 so far, opDotExp's benefits are trivial at best. I don't want to have to
 keep track of "ok, is this class using opDotExp or not, because if it is,
 then I need to be more careful", just for the sake of a feature that
 provides such a tiny and questionable benefit.

class AbstractConceptInvoker { public void activate(){} } auto a = new AbstractConceptInvoker(); a.activate(); What does my code do? Who knows? It could connect to a DB and delete all tables. It could be an O(infinity) operation... You have no idea until you either read (correct) documentation about it, or look at the code. Now let's go from that obvious observation to opDotExp() You know the class uses opDotExp() because it said so in the docs. Examples that could really benifit from this are: - XMLRPC and other kinds of remoting - Quick access to: XML / JSON / Yaml / Config files / DB access - Calling DLLs without bindings - Lots more All these would mention it in their docs, guaranteed. Because they use opDotExp it's implicitly mentioned. I don't think anyone would tell a documentation generator to list all public methods except opDotExp .. that would be just braindead. And you could generate the docs yourself if you have to code.. So what exactly are you afraid of?!
Apr 18 2009
prev sibling next sibling parent "Danny Wilson" <bluezenix gmail.com> writes:
Op Sat, 18 Apr 2009 12:25:55 +0200 schreef Danny Wilson  
<bluezenix gmail.com>:

 Op Fri, 17 Apr 2009 22:31:04 +0200 schreef Nick Sabalausky <a a.a>:

 so far, opDotExp's benefits are trivial at best. I don't want to have to
 keep track of "ok, is this class using opDotExp or not, because if it  
 is,
 then I need to be more careful", just for the sake of a feature that
 provides such a tiny and questionable benefit.

So what exactly are you afraid of?!

What I was really trying to say is: sure you have to keep track of which class uses opDotExp but you won't be thinking about it. You'll be thinking "How do I access this json 'object'?" and then remember you can just access it. With quoted identifiers you'll think exactly the same: "How do i access?" use: ["object"]. In both cases you'll get a runtime exception of some sort and have to take a look at the code if you made typos. HaXe (and Actionscript a little) are great examples of statically-dynamically-typed languages :-) I use static typing pretty much all the time. Just in a few cases I use Dynamic (or this opDotExp like thing) and then I'll know where to look for simple errors like typos. Null pointers are a far more common bug for me but that's beside the point.
Apr 18 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 17 Apr 2009 23:43:22 -0400, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:

 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.

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
Apr 18 2009
prev sibling next sibling parent davidl <davidl nospam.org> writes:
在 Sun, 19 Apr 2009 02:16:30 +0800,Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> 写道:

 bearophile wrote:
 Andrei Alexandrescu:
 Yes. The amount of confusion in this thread is staggering.

string isn't determined at run time, then this thing isn't useful for my purposes, and it's not close to the object-C as I was talking about, and it's not similar to __getattr__ of Python, etc. Bye, bearophile

In the syntax a.b how would either of a and b be identified at runtime? I mean, you write the code somewhere and it gets compiled. It's not like you're reading "a.b" from the console and then call some eval() function against them.

Depends on the compiler semantic. The compiler can feed the opDot with the variable b if it's a string. The compiler can intepret the opDot that way. Consider: string b = "myfunction"; dynamo.b(1); we can translate this to dynamo.opDot("b",1); and we can also rewrite this to dynamo.opDot(b, 1);( if there's a non-template version of opDot just like the current one I have) and even more we can let the compiler decide which rewrite version it prefers, because compiler can simply firstly try to query if there's a variable named b with type of string in the scope of caller. There's someone posted this sort of dynamic language semantic, and I was astonished and thought it were impossible under the opDot scheme. Yet till now, I finally realize that the compiler gets all source input just as the dynamic language interpreter gets input from a console. There won't be much barrier blocking us from mocking every dynamic features. Just we need to be careful to choose some useful part and equip our static land without much potential risk while developer friendly interface. I have not and don't intend to choose the second rewrite version. Whether we should go the second version is left for more discussion. I think many static language developers won't enjoy the second rewrite version at the first glance. I didn't like it either. Yet, after consider a little bit longer, I start to think it might not be as that bad as the first imagination impression in our own mind. It can be funny to have some code like: string doSomething; if (animal = "dog") doSomething="bark"; if (animal = "human") doSomething="talk"; dynamo_animal.doSomething(); Ok, this may look pretty evil in a static language for many among us. I need to claify that I don't intend to go into that direction till further discussion will be made. The first rewrite version and revised by your template suggestion should be able to do the __getattr__ trick, if I get the idea of __getattr__ correctly. I think bearophile's worry can be alleviated. ;)
 Andrei

-- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 18 2009
prev sibling next sibling parent davidl <davidl nospam.org> writes:
在 Sun, 19 Apr 2009 03:15:02 +0800,Daniel Keep  
<daniel.keep.lists gmail.com> 写道:

 Andrei Alexandrescu wrote:
 Michel Fortin wrote:
 ...

 Andrei, I think you, and perhaps everyone here, are overlooking one
 small but important detail.

 opDotExp, if a template like you're adovcating, undermines future
 runtime dynamic call capabilities (which are part of most runtime
 reflection systems).

 If you're going to have something such as

     d.invoke("foo");

 available on any type (using some yet-to-see runtime reflection), it
 will work for a non-template opDotExp (invoke would just forward to
 opDotExp with the string "foo" if it doesn't find the member through
 reflection), but it cannot work for an opDotExp template using "foo"
 as a template argument since string "foo" is a runtime argument. (In
 fact, I don't see how any template can be callable from runtime
 reflection.)

 It ensues that if later we add runtime reflection to D, dynamic calls
 won't work for template opDotExp.

 So I'm not really convinced that template is the way to go, even
 though it would allow great things. Almost all you can do with a
 template, you already can do by adding members using a mixin. And
 adding members using a mixin will also work with runtime reflection,
 unlike templated opDotExp. The only use case left unadressed by mixins
 and runtime opDotExp is the function with an infinite number of
 members which want compile time dispatching.

 Perhaps we need both template and runtime opDotExp...

 Anyway, I like the general concept so I hope we'll get it, template or
 not. I just feel the point above has been neglected in the discussion.

I'm confused. Isn't it clear that at the moment we "have" the ability to pass a function name as a runtime string? What we're lacking is the ability to implement that using reflection, but that's an entirely separated discussion! And no, you're not supposed to forward from invoke(string, Variant[]...) to opDotExp - it's precisely the other way around! I'm not sure where I'm wrong in explaining this, it looks like I'm unable to remove a very persisting confusion. So let me restate: opDotExp taking a runtime string does not EXPAND your options, it severely LIMITS them. It's very simple: with the former you have strictly LESS options and no NOTHING in terms of added power. It's simple, I swear. Andrei

A related issue is that obj.opDotExp!"foo"(...) cannot be reflected over because template instantiations aren't part of the typeinfo. Which means if you DO have any methods accessed via opDotExp, you CAN'T invoke them dynamically at runtime, but you COULD if it took a string argument instead of a string template argument.

Umm, actually you can... but with bad duplication in code: void Invoke(methodname, ...) { if (methodname = "compile_time_dispatched_method") // these sort of comparison exists in template opDot in a static manner. { opDot("compile_time_dispatched_method")(args); } } Yes, we get duplication here. I don't have an idea to solve the duplication elegantly yet. Maybe string mixins?
 Just sayin' :)

   -- Daniel

-- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 18 2009
prev sibling next sibling parent davidl <davidl nospam.org> writes:
在 Sun, 19 Apr 2009 03:42:02 +0800,Michel Fortin  
<michel.fortin michelf.com> 写道:

 On 2009-04-18 11:19:38 -0400, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> said:

 I'm confused. Isn't it clear that at the moment we "have" the ability  
 to pass a function name as a runtime string?

Indeed, you can pass the template argument as a runtime argument to another function. No misunderstanding here.
 What we're lacking is the ability to implement that using reflection,  
 but that's an entirely separated discussion!

Runtime reflection lacking indeed, but wether runtime reflection is an entirely separated discussion depends on if what we're doing will get in the way of implementing it.
 And no, you're not supposed to forward from invoke(string,  
 Variant[]...) to opDotExp - it's precisely the other way around!

Wrong. Whether it's one way or another entirely depends on what your goals are. When writing in D and interacting with a scripting language, or remote objects, you can write a function call that will use opDotExp to forward the call to the script object. A template fulfill this goal pretty well. You may want the reverse too: call a D function from a script, or a remote machine, via some runtime reflection mecanism. That's where we have a problem: if the script calls function "foo", and the object implements function "foo" using opDotExp (because the object is itself a proxy to something else for instance), then it just won't work.

Umm, right, you hunt the problem I didn't consider before. But in that case, actually you are trying to use a static info based runtime reflection to deal with a dynamic D object. Just like you try to deal with a dynamic object with previous introspection of it. I think for runtime reflection against a dynamic object, the only way is querying the object itself. And the object returns the runtime info back.
 Also, if, for example, you use a templated opDotExp to implement a bunch  
 of related methods (like those "xyzw", "xwyz", etc.), then you're making  
 those inaccessible from reflection. If on the other hand you do the same  
 using a mixin adding true members to your class or struct, then the  
 compiler can include them in the reflection data, and they'll be  
 accessible to the outside world.

 So, a runtime opDotExp is superior for some use cases because it works  
 for function calls comming from the outside (via runtime or compile-time  
 reflection), and you may need that if you want to expose some D code via  
 reflection.

-- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Apr 18 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Sat, 18 Apr 2009 21:43:15 +0400, Steven Schveighoffer <schveiguy yahoo.com>
wrote:

 On Fri, 17 Apr 2009 23:43:22 -0400, Steven Schveighoffer  
 <schveiguy yahoo.com> wrote:

 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.

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.
Apr 18 2009
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
Yigal Chripun wrote:
 what prevents D from having an eval function?
 suppose someone modifies the DMD front-end to compile a string with the  
 source code of a function in-memory, than this is processed by something  
 based on DDL and what you get is an API call that takes source code in a  
 string and returns a function pointer.

I suggest you read Burton Radons' "The Joy and Gibbering Terror of Custom-Loading Executable". http://members.shaw.ca/burton-radons/The%20Joy%20and%20Gibbering%20Terror%20of%20Custom-Loading%20Executables.html -- Simen
Apr 18 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sat, 18 Apr 2009 14:05:30 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 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.

class X { auto opDotExp(string fname, T...)(T args) { static if(fname == "blah") return foo(args); else static if(fname == "blither") return bar(args); else static assert(0, "Dunno how to "~fname); } }

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
Apr 18 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
Apr 19 2009
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
davidl wrote:
 Any comments? Do you like this feature?

And here it is (called opDispatch, Michel Fortin's suggestion): http://www.dsource.org/projects/dmd/changeset?new=trunk%2Fsrc 268&old=trunk%2Fsrc 267
Nov 28 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Walter Bright:
 And here it is (called opDispatch, Michel Fortin's suggestion):<

That's short code. Do you like my related suggestion of opDynamic (that works with run-time method names)? Bye, bearophile
Nov 28 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
bearophile wrote:
 Walter Bright:
 And here it is (called opDispatch, Michel Fortin's suggestion):<

That's short code. Do you like my related suggestion of opDynamic (that works with run-time method names)?

opDispatch can be written to do runtime method names, no language changes needed.
Nov 28 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Walter Bright:
 opDispatch can be written to do runtime method names, no language 
 changes needed.

Very good. Then the opDynamic name wasn't wrong. Can someone show me a small example of how to use it with runtime method names? Bye, bearophile
Nov 28 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
bearophile wrote:
 Can someone show me a small example of how to use it with runtime method names?

class C { void dynamic(string s, int i) { ... } void opDispatch(string s)(int i) { dynamic(s, i); } }
Nov 28 2009
prev sibling parent reply "Simen kjaeraas" <simen.kjaras gmail.com> writes:
On Sun, 29 Nov 2009 00:37:34 +0100, Walter Bright  
<newshound1 digitalmars.com> wrote:

 davidl wrote:
 Any comments? Do you like this feature?

And here it is (called opDispatch, Michel Fortin's suggestion): http://www.dsource.org/projects/dmd/changeset?new=trunk%2Fsrc 268&old=trunk%2Fsrc 267

Just tested it - it does not seem to allow template parameters beyond just the function name. Is this something we can expect in the future? The use case I have is this: struct foo { void opDispatch( string name, T )( T value ) { static if ( is( T == float ) ) { // Do something } else static if ( T == int ){ } else { static assert( false, "Unsupported argument type." ); } } } Thank you, Simen
Nov 28 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
Simen kjaeraas wrote:
 Just tested it - it does not seem to allow template parameters beyond 
 just the
 function name. Is this something we can expect in the future?
 The use case I have is this:
 
 struct foo {
   void opDispatch( string name, T )( T value ) {
     static if ( is( T == float ) ) {
       // Do something
     } else static if ( T == int ){
 
     } else {
       static assert( false, "Unsupported argument type." );
     }
   }
 }

I see. That's a bug, and I'll fix it.
Nov 28 2009