View mode: basic / threaded / horizontal-split · Log in · Help
November 02, 2010
The Expressiveness of D
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; };
}
November 02, 2010
Re: The Expressiveness of D
%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)
November 02, 2010
Re: The Expressiveness of D
%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
November 02, 2010
Re: The Expressiveness of D
%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.
November 02, 2010
Re: The Expressiveness of D
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
> 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.
>
November 02, 2010
Re: The Expressiveness of D
> // 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.
November 02, 2010
Re: The Expressiveness of D
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.
November 02, 2010
Re: The Expressiveness of D
]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
November 02, 2010
Re: The Expressiveness of D
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.
November 02, 2010
Re: The Expressiveness of D
%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
« First   ‹ Prev
1 2 3
Top | Discussion index | About this forum | D home