November 28, 2009
== Quote from Walter Bright (newshound1@digitalmars.com)'s article
> dsimcha wrote:
> > Sometimes I feel like there should be a law similar to Greenspun's Law for language design:
> >
> > Any sufficiently long-lived language that promises to be "simpler" than C++ and D will grow to contain an ad-hoc, bug-ridden, informally specified, slow implementation of half of C++ and D.
> The dogged inventiveness of the C++ community never ceases to amaze me.
> Someone always finds a way to make a library to support some paradigm.
> Look at all the things Boost does.
> The problem, though, is the result is often just so strange I'd rather
> do without. Sometimes, you just need to improve the language to support
> things better.

Right, but sometimes (though certainly not always) it's better to provide a meta-feature that solves a whole bunch of problems (like better templates) and then solve the individual problems at the library level, rather than add a language feature specifically to address each need.  One thing D does very well is allow you to do the same kind of metaprogramming solutions you would do in C++, except that the result doesn't suck.  For example, std.range implements functional-style lazy evaluation as a library, and does it well.  The point is that, if you can't deal with the complexity of having real templates, you better be prepared for the complexity created by not having them.

Having never done it before, I really cannot imagine how people get any work done in a language that doesn't have either duck typing or good templates.  It's just too rigid.  It seems like modern statically typed languages like Java and C# end up adding tons of ad-hoc workarounds for lacking either of these as well-integrated language features.  The best/worst example is auto-boxing.
November 28, 2009
dsimcha wrote:
> Right, but sometimes (though certainly not always) it's better to provide a
> meta-feature that solves a whole bunch of problems (like better templates) and
> then solve the individual problems at the library level, rather than add a
> language feature specifically to address each need.

Yup. The hard part, though, is figuring out what the magic set of seminal features should be.


> One thing D does very well is
> allow you to do the same kind of metaprogramming solutions you would do in C++,
> except that the result doesn't suck.  For example, std.range implements
> functional-style lazy evaluation as a library, and does it well.  The point is
> that, if you can't deal with the complexity of having real templates, you better
> be prepared for the complexity created by not having them.

Right. A "simple" language pushes the complexity onto the programmer, so he has to write complicated code instead. D programs tend to be dramatically shorter than the equivalent C++ one.


> Having never done it before, I really cannot imagine how people get any work done
> in a language that doesn't have either duck typing or good templates.  It's just
> too rigid.  It seems like modern statically typed languages like Java and C# end
> up adding tons of ad-hoc workarounds for lacking either of these as
> well-integrated language features.  The best/worst example is auto-boxing.

I tried programming in Java.

A friend of mine had an unexpected insight. He used Java a lot at a major corporation. He said an IDE was indispensable because with "one click" you could generate a "hundred lines of code". The light bulb came on. Java makes up for its lack of expressiveness by putting that expressiveness into the IDE!

In D, you generate that hundred lines of code with templates and mixins.
November 28, 2009
Walter Bright wrote:
> One thing Java and Python, Ruby, etc., still hold over D is dynamic classes, i.e. classes that are only known at runtime, not compile time. In D, this:
> 
>    s.foo(3);
> 
> could be emulated with:
> 
>    s.dynamicMethod("foo", 3);
> 
> Unfortunately, that makes it impossible to use s with generic code (besides looking unappealing). But with a small feature, we can make this work:
> 
>    struct S
>    {
>         ...
>     T opDynamic(s : string)(args...);
>    }
> 
> and then s.foo(3), if foo is not a compile time member of s, is rewritten as:
> 
>    s.opDynamic!("foo")(3);
> 
> and opDynamic defers all the nuts-and-bolts of making this work out of the language and into the library.
> 
> In particular, opDynamic's parameter and return types should all be instances of std.variant.
> 
> (This has come up in various forms in this n.g. before, but I don't have any references handy.)

Seems fine, but how will this interact with "alias...this" and opDot?  The former seems simple enough: if the "alias...this" field provides the member, use that, otherwise fall back on opDynamic.  The latter seems iffy, though.  Maybe something like this:

	// if the return type of opDot provides the member...
	(auto tmp = s.opDot, tmp ? tmp.foo(3) : s.opDynamic!"foo"(3))

Hmm... ew... but I can't think of anything better off-hand.  The "simple" design would probably be for opDynamic's implementation to make the call on whether to forward to opDot's result; aka, push the decision to the programmer.  Stick a mixin somewhere for the most basic case (what I showed above) and its no big deal.

-- Chris Nicholson-Sauls
November 28, 2009
On Sat, 28 Nov 2009 10:16:33 +0300, Chris Nicholson-Sauls <ibisbasenji@gmail.com> wrote:

> Walter Bright wrote:
>> One thing Java and Python, Ruby, etc., still hold over D is dynamic classes, i.e. classes that are only known at runtime, not compile time. In D, this:
>>     s.foo(3);
>>  could be emulated with:
>>     s.dynamicMethod("foo", 3);
>>  Unfortunately, that makes it impossible to use s with generic code (besides looking unappealing). But with a small feature, we can make this work:
>>     struct S
>>    {
>>         ...
>>     T opDynamic(s : string)(args...);
>>    }
>>  and then s.foo(3), if foo is not a compile time member of s, is rewritten as:
>>     s.opDynamic!("foo")(3);
>>  and opDynamic defers all the nuts-and-bolts of making this work out of the language and into the library.
>>  In particular, opDynamic's parameter and return types should all be instances of std.variant.
>>  (This has come up in various forms in this n.g. before, but I don't have any references handy.)
>
> Seems fine, but how will this interact with "alias...this" and opDot?  The former seems simple enough: if the "alias...this" field provides the member, use that, otherwise fall back on opDynamic.  The latter seems iffy, though.  Maybe something like this:
>
> 	// if the return type of opDot provides the member...
> 	(auto tmp = s.opDot, tmp ? tmp.foo(3) : s.opDynamic!"foo"(3))
>
> Hmm... ew... but I can't think of anything better off-hand.  The "simple" design would probably be for opDynamic's implementation to make the call on whether to forward to opDot's result; aka, push the decision to the programmer.  Stick a mixin somewhere for the most basic case (what I showed above) and its no big deal.
>
> -- Chris Nicholson-Sauls

I think opDot should be deprecated and eventually removed. Never used it since alias this was introduced. Why would you use it?
November 28, 2009
On 2009-11-27 18:30:14 -0500, Walter Bright <newshound1@digitalmars.com> said:

> But with a small feature, we can make this work:
> 
>     struct S
>     {
>          ...
> 	T opDynamic(s : string)(args...);
>     }
> 
> and then s.foo(3), if foo is not a compile time member of s, is rewritten as:
> 
>     s.opDynamic!("foo")(3);
> 
> and opDynamic defers all the nuts-and-bolts of making this work out of the language and into the library.

Please make sure it can work to implement properties too.

The only thing that worries me is that "functions" defined through opDynamic won't be reachable via reflection. There's no way to call "foo" if "foo" is a runtime string; with regular functions you can use compile-time reflection to build a dispatch table, but for those implemented through opDynamic (which are not available through reflection) it won't work.

Also, I would name it "opDispatch" instead. I fail to see anything "dymamic" in it... it's a template so it's static isn't it? Of course you can implement dynamic dispatch with this, but that's not a requirement.


> In particular, opDynamic's parameter and return types should all be instances of std.variant.

That seems unnecessary. It's a template, so you should be able to define opDynamic like this:

	auto opDynamic(s : string, A...)(A args) { return args[0]; }


-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

November 28, 2009
dsimcha wrote:
> == Quote from Walter Bright (newshound1@digitalmars.com)'s article
>> dsimcha wrote:
>>> Sometimes I feel like there should be a law similar to Greenspun's Law for
> 
> Having never done it before, I really cannot imagine how people get any work done in a language that doesn't have either duck typing or good templates.  It's just too rigid.  It seems like modern statically typed languages like Java and C# end up adding tons of ad-hoc workarounds for lacking either of these as well-integrated language features.  The best/worst example is auto-boxing.

Which is why the .NET framework is so bloody huge.
If MS hadn't provided that out of the box in the first version, .NET
would have been a dead duck.

- --
My enormous talent is exceeded only by my outrageous laziness.
http://www.ssTk.co.uk
November 28, 2009
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
November 29, 2009
Le 29/11/09 00:36, Walter Bright a écrit :
> 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
>

Seems interesting, but for now the error message when no opDispatch template can be instantiated looks confusing when trying to use a class with an opDispatch implemented, and making e.g. a typo error:

=============================================
module lib;
class Test
{
    string opDispatch(string name)()
    {
        static if (name == "foo")
            return "foo";
    }
}
=============================================
module main;
import lib;
import std.stdio;

void main()
{
    auto test = new Test;
    writeln(test.foo); // OK
    writeln(test.fooo); // Error
}
=============================================

Error is: """
lib.d(5): Error: function lib.Test.opDispatch!("fooo").opDispatch expected to return a value of type string
lib.d(9): Error: template instance lib.Test.opDispatch!("fooo") error instantiating
"""

nicolas


November 29, 2009
On Sun, 29 Nov 2009 11:44:56 +0100, biozic <dransic@free.fr> wrote:

> Le 29/11/09 00:36, Walter Bright a écrit :
>> 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
>>
>
> Seems interesting, but for now the error message when no opDispatch template can be instantiated looks confusing when trying to use a class with an opDispatch implemented, and making e.g. a typo error:
>
> =============================================
> module lib;
> class Test
> {
>      string opDispatch(string name)()
>      {
>          static if (name == "foo")
>              return "foo";
>      }
> }
> =============================================
> module main;
> import lib;
> import std.stdio;
>
> void main()
> {
>      auto test = new Test;
>      writeln(test.foo); // OK
>      writeln(test.fooo); // Error
> }
> =============================================
>
> Error is: """
> lib.d(5): Error: function lib.Test.opDispatch!("fooo").opDispatch expected to return a value of type string
> lib.d(9): Error: template instance lib.Test.opDispatch!("fooo") error instantiating
> """
>
> nicolas

That is because your opDispatch is instantiated no matter what the name is, but only does something sensible if it's foo. Try this:

string opDispatch( string name )( ) {
  static if ( name == "foo" ) {
    return "foo";
  } else {
    static assert( false, "Invalid member name." );
  }
}

-- 
Simen
November 29, 2009
Le 29/11/09 12:14, Simen kjaeraas a écrit :
> That is because your opDispatch is instantiated no matter what the name
> is, but only does something sensible if it's foo. Try this:
>
> string opDispatch( string name )( ) {
> static if ( name == "foo" ) {
> return "foo";
> } else {
> static assert( false, "Invalid member name." );
> }
> }
>

Ok but what still looks confusing is that the error is reported on the template code, as for any template instantiation error, while the user could not be aware of being instantiating a template (or should he?).

Anyway, this feature is fun to play with.