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

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


April 18, 2009
Hello Denis,

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

So I saw, I'm just saying it's not without precedent.


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

bearophile wrote:
> 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

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

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