www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The Expressiveness of D

reply %u <user web.news> writes:
I found a slideshow called 'The Expressiveness of Go' recently. The conclusions
are:

* Go is not a small language but it is an expressive and comprehensible one.

* Expressiveness comes from orthogonal composition of constructs.

* Comprehensibility comes from simple constructs that interact in easily
understood ways.

* Build a language from simple orthogonal constructs and you have a language
that will be easy and productive to use.

* The surprises you discover will be pleasant ones.

----

Is D orthogonal? Could it be more orthogonal? Two things come to my mind:
removing special cases and making widely used things first class. For data
types this means that they have literals, can be given to functions and
returned from functions. I made a small test and found that the discoveries
aren't pleasant to me:


class A {}
class B : A {}
class C : A {}

template T(A...) { alias A T; }

void main() {
  auto a = true ? new B : new C;
// these don't work - why?
//  auto b = [new B, new C];
//  auto c = { return [1: new B,2: new C]; };

  T!(int,int) e = (1,2);
  e = T!(3,4);

// ah - so (1,2) syntax on initialization, T!(1,2) when assigning!
  T!(int,int) d = T!(1,2);

  e = d;

// tuples aren't first class, why?
//  auto f = { return e; };
}
Nov 02 2010
next sibling parent reply %u <user web.news> writes:
%u Wrote:

 I found a slideshow called 'The Expressiveness of Go' recently. The
conclusions are:
 
 * Go is not a small language but it is an expressive and comprehensible one.
 
 * Expressiveness comes from orthogonal composition of constructs.
 
 * Comprehensibility comes from simple constructs that interact in easily
understood ways.
 
 * Build a language from simple orthogonal constructs and you have a language
that will be easy and productive to use.
 
 * The surprises you discover will be pleasant ones.
 
 ----
 
 Is D orthogonal? Could it be more orthogonal? Two things come to my mind:
removing special cases and making widely used things first class. For data
types this means that they have literals, can be given to functions and
returned from functions. I made a small test and found that the discoveries
aren't pleasant to me:
 
 
 class A {}
 class B : A {}
 class C : A {}
 
 template T(A...) { alias A T; }
 
 void main() {
   auto a = true ? new B : new C;
 // these don't work - why?
 //  auto b = [new B, new C];
 //  auto c = { return [1: new B,2: new C]; };
 
   T!(int,int) e = (1,2);
   e = T!(3,4);
 
 // ah - so (1,2) syntax on initialization, T!(1,2) when assigning!
   T!(int,int) d = T!(1,2);
 
   e = d;
 
 // tuples aren't first class, why?
 //  auto f = { return e; };
 }

I then test this in Scala REPL: scala> class A; class B extends A; class C extends A defined class A defined class B defined class C scala> val a = List(new B,new C) a: List[A] = List(B 1f18cd5, C 154f6ff) scala> val b = Map(1 -> new B, 2 -> new C) b: scala.collection.immutable.Map[Int,A] = Map((1,B 14512e), (2,C 1ddbcb1)) scala> var e = (1,2) e: (Int, Int) = (1,2) scala> e = (3,4) e: (Int, Int) = (3,4) scala> val d = (1,2) d: (Int, Int) = (1,2) scala> e = d e: (Int, Int) = (1,2) scala> val f = () => e f: () => (Int, Int) = <function0> scala> f() res0: (Int, Int) = (1,2)
Nov 02 2010
next sibling parent reply Gary Whatmore <no spam.sp> writes:
%u Wrote:

 %u Wrote:
 
 I found a slideshow called 'The Expressiveness of Go' recently. The
conclusions are:
 
 * Go is not a small language but it is an expressive and comprehensible one.
 
 * Expressiveness comes from orthogonal composition of constructs.
 
 * Comprehensibility comes from simple constructs that interact in easily
understood ways.
 
 * Build a language from simple orthogonal constructs and you have a language
that will be easy and productive to use.
 
 * The surprises you discover will be pleasant ones.
 
 ----
 
 Is D orthogonal? Could it be more orthogonal? Two things come to my mind:
removing special cases and making widely used things first class. For data
types this means that they have literals, can be given to functions and
returned from functions. I made a small test and found that the discoveries
aren't pleasant to me:
 
 
 class A {}
 class B : A {}
 class C : A {}
 
 template T(A...) { alias A T; }
 
 void main() {
   auto a = true ? new B : new C;
 // these don't work - why?
 //  auto b = [new B, new C];
 //  auto c = { return [1: new B,2: new C]; };
 
   T!(int,int) e = (1,2);
   e = T!(3,4);
 
 // ah - so (1,2) syntax on initialization, T!(1,2) when assigning!
   T!(int,int) d = T!(1,2);
 
   e = d;
 
 // tuples aren't first class, why?
 //  auto f = { return e; };
 }

I then test this in Scala REPL: scala> class A; class B extends A; class C extends A defined class A defined class B defined class C scala> val a = List(new B,new C) a: List[A] = List(B 1f18cd5, C 154f6ff) scala> val b = Map(1 -> new B, 2 -> new C) b: scala.collection.immutable.Map[Int,A] = Map((1,B 14512e), (2,C 1ddbcb1)) scala> var e = (1,2) e: (Int, Int) = (1,2) scala> e = (3,4) e: (Int, Int) = (3,4) scala> val d = (1,2) d: (Int, Int) = (1,2) scala> e = d e: (Int, Int) = (1,2) scala> val f = () => e f: () => (Int, Int) = <function0> scala> f() res0: (Int, Int) = (1,2)

My eyes radiate red light of hate every time I see these comparisons between D and some slower virtual machine language. Of course the virtual machine languages are simpler to use and look nice. That's their way to lure you into using them. Boom! Suddenly 200% larger memory usage and 50% to 90% of the processing power is lost. Building a native language doesn't have all mumbo jumbo JIT daemons running there. Thus the code has to optimized at compile time and that's why those codes above are more complex in D and don't work. - G.W.
Nov 02 2010
parent Gary Whatmore <no spam.sp> writes:
The D way of returning tuples is:

  T!(int,int) ret;
  auto f = (ref T!(int,int) r){ r = e; };
  f(ret);

It doesn't look so bad if you think about it. The tuple is first stack
allocated. It doesn't trigger heap allocation. High level scripting languages
always cause extra heap allocation if you do

auto f = { return (1,2); };

It's much faster the D way.
Nov 02 2010
prev sibling next sibling parent Michael Stover <michael.r.stover gmail.com> writes:
--0016e64654625e5d500494120601
Content-Type: text/plain; charset=ISO-8859-1

I don't see how compiling to native prevents the source code from expressing
high-level concepts in a reasonable way.  Lisp is compiled to naitve code.
 Even Java can be compiled to native code.  And frankly, that has little to
do with speed and memory usage.  The memory usage is largely from the
garbage collector, and thus is still there even when compiling to native.  I
imagine D has that issue too.

-Mike

On Tue, Nov 2, 2010 at 9:27 AM, Gary Whatmore <no spam.sp> wrote:

 %u Wrote:

 %u Wrote:

 I found a slideshow called 'The Expressiveness of Go' recently. The


 * Go is not a small language but it is an expressive and comprehensible


 * Expressiveness comes from orthogonal composition of constructs.

 * Comprehensibility comes from simple constructs that interact in


 * Build a language from simple orthogonal constructs and you have a


 * The surprises you discover will be pleasant ones.

 ----

 Is D orthogonal? Could it be more orthogonal? Two things come to my


data types this means that they have literals, can be given to functions and returned from functions. I made a small test and found that the discoveries aren't pleasant to me:
 class A {}
 class B : A {}
 class C : A {}

 template T(A...) { alias A T; }

 void main() {
   auto a = true ? new B : new C;
 // these don't work - why?
 //  auto b = [new B, new C];
 //  auto c = { return [1: new B,2: new C]; };

   T!(int,int) e = (1,2);
   e = T!(3,4);

 // ah - so (1,2) syntax on initialization, T!(1,2) when assigning!
   T!(int,int) d = T!(1,2);

   e = d;

 // tuples aren't first class, why?
 //  auto f = { return e; };
 }

I then test this in Scala REPL: scala> class A; class B extends A; class C extends A defined class A defined class B defined class C scala> val a = List(new B,new C) a: List[A] = List(B 1f18cd5, C 154f6ff) scala> val b = Map(1 -> new B, 2 -> new C) b: scala.collection.immutable.Map[Int,A] = Map((1,B 14512e),

 scala> var e = (1,2)
 e: (Int, Int) = (1,2)

 scala> e = (3,4)
 e: (Int, Int) = (3,4)

 scala> val d = (1,2)
 d: (Int, Int) = (1,2)

 scala> e = d
 e: (Int, Int) = (1,2)

 scala> val f = () => e
 f: () => (Int, Int) = <function0>

 scala> f()
 res0: (Int, Int) = (1,2)

My eyes radiate red light of hate every time I see these comparisons between D and some slower virtual machine language. Of course the virtual machine languages are simpler to use and look nice. That's their way to lure you into using them. Boom! Suddenly 200% larger memory usage and 50% to 90% of the processing power is lost. Building a native language doesn't have all mumbo jumbo JIT daemons running there. Thus the code has to optimized at compile time and that's why those codes above are more complex in D and don't work. - G.W.

--0016e64654625e5d500494120601 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable I don&#39;t see how compiling to native prevents the source code from expre= ssing high-level concepts in a reasonable way. =A0Lisp is compiled to naitv= e code. =A0Even Java can be compiled to native code. =A0And frankly, that h= as little to do with speed and memory usage. =A0The memory usage is largely= from the garbage collector, and thus is still there even when compiling to= native. =A0I imagine D has that issue too.<div> <br></div><div>-Mike<br><br><div class=3D"gmail_quote">On Tue, Nov 2, 2010 = at 9:27 AM, Gary Whatmore <span dir=3D"ltr">&lt;no spam.sp&gt;</span> wrote= :<br><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-le= ft:1px #ccc solid;padding-left:1ex;"> <div><div></div><div class=3D"h5">%u Wrote:<br> <br> &gt; %u Wrote:<br> &gt;<br> &gt; &gt; I found a slideshow called &#39;The Expressiveness of Go&#39; rec= ently. The conclusions are:<br> &gt; &gt;<br> &gt; &gt; * Go is not a small language but it is an expressive and comprehe= nsible one.<br> &gt; &gt;<br> &gt; &gt; * Expressiveness comes from orthogonal composition of constructs.= <br> &gt; &gt;<br> &gt; &gt; * Comprehensibility comes from simple constructs that interact in= easily understood ways.<br> &gt; &gt;<br> &gt; &gt; * Build a language from simple orthogonal constructs and you have= a language that will be easy and productive to use.<br> &gt; &gt;<br> &gt; &gt; * The surprises you discover will be pleasant ones.<br> &gt; &gt;<br> &gt; &gt; ----<br> &gt; &gt;<br> &gt; &gt; Is D orthogonal? Could it be more orthogonal? Two things come to = my mind: removing special cases and making widely used things first class. = For data types this means that they have literals, can be given to function= s and returned from functions. I made a small test and found that the disco= veries aren&#39;t pleasant to me:<br> &gt; &gt;<br> &gt; &gt;<br> &gt; &gt; class A {}<br> &gt; &gt; class B : A {}<br> &gt; &gt; class C : A {}<br> &gt; &gt;<br> &gt; &gt; template T(A...) { alias A T; }<br> &gt; &gt;<br> &gt; &gt; void main() {<br> &gt; &gt; =A0 auto a =3D true ? new B : new C;<br> &gt; &gt; // these don&#39;t work - why?<br> &gt; &gt; // =A0auto b =3D [new B, new C];<br> &gt; &gt; // =A0auto c =3D { return [1: new B,2: new C]; };<br> &gt; &gt;<br> &gt; &gt; =A0 T!(int,int) e =3D (1,2);<br> &gt; &gt; =A0 e =3D T!(3,4);<br> &gt; &gt;<br> &gt; &gt; // ah - so (1,2) syntax on initialization, T!(1,2) when assigning= !<br> &gt; &gt; =A0 T!(int,int) d =3D T!(1,2);<br> &gt; &gt;<br> &gt; &gt; =A0 e =3D d;<br> &gt; &gt;<br> &gt; &gt; // tuples aren&#39;t first class, why?<br> &gt; &gt; // =A0auto f =3D { return e; };<br> &gt; &gt; }<br> &gt;<br> &gt; I then test this in Scala REPL:<br> &gt;<br> &gt; scala&gt; class A; class B extends A; class C extends A<br> &gt; defined class A<br> &gt; defined class B<br> &gt; defined class C<br> &gt;<br> &gt; scala&gt; val a =3D List(new B,new C)<br> &gt; a: List[A] =3D List(B 1f18cd5, C 154f6ff)<br> &gt;<br> &gt; scala&gt; val b =3D Map(1 -&gt; new B, 2 -&gt; new C)<br> &gt; b: scala.collection.immutable.Map[Int,A] =3D Map((1,B 14512e), (2,C 1d= dbcb1))<br> &gt;<br> &gt; scala&gt; var e =3D (1,2)<br> &gt; e: (Int, Int) =3D (1,2)<br> &gt;<br> &gt; scala&gt; e =3D (3,4)<br> &gt; e: (Int, Int) =3D (3,4)<br> &gt;<br> &gt; scala&gt; val d =3D (1,2)<br> &gt; d: (Int, Int) =3D (1,2)<br> &gt;<br> &gt; scala&gt; e =3D d<br> &gt; e: (Int, Int) =3D (1,2)<br> &gt;<br> &gt; scala&gt; val f =3D () =3D&gt; e<br> &gt; f: () =3D&gt; (Int, Int) =3D &lt;function0&gt;<br> &gt;<br> &gt; scala&gt; f()<br> &gt; res0: (Int, Int) =3D (1,2)<br> <br> </div></div>My eyes radiate red light of hate every time I see these compar= isons between D and some slower virtual machine language. Of course the vir= tual machine languages are simpler to use and look nice. That&#39;s their w= ay to lure you into using them. Boom! Suddenly 200% larger memory usage and= 50% to 90% of the processing power is lost. Building a native language doe= sn&#39;t have all mumbo jumbo JIT daemons running there. Thus the code has = to optimized at compile time and that&#39;s why those codes above are more = complex in D and don&#39;t work.<br> <font color=3D"#888888"><br> =A0- G.W.<br> </font></blockquote></div><br></div> --0016e64654625e5d500494120601--
Nov 02 2010
prev sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
]Gary Whatmore <no spam.sp> wrote:

 The D way of returning tuples is:

   T!(int,int) ret;
   auto f = (ref T!(int,int) r){ r = e; };
   f(ret);

D also has better tuples in std.typecons. auto t1 = tuple( 1,2 ); auto t2 = { return t1; } -- Simen
Nov 02 2010
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
%u:

 * The surprises you discover will be pleasant ones.

This is a very good note to keep in mind.
 // tuples aren't first class, why?

Maybe because D is mostly designed bottom up, low level considerations guide the design. This is positive because you end with an efficient language that requires just a "simple" back-end fit for a C compiler, but it misses a high level view of what's good for the programmer, and orthogonality is less common. Recently Walter has expressed the desire to improve tuples (I presume for D3): http://digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=118557 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=118558 I have given some answers, where I have put lot of thought and energy, like: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=118601 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=118711 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=118788 But the discussion has gone nowhere again, I don't know why (as for integer overflows and some other discussions). Bye, bearophile
Nov 02 2010
prev sibling next sibling parent reply Torarin <torarind gmail.com> writes:
 // these don't work - why?
 //  auto b = [new B, new C];
 //  auto c = { return [1: new B,2: new C]; };

That seems to be just a matter of improving the compiler to make it find the common type. I don't think there's anything in the language stopping that from working.
Nov 02 2010
next sibling parent reply Gary Whatmore <no spam.sp> writes:
Torarin Wrote:

 // these don't work - why?
 //  auto b = [new B, new C];
 //  auto c = { return [1: new B,2: new C]; };

That seems to be just a matter of improving the compiler to make it find the common type. I don't think there's anything in the language stopping that from working.

This is another one of those low priority issues. Never seen this kind of code in practice. These examples are highly synthetic and come from advocates of "clean" simple languages. "No corner cases" - does it matter if the language is full of broken corner cases if the practical real world code just works? D isn't nowhere near that bad. Other than that, I'm quite sure compiler construction isn't that straightforward. You can't use the same type deduction code of "? :" in this context. A functional kid might say, if you have infer(a,b) and reduce(), you can solve this, but you have to consider metaprogramming and all complex stuff in D. Otherwise this would work already.
Nov 02 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Gary Whatmore:

 This is another one of those low priority issues. Never seen this kind of code
in practice. These examples are highly synthetic and come from advocates of
"clean" simple languages. "No corner cases" - does it matter if the language is
full of broken corner cases if the practical real world code just works?

Yes, of course the number of broken corner cases matters a lot. Few corner cases are acceptable, too many and the language (es. Perl) becomes a mess that I will not use. ----------------------------- Andrei:
Orthogonality has been long debated in the PL community. Clearly in reasonable
quantities it's a desirable trait, but one must also know where to stop. There
are many orthogonal languages that people didn't quite enjoy.<

I agree. Scheme is a very orthogonal language, but I don't like it. You have to build all things by yourself from the offered little (orthogonal) parts.
 My suggestion to address this issue was (and is) to make the assigned
 part of the function call:
 
 int a, b;
 scatter(tuple(b, a), a, b);
 scatter(tuple(b, a + b), a, b);
 int[] arr = [0, 1, 2, 3, 4];
 int[] c;
 scatter(arr, a, b, c);

I have implemented something similar in dlibs1, but in practice I don't like to use that. Tuples aren't something you use once in a while, if they are handy and good they become a fundamental language construct, I use them every few lines of code in Python, in every kind of Python programs I write. So they deserve a handy syntax. So it's a situation where some syntax sugar support is warmly encouraged. We may wait for D3, if necessary. Bye, bearophile
Nov 02 2010
prev sibling next sibling parent Jesse Phillips <jessekphillips+D gmail.com> writes:
Torarin Wrote:

 // these don't work - why?
 //  auto b = [new B, new C];
 //  auto c = { return [1: new B,2: new C]; };

That seems to be just a matter of improving the compiler to make it find the common type. I don't think there's anything in the language stopping that from working.

I don't recall why it doesn't find the common type. But what it does do is use the last element to deduce the type, so there are two ways to make this work. auto b = [new B, cast(A) new C]; auto b = [new B, new C, new A]; What I don't really like is that it can't be fixed with: A[] b = [new B, new C];
Nov 02 2010
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 11/2/10 8:39 AM, Torarin wrote:
 // these don't work - why?
 //  auto b = [new B, new C];
 //  auto c = { return [1: new B,2: new C]; };

That seems to be just a matter of improving the compiler to make it find the common type. I don't think there's anything in the language stopping that from working.

Yah, in fact it's a recent addition that it even attempts to find the common type. While I was writing TDPL, the first draft mentioned the rule then in effect: the literal array type is exclusively determined by its first element. The common type rule was only introduced later (though in time to fix TDPL), so I'm not surprised there is a glitch in there. Andrei
Nov 02 2010
prev sibling next sibling parent reply "Simen kjaeraas" <simen.kjaras gmail.com> writes:
%u <user web.news> wrote:

 class A {}
 class B : A {}
 class C : A {}

 template T(A...) { alias A T; }

 void main() {
   auto a = true ? new B : new C;
 // these don't work - why?
 //  auto b = [new B, new C];
 //  auto c = { return [1: new B,2: new C]; };

"The type of the first element is taken to be the type of all the elements, and all elements are implicitly converted to that type." (http://digitalmars.com/d/2.0/expression.html#ArrayLiteral) I believe it has been discussed numerous times before that the ?: test should be used to find the element type - not sure why it isn't.
   T!(int,int) e = (1,2);
   e = T!(3,4);

 // ah - so (1,2) syntax on initialization, T!(1,2) when assigning!
   T!(int,int) d = T!(1,2);

   e = d;

 // tuples aren't first class, why?
 //  auto f = { return e; };
 }

Compile-time argument tuples are something of a strange beast. They behave not very unlike tuples, but due to their ability to hold types, literals, expressions and aliases to whatever, they are not a very good match for what you'd expect tuples to be. (e.g, what do you expect T!(3,T) to be?) For tuples, you should instead look into std.typecons' Tuple struct and tuple function: Tuple!( int, "n", string, "s" ) tup; tup.n = 4; tup.s = "A string! My kingdom for a string!"; auto tup2 = tuple( 1, 2 ); assert( is( typeof( tup2 ) == Tuple!( int, int ) ) ); For even better support of tuples, you should have a look-see at Philippe Sigaud's dranges.tuple and dranges.reftuple (http://svn.dsource.org/projects/dranges/trunk/dranges/docs/tuple.html and http://svn.dsource.org/projects/dranges/trunk/dranges/docs/reftuple.html) The latter is absolutely awesome, and should be made part of phobos ASAP, IMO (though according to the documentation, it is not in tip-top shape). Examples from the reftuple page: int a, b; _(a,b) = tuple(b,a); // swap _(a,b) = tuple(b,a+b); // fibonacci int[] arr = [0,1,2,3,4]; int [] c; _(a,b,c) = arr; // a = 0, b = 1, c = [2,3,4] -- Simen
Nov 02 2010
next sibling parent %u <user web.news> writes:
Simen kjaeraas Wrote:

 Examples from the reftuple page:
 
 
 int a, b;
 _(a,b) = tuple(b,a); // swap
 _(a,b) = tuple(b,a+b); // fibonacci
 
 int[] arr = [0,1,2,3,4];
 int [] c;
 _(a,b,c) = arr; // a = 0, b = 1, c = [2,3,4]

This is close to what I had in mind, thanks! I just find it odd.. arr is int[] a, b are int and c is int[] int[] becomes int, int, and int[] Does the same work if I want to extract tuple elements in similar way? Does it support nested tuples well? Gotta try.
Nov 02 2010
prev sibling next sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
Simen kjaeraas wrote:

 %u <user web.news> wrote:
 
 class A {}
 class B : A {}
 class C : A {}

 template T(A...) { alias A T; }

 void main() {
   auto a = true ? new B : new C;
 // these don't work - why?
 //  auto b = [new B, new C];
 //  auto c = { return [1: new B,2: new C]; };

"The type of the first element is taken to be the type of all the elements, and all elements are implicitly converted to that type." (http://digitalmars.com/d/2.0/expression.html#ArrayLiteral) I believe it has been discussed numerous times before that the ?: test should be used to find the element type - not sure why it isn't.

It is stated in TDPL that the ?: test should be used, and it already works: auto a = [1,2,3.5]; pragma(msg, typeof(a).stringof); // prints double[], not int[]
 
   T!(int,int) e = (1,2);
   e = T!(3,4);

 // ah - so (1,2) syntax on initialization, T!(1,2) when assigning!
   T!(int,int) d = T!(1,2);

   e = d;

 // tuples aren't first class, why?
 //  auto f = { return e; };
 }

Compile-time argument tuples are something of a strange beast. They behave not very unlike tuples, but due to their ability to hold types, literals, expressions and aliases to whatever, they are not a very good match for what you'd expect tuples to be. (e.g, what do you expect T!(3,T) to be?) For tuples, you should instead look into std.typecons' Tuple struct and tuple function: Tuple!( int, "n", string, "s" ) tup; tup.n = 4; tup.s = "A string! My kingdom for a string!"; auto tup2 = tuple( 1, 2 ); assert( is( typeof( tup2 ) == Tuple!( int, int ) ) ); For even better support of tuples, you should have a look-see at Philippe Sigaud's dranges.tuple and dranges.reftuple (http://svn.dsource.org/projects/dranges/trunk/dranges/docs/tuple.html and http://svn.dsource.org/projects/dranges/trunk/dranges/docs/reftuple.html) The latter is absolutely awesome, and should be made part of phobos ASAP, IMO (though according to the documentation, it is not in tip-top shape).

vote++
Nov 02 2010
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 11/2/10 9:07 AM, Simen kjaeraas wrote:
 %u <user web.news> wrote:

 class A {}
 class B : A {}
 class C : A {}

 template T(A...) { alias A T; }

 void main() {
 auto a = true ? new B : new C;
 // these don't work - why?
 // auto b = [new B, new C];
 // auto c = { return [1: new B,2: new C]; };

"The type of the first element is taken to be the type of all the elements, and all elements are implicitly converted to that type." (http://digitalmars.com/d/2.0/expression.html#ArrayLiteral) I believe it has been discussed numerous times before that the ?: test should be used to find the element type - not sure why it isn't.

Looks like outdated documentation to me.
 T!(int,int) e = (1,2);
 e = T!(3,4);

 // ah - so (1,2) syntax on initialization, T!(1,2) when assigning!
 T!(int,int) d = T!(1,2);

 e = d;

 // tuples aren't first class, why?
 // auto f = { return e; };
 }

Compile-time argument tuples are something of a strange beast. They behave not very unlike tuples, but due to their ability to hold types, literals, expressions and aliases to whatever, they are not a very good match for what you'd expect tuples to be. (e.g, what do you expect T!(3,T) to be?) For tuples, you should instead look into std.typecons' Tuple struct and tuple function: Tuple!( int, "n", string, "s" ) tup; tup.n = 4; tup.s = "A string! My kingdom for a string!"; auto tup2 = tuple( 1, 2 ); assert( is( typeof( tup2 ) == Tuple!( int, int ) ) ); For even better support of tuples, you should have a look-see at Philippe Sigaud's dranges.tuple and dranges.reftuple (http://svn.dsource.org/projects/dranges/trunk/dranges/docs/tuple.html and http://svn.dsource.org/projects/dranges/trunk/dranges/docs/reftuple.html) The latter is absolutely awesome, and should be made part of phobos ASAP, IMO (though according to the documentation, it is not in tip-top shape). Examples from the reftuple page: int a, b; _(a,b) = tuple(b,a); // swap _(a,b) = tuple(b,a+b); // fibonacci int[] arr = [0,1,2,3,4]; int [] c; _(a,b,c) = arr; // a = 0, b = 1, c = [2,3,4]

This was already contributed to in Phobos (under a different syntax) but I rejected it on grounds of safety: the value built by "_" must escape addresses of all of its arguments, so it's not safe. My suggestion to address this issue was (and is) to make the assigned part of the function call: int a, b; scatter(tuple(b, a), a, b); scatter(tuple(b, a + b), a, b); int[] arr = [0, 1, 2, 3, 4]; int[] c; scatter(arr, a, b, c); Andrei
Nov 02 2010
prev sibling next sibling parent reply Lutger <lutger.blijdestijn gmail.com> writes:
%u wrote:
...
 
 template T(A...) { alias A T; }
 
 void main() {
   auto a = true ? new B : new C;
 // these don't work - why?
 //  auto b = [new B, new C];
 //  auto c = { return [1: new B,2: new C]; };

I think it should, it works for arithmetic types. See this bug report: http://d.puremagic.com/issues/show_bug.cgi?id=3543
   T!(int,int) e = (1,2);

I am surprised this works since D doesn't have a tuple literal syntax.
   e = T!(3,4);
 
 // ah - so (1,2) syntax on initialization, T!(1,2) when assigning!
   T!(int,int) d = T!(1,2);
 
   e = d;
 
 // tuples aren't first class, why?
 //  auto f = { return e; };
 }

There has been discussion about this, as mentioned by bearophile. In summary, the reason for not having first class tuples is that current language constructs provide sufficient means to express tuples as a library type, see http://www.digitalmars.com/d/2.0/phobos/std_typecons.html#Tuple. You can do most - but not all - of what builtin tuples could do with that template.
Nov 02 2010
parent %u <user web.news> writes:
Lutger Wrote:

 %u wrote:
 ...
 
 template T(A...) { alias A T; }
 
 void main() {
   auto a = true ? new B : new C;
 // these don't work - why?
 //  auto b = [new B, new C];
 //  auto c = { return [1: new B,2: new C]; };

I think it should, it works for arithmetic types. See this bug report: http://d.puremagic.com/issues/show_bug.cgi?id=3543

Thanks. I understand it's a known bug now. The explicit casts shown in this thread are what annoy me. In code review you have to add a comment "// totally unnecessary cast, but see dmd bug #3543". Confuses new users. Not anything serious and there's a workaround. Just annoys. Especially if code guide tells to avoid casts. Especially now that I found it by accident and always remember it.
  
   T!(int,int) e = (1,2);

I am surprised this works since D doesn't have a tuple literal syntax.

I actually found these examples when doing a google search. Wanted to see how tuples in D work. I didn't realize there's a library solution. We should probably remove the old newsgroup posts of this errorenous "tuple". If we can hide it well enough, new users won't even notice DMD accepts this confusing code.
 
   e = T!(3,4);
 
 // ah - so (1,2) syntax on initialization, T!(1,2) when assigning!
   T!(int,int) d = T!(1,2);
 
   e = d;
 
 // tuples aren't first class, why?
 //  auto f = { return e; };
 }

There has been discussion about this, as mentioned by bearophile. In summary, the reason for not having first class tuples is that current language constructs provide sufficient means to express tuples as a library type, see http://www.digitalmars.com/d/2.0/phobos/std_typecons.html#Tuple. You can do most - but not all - of what builtin tuples could do with that template.

Crazy.. yes, the library solution is enough to make the language orthogonal in this way. It just confuses me why dmd almost accepts what I did.
Nov 02 2010
prev sibling next sibling parent Torarin <torarind gmail.com> writes:
2010/11/2 Simen kjaeraas <simen.kjaras gmail.com>:
 I believe it has been discussed numerous times before that the ?:
 test should be used to find the element type - not sure why it
 isn't.

That is indeed what TDPL says.
Nov 02 2010
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 11/2/10 7:53 AM, %u wrote:
 I found a slideshow called 'The Expressiveness of Go' recently. The
conclusions are:

 * Go is not a small language but it is an expressive and comprehensible one.

 * Expressiveness comes from orthogonal composition of constructs.

 * Comprehensibility comes from simple constructs that interact in easily
understood ways.

 * Build a language from simple orthogonal constructs and you have a language
that will be easy and productive to use.

 * The surprises you discover will be pleasant ones.

 ----

The slides fared surprisingly poorly with reddit, though I'm not sure what that proves (if anything) :o).
 Is D orthogonal? Could it be more orthogonal?

Orthogonality has been long debated in the PL community. Clearly in reasonable quantities it's a desirable trait, but one must also know where to stop. There are many orthogonal languages that people didn't quite enjoy. The most orthogonal of all is Algol, but next to no one could bring themselves to actually use it. Humans are not orthogonal, our linguistic and related abilities don't thrive on orthogonality, so it's a tenuous case that orthogonality must be a be-all end-all of programming languages.
 Two things come to my mind: removing special cases and making widely used
things first class. For data types this means that they have literals, can be
given to functions and returned from functions. I made a small test and found
that the discoveries aren't pleasant to me:


 class A {}
 class B : A {}
 class C : A {}

 template T(A...) { alias A T; }

 void main() {
    auto a = true ? new B : new C;
 // these don't work - why?
 //  auto b = [new B, new C];

Compiler bug. Must deduce common type as A.
 //  auto c = { return [1: new B,2: new C]; };

Compiler bug again.
    T!(int,int) e = (1,2);
    e = T!(3,4);

Variadic alias tuples (incorrectly called type tuples) are one of the more awkward features of the language. Though I agree they should ideally be made more palatable, I suggest using Tuple instead: auto e = tuple(1, 2); e = tuple(3, 4);
 // ah - so (1,2) syntax on initialization, T!(1,2) when assigning!
    T!(int,int) d = T!(1,2);

auto d = tuple(1, 2);
    e = d;

 // tuples aren't first class, why?
 //  auto f = { return e; };
 }

This should work with the library tuples. Andrei
Nov 02 2010
prev sibling next sibling parent Leandro Lucarella <luca llucax.com.ar> writes:
Gary Whatmore, el  2 de noviembre a las 09:40 me escribiste:
 The D way of returning tuples is:
 
   T!(int,int) ret;
   auto f = (ref T!(int,int) r){ r = e; };
   f(ret);
 
 It doesn't look so bad if you think about it. The tuple is first stack
allocated. It doesn't trigger heap allocation. High level scripting languages
always cause extra heap allocation if you do
 
 auto f = { return (1,2); };
 
 It's much faster the D way.

What has returning a tuple has to do with heap allocation. Is like returning a struct, which you can. Not being able to return a tuple has no explanation performance-wide. If you are concerned about the copy, is the same with static arrays, which are value types in D2. So here something is not very consistent... -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- PROTESTA EN PLAZA DE MAYO: MUSICO SE COSIO LA BOCA -- Crónica TV
Nov 02 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:

 I believe it has been discussed numerous times before that the ?:
 test should be used to find the element type - not sure why it
 isn't.

Looks like outdated documentation to me.

Well, it certainly doesn't behave as it should, if %u's post is anything to go by. (and my testing of the same issue - it's not just %u) Filed as 5156.
 This was already contributed to in Phobos (under a different syntax) but  
 I rejected it on grounds of safety: the value built by "_" must escape  
 addresses of all of its arguments, so it's not safe.

True. Bummer. Hm, now, the intent of _ is that the constructed type should exist only as a temporary, I believe. Maybe it is possible to do this in a safer way. I actually have a solution, which I have posted here before, and I feel might be worthy of an enhancement request: that templates may be used as pseudo-types, in that a operator overloading should be allowed on non-type template instances: import std.typecons; import std.typetuple; import dranges.templates; import dranges.typetuple; template _( T... ) if ( allSatisfy!( isAlias, T ) ) { Tuple!( StaticMap!( TypeOf, T ) ) opAssign( Tuple!( StaticMap!( TypeOf, T ) ) args ) { foreach ( i, e; T ) { e = args[i]; } return args; } } unittest { int a = 1; b = 1; _!( a, b ) = tuple( b, a+b ); // Equivalent to _!( a, b ).opAssign( b, a+b ); } Sadly, this currently does not work, not just due to the aforementioned limitation, but also because the reassigned values are discarded as the function returns. This latter part is confusing me. Is this intended behavior? A different solution might be ephemeral, marking types that are not supposed to be stored anywhere, and statically disallowing their use as variables.
 My suggestion to address this issue was (and is) to make the assigned  
 part of the function call:

 int a, b;
 scatter(tuple(b, a), a, b);
 scatter(tuple(b, a + b), a, b);
 int[] arr = [0, 1, 2, 3, 4];
 int[] c;
 scatter(arr, a, b, c);

That certainly works, but it is not as prettiful or straightforward. -- Simen
Nov 02 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Lutger <lutger.blijdestijn gmail.com> wrote:

 It is stated in TDPL that the ?: test should be used, and it already  
 works:

 auto a = [1,2,3.5];
 pragma(msg, typeof(a).stringof); // prints double[], not int[]

So it works for things that are not classes, then. That, to me, counts as 'does not work correctly'. Filed as 5156. -- Simen
Nov 02 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Simen kjaeraas <simen.kjaras gmail.com> wrote:

 Sadly, this currently does not work, not just due to the aforementioned
 limitation, but also because the reassigned values are discarded as the
 function returns. This latter part is confusing me. Is this intended
 behavior?

Curiouser: T[i] = args[i]; works. -- Simen
Nov 02 2010
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"%u" <user web.news> wrote in message news:iap1l4$17hk$1 digitalmars.com...
I found a slideshow called 'The Expressiveness of Go' recently. The 
conclusions are:

 * Go is not a small language but it is an expressive and comprehensible 
 one.

 * Expressiveness comes from orthogonal composition of constructs.

 * Comprehensibility comes from simple constructs that interact in easily 
 understood ways.

 * Build a language from simple orthogonal constructs and you have a 
 language that will be easy and productive to use.

 * The surprises you discover will be pleasant ones.

I know how much the Unix creators (ie, Go creators) *love* taking orthogonality to extremes. I find that leads to puritanical languages that actively avoid pragmatism (ie, some of the worst kinds of languages). Orthogonality is good for *machines*, but not quite as much for humans (in moderation, yes, in large doses, no). Even programmers aren't as orthogonally-minded as we often think we are. It's a bad idea for them, and it's just gonna lead to another Java/Smalltalk/Haskel/etc, and we've already got a million of those, we certainly don't need yet another. I find it really odd that no matter how many times people keep trying that purity-not-pragmatic approach to language design and end up with junk, others still keep trying to make "better" languages by using the same damn ideology that led to the problems in the first place.
Nov 02 2010
next sibling parent dsimcha <dsimcha yahoo.com> writes:
== Quote from Nick Sabalausky (a a.a)'s article
 "%u" <user web.news> wrote in message news:iap1l4$17hk$1 digitalmars.com...
I found a slideshow called 'The Expressiveness of Go' recently. The
conclusions are:

 * Go is not a small language but it is an expressive and comprehensible
 one.

 * Expressiveness comes from orthogonal composition of constructs.

 * Comprehensibility comes from simple constructs that interact in easily
 understood ways.

 * Build a language from simple orthogonal constructs and you have a
 language that will be easy and productive to use.

 * The surprises you discover will be pleasant ones.

orthogonality to extremes. I find that leads to puritanical languages that actively avoid pragmatism (ie, some of the worst kinds of languages). Orthogonality is good for *machines*, but not quite as much for humans (in moderation, yes, in large doses, no). Even programmers aren't as orthogonally-minded as we often think we are. It's a bad idea for them, and it's just gonna lead to another Java/Smalltalk/Haskel/etc, and we've already got a million of those, we certainly don't need yet another. I find it really odd that no matter how many times people keep trying that purity-not-pragmatic approach to language design and end up with junk, others still keep trying to make "better" languages by using the same damn ideology that led to the problems in the first place.

The big problem with an excessive focus on orthogonality is that you lose the ability to program at a wide range of abstraction levels in the same language, because the different levels will inevitably clash in some odd corner cases. IMHO, though, these corner cases are worth having in exchange for the ability to program at a variety of abstraction levels in one language.
Nov 02 2010
prev sibling next sibling parent retard <re tard.com.invalid> writes:
Tue, 02 Nov 2010 17:00:06 -0400, Nick Sabalausky wrote:

 "%u" <user web.news> wrote in message
 news:iap1l4$17hk$1 digitalmars.com...
I found a slideshow called 'The Expressiveness of Go' recently. The
conclusions are:

 * Go is not a small language but it is an expressive and comprehensible
 one.

 * Expressiveness comes from orthogonal composition of constructs.

 * Comprehensibility comes from simple constructs that interact in
 easily understood ways.

 * Build a language from simple orthogonal constructs and you have a
 language that will be easy and productive to use.

 * The surprises you discover will be pleasant ones.

orthogonality to extremes. I find that leads to puritanical languages that actively avoid pragmatism (ie, some of the worst kinds of languages). Orthogonality is good for *machines*, but not quite as much for humans (in moderation, yes, in large doses, no). Even programmers aren't as orthogonally-minded as we often think we are. It's a bad idea for them, and it's just gonna lead to another Java/Smalltalk/Haskel/etc, and we've already got a million of those, we certainly don't need yet another. I find it really odd that no matter how many times people keep trying that purity-not-pragmatic approach to language design and end up with junk, others still keep trying to make "better" languages by using the same damn ideology that led to the problems in the first place.

I don't know/like Go that much, but I think the bugs %u listed had some merit. Even if they are compiler bugs, they add inconsistensies to the language. I don't know about the tuple stuff.. having _(...) on the left hand side and tuple(...) on the right hand side seems unlogical, but if that's the way it works, it's a simple rule. All kinds of extremes are bad. Scheme, Io, Unlambda, Intercal, Whitespace, .. all have a over simplistic syntax. It restricts way too much. APL, OTOH, has too much syntax for general purpose work. Same thing with type systems. Untyped or very weakly typed languages are dangerous. Extremely strongly typed languages make it challenging to write anything that compiles.
Nov 02 2010
prev sibling parent Jesse Phillips <jessekphillips+D gmail.com> writes:
Nick Sabalausky Wrote:

 I know how much the Unix creators (ie, Go creators) *love* taking 
 orthogonality to extremes. I find that leads to puritanical languages that 
 actively avoid pragmatism (ie, some of the worst kinds of languages). 
 Orthogonality is good for *machines*, but not quite as much for humans (in 
 moderation, yes, in large doses, no). Even programmers aren't as 
 orthogonally-minded as we often think we are. It's a bad idea for them, and 
 it's just gonna lead to another Java/Smalltalk/Haskel/etc, and we've already 
 got a million of those, we certainly don't need yet another. I find it 
 really odd that no matter how many times people keep trying that 
 purity-not-pragmatic approach to language design and end up with junk, 
 others still keep trying to make "better" languages by using the same damn 
 ideology that led to the problems in the first place.

If find that how people classify whether something is orthogonal to be completely inconsistent. For example it could be said that 'static if' is not orthogonal because you can't do static else or static foreach. But the feature isn't 'static' it is 'static if' and 'static if foreach' doesn't make sense. I can't say being orthogonal is a bad thing, but if it is the only reason not to add a feature then reason is no longer part of the discussion making. I found this interesting piece on type inference of C#. Type inference will work on a lambda but not when overload resolution of methods might happen at some point. http://blogs.msdn.com/b/ericlippert/archive/2007/11/05/c-3-0-return-type-inference-does-not-work-on-member-groups.aspx D takes the approach of only complaining when two matches are equal or come from separate modules.
Nov 02 2010