April 18, 2009
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?!
April 18, 2009
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.
April 18, 2009
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()?
>>> 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?
> 
> 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);
}

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

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