April 18, 2009
在 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.
>>  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.
>

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/
April 18, 2009
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/

April 18, 2009
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.
I agree.

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?


April 18, 2009
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.
April 18, 2009
在 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/
April 18, 2009
在 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/
April 18, 2009
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.
> 
That's a very clear description.

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.
April 18, 2009
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.


April 18, 2009
davidl wrote:
> 在 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.
>>>  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.
>>
> 
> 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
April 18, 2009
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