Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-10 Thread JR via Digitalmars-d-learn

On Monday, 7 July 2014 at 23:47:26 UTC, Aerolite wrote:
So, if you would be so kind, give me a bullet list of the 
aspects of D you believe to be good, awesome, bad, and/or ugly. 
If you have the time, some code examples wouldn't go amiss 
either! Try not to go in-depth to weird edge cases - remain 
general, yet informative. E.g. I consider D's string mixins to 
be in the 'awesome' category, but its reliance on the GC for 
large segments of the standard library to be in the 'ugly' 
category.


I'm a big fan of (templated) UFCS. Along with parameterless 
function calls it goes a long way toward improving readability 
with its pipe-like flow. And like with all templates, allowing 
for breaking out common functionality *without* resorting to 
inheritance. Templates overall are sexy.



void main() {
import std.stdio;
import std.conv;
import std.string;
import std.range;
import core.thread;

// values known at compile-time --> CTFE kicks in~

static assert(12345.to!string == "12345");

immutable period = 1.seconds + 100.msecs + 100.usecs;
writeln(period);
Thread.sleep(period);

immutable line = "fedcba"
.retro
.to!string
.toUpper;

// wish this worked: typeof(line).is!string;
static assert(is(typeof(line) : string));
static assert(line == "ABCDEF");
}


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-10 Thread Dominikus Dittes Scherkl via Digitalmars-d-learn

So at all the implementation will look something like this:

int opCmp(T, U)(const(T) a, const(U) b) @primitive 
if(isIntegral!T && isIntegral!U)

{
   alias CommonType!(Signed!T, Signed!U) C;

   static if(isSigned!T && isUnsigned!U)
   {
  return (b > cast(Unsigned!C)C.max) ? -1 : cast(C)a - 
cast(C)b;

   }
   else static if(isUnsigned!T && isSigned!U)
   {
  return (a > cast(Unsigned!C)C.max) ? 1 : cast(C)a - 
cast(C)b;

   }
   else // both signed or both unsigned
   {
  return cast(C)a - cast(C)b;
   }
}

And it will be just as fast as ever, except if you compare apples 
with peaches where it take a tick longer but give the correct 
result anyway


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-10 Thread Dominikus Dittes Scherkl via Digitalmars-d-learn

Should of course be:

int compare2(uint x, int y)
{
   return (x > int.max) ? 1 : (cast(int)x - y);
}


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-10 Thread Dominikus Dittes Scherkl via Digitalmars-d-learn
On Wednesday, 9 July 2014 at 19:54:47 UTC, H. S. Teoh via 
Digitalmars-d-learn wrote:

[...]
The problem is that the function needs to return
int, but given two uints, their difference may be
greater than int.max, so simply subtracting them
will not work. So the best I can come up with is:

int compare2(int x, uint y)
{
return (x < 0) ? -1 :
(y > int.max) ? -1 :
(x - y);
}

which requires 2 comparisons.


Hmm. Diff works for compare(int,int).
So how about this:

int compare2(int x, uint y)
{
   return (y > int.max) ? -1 : (x - cast(int)y);
}

int compare2(uint x, int y)
{
   return (x > int.max) ? -1 : (cast(int)x - y);
}


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-09 Thread H. S. Teoh via Digitalmars-d-learn
On Wed, Jul 09, 2014 at 12:53:08PM -0700, H. S. Teoh via Digitalmars-d-learn 
wrote:
[...]
> (with gdc -O3 -funittest:)
> 
>   non-branching compare(signed,unsigned): 516 msecs
>   branching compare(signed,unsigned): 1209 msecs
>   non-branching compare(unsigned,signed): 453 msecs
>   branching compare(unsigned,signed): 756 msecs
>   Optimizer-thwarting value: 0
> 
> (Ignore the last lines of each output; that's just a way to prevent gdc
> -O3 from being over-eager and optimizing out the entire test so that
> everything returns 0 msecs.)
[...]

Argh. I just looked at the disassembly, and unfortunately, we have to
discard the test results for gdc, because gdc -O3 has apparently turned
on auto-*vectorising* optimizations, so the reason the non-branching
implementation runs so fast, is because multiple calls are being run in
parallel in the xmm* registers!

While this is certainly an impressive feat for gdc's optimizer, it
unfortunately also means the above benchmark doesn't reflect the actual
performance of standalone int/uint comparisons. :-(


T

-- 
I see that you JS got Bach.


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-09 Thread H. S. Teoh via Digitalmars-d-learn
On Wed, Jul 09, 2014 at 11:29:06AM -0700, H. S. Teoh via Digitalmars-d-learn 
wrote:
> On Wed, Jul 09, 2014 at 05:43:15PM +, Dominikus Dittes Scherkl via 
> Digitalmars-d-learn wrote:
> > On Wednesday, 9 July 2014 at 17:13:21 UTC, H. S. Teoh via
> > Digitalmars-d-learn wrote:
[...]
> > >Often, such kinds of hacks often perform more poorly than expected
> > >due to unforeseen complications with today's complex CPU's. So for
> > >all I know, I could've just been spouting nonsense above. :P)
> > I don't see such a compiler change as a hack. It is a strong
> > improvement IMHO.
> 
> I was talking about using | and & to get rid of the branch in
> signed/unsigned comparison. As it turns out, the compare(uint,int)
> case seems far more costly than a simple compare-and-branch as you had
> it at the beginning. So at least that part of what I wrote is probably
> nonsense.  :P
> 
> But I can't say for sure until I actually run some benchmarks on it.
[...]

Hmph. I'm having trouble coming up with a fair benchmark, because I
realized that D doesn't actually have a way of expressing opCmp for
unsigned int's in a minimal way! The problem is that the function needs
to return int, but given two uints, their difference may be greater than
int.max, so simply subtracting them will not work. So the best I can
come up with is:

int compare2(int x, uint y)
{
return (x < 0) ? -1 :
(y > int.max) ? -1 :
(x - y);
}

which requires 2 comparisons.

Similarly, for the uint-int case:

int compare2(uint x, int y)
{
return (y < 0) ? 1 :
(x > int.max) ? 1 :
(x - y);
}

If you have a better implementation in mind, I'm all ears.

In any case, I went ahead and benchmarked the above two functions along
with my branchless implementations, and here are the results:

(with dmd -O -unittest:)

non-branching compare(signed,unsigned): 5513 msecs
branching compare(signed,unsigned): 5442 msecs
non-branching compare(unsigned,signed): 5441 msecs
branching compare(unsigned,signed): 5744 msecs
Optimizer-thwarting value: 0

(with gdc -O3 -funittest:)

non-branching compare(signed,unsigned): 516 msecs
branching compare(signed,unsigned): 1209 msecs
non-branching compare(unsigned,signed): 453 msecs
branching compare(unsigned,signed): 756 msecs
Optimizer-thwarting value: 0

(Ignore the last lines of each output; that's just a way to prevent gdc
-O3 from being over-eager and optimizing out the entire test so that
everything returns 0 msecs.)

Interestingly, with dmd, the branching compare for the signed-unsigned
case is faster than my non-branching one, but the order is reversed for
the unsigned-signed case. They're pretty close, though, and on some runs
the order of the latter case is reversed.

With gdc, however, it seem the non-branching versions are clearly
better, even in the unsigned-signed case, which I thought would be
inferior. So clearly, these results are very optimizer-dependent.

Keep in mind, though, that this may not necessarily reflect actual
performance when the compiler generates the equivalent code for the
built-in integer comparison operators, because in codegen the compiler
can take advantage of the CPU's carry and overflow bits, and can elide
the actual return values of opCmp. This may skew the results enough to
reverse the order of some of these cases.

Anyway, here's the code (for independent verification):

int compare(int x, uint y)
{
enum signbitMask = 1u << (int.sizeof*8 - 1);
static assert(signbitMask == 0x8000);

// The (x|y) & signbitMask basically means that if either x is 
negative
// or y > int.max, then the result will always be negative 
(sign bit
// set).
return (cast(uint)x - y) | ((x | y) & signbitMask);
}

unittest
{
// Basic cases
assert(compare(5, 10u) < 0);
assert(compare(5, 5u) == 0);
assert(compare(10, 5u) > 0);

// Large cases
assert(compare(0, uint.max) < 0);
assert(compare(50, uint.max) < 0);

// Sign-dependent cases
assert(compare(-1, 0u) < 0);
assert(compare(-1, 10u) < 0);
assert(compare(-1, uint.max) < 0);
}

int compare2(int x, uint y)
{
return (x < 0) ? -1 :
(y > int.max) ? -1 :
x - y;
}

unittest
{
// Basic cases
assert(compare2(5, 10u) < 0);
assert(compare2(5, 5u) == 0);
assert(compare2(10, 5u) > 0);

 

Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-09 Thread H. S. Teoh via Digitalmars-d-learn
On Wed, Jul 09, 2014 at 05:43:15PM +, Dominikus Dittes Scherkl via 
Digitalmars-d-learn wrote:
> On Wednesday, 9 July 2014 at 17:13:21 UTC, H. S. Teoh via
> Digitalmars-d-learn wrote:
[..]
> >Yeah, I don't see what's the problem with comparing signed and unsigned
> >values, as long as the result is as expected. Currently, however, this
> >code asserts, which is wrong:
> >
> > uint x = uint.max;
> > int y = -1;
> > assert(x > y);
>
> Yes, this is really bad.
> But last time I got the response that this is so to be compatible with
> C.  That is what I really thought was the reason why D throw away
> balast from C, to fix bugs.

I think the slogan was that if something in D looks like C, then it
should either have C semantics or not compile. According to this logic,
the only recourse here is to prohibit comparison of signed with
unsigned, which I don't think is workable because there are many valid
use cases for it (plus, it will break a ton of code and people will be
very unhappy).

I don't like the current behaviour, though. It just reeks of wrong in so
many different ways. If you *really* want semantives like the above, you
really should be casting y to unsigned so that it's clear what exactly
you're trying to achieve.


[...]
> >Hmm. I wonder if there's a more efficient way to do this.
> I'm sure. But I think it should be done at the compiler, not in a
> library.

Obviously, yes. But I wasn't thinking about implementing opCmp in the
library -- that would be strange since ints, of all things, need to have
native compiler support. I was thinking more about how the compiler
would implement "safe" signed/unsigned comparisons.


[...]
> >So I submit that the unbranched version is better. ;-)
> I don't think so, because the branch will only be taken if the signed
> type is >= 0 (in fact unsigned). So if the signed/unsigned comparison
> is by accident, you pay the extra runtime. But if it is intentional
> the signed value is likely to be negative, so you get a correct result
> with no extra cost.

Good point. Moreover, I have discovered multiple bugs in my proposed
implementation; the correct implementation should be as follows:

int compare(int x, uint y)
{
enum signbitMask = 1u << (int.sizeof*8 - 1);
static assert(signbitMask == 0x8000);

// The (x|y) & signbitMask basically means that if either x is 
negative
// or y > int.max, then the result will always be negative 
(sign bit
// set).
return (cast(uint)x - y) | ((x | y) & signbitMask);
}

unittest
{
// Basic cases
assert(compare(5, 10u) < 0);
assert(compare(5, 5u) == 0);
assert(compare(10, 5u) > 0);

// Large cases
assert(compare(0, uint.max) < 0);
assert(compare(50, uint.max) < 0);

// Sign-dependent cases
assert(compare(-1, 0u) < 0);
assert(compare(-1, 10u) < 0);
assert(compare(-1, uint.max) < 0);
}

int compare(uint x, int y)
{
enum signbitMask = 1u << (int.sizeof*8 - 1);
static assert(signbitMask == 0x8000);
return ((x - y) & ~(x & signbitMask)) | ((cast(uint)y & 
signbitMask) >> 1);
}

unittest
{
// Basic cases
assert(compare(0u, 10) < 0);
assert(compare(10u, 10) == 0);
assert(compare(10u, 5) > 0);

// Large cases
assert(compare(uint.max, 10) > 0);
assert(compare(uint.max, -10) > 0);

// Sign-dependent cases
assert(compare(0u, -1) > 0);
assert(compare(10u, -1) > 0);
assert(compare(uint.max, -1) > 0);
}


Using gdc -O3, I managed to get a very good result for
compare(int,uint), only 5 instructions long.

However, for compare(uint,int), there is the annoying special case of
compare(uint.max, -1), which can only be fixed by the hack
... | ((y & signbitMask) >> 1).  Unfortunately, this makes it 11
instructions long, which is unacceptable. So it looks like a simple
compare and branch would be far better in the compare(uint,int) case --
it's far more costly to avoid the branch than to live with it.


> Even better for constants, where the compiler can not only evaluate
> expressions like (uint.max > -1) correct, but it should optimize them
> completely away!

Actually, with gdc -O3, I found that the body of the above unittests got
completely optimized away at compile-time, so that the unittest body is
empty in the executable! So even with a library implementation the
compiler was able to maximize performance. DMD left the assert calls in,
but then it's not exactly known for generating optima

Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-09 Thread Dominikus Dittes Scherkl via Digitalmars-d-learn
On Wednesday, 9 July 2014 at 17:13:21 UTC, H. S. Teoh via 
Digitalmars-d-learn wrote:

The branched version would look something like this:

mov eax, []
mov ebx, []
cmp ebx, $#0
jge label1  ; first branch
mov eax, $#
jmp label2  ; 2nd branch
label1:
sub eax, ebx
label2:
(ret)

Why?

I would say:
mov eax, [] ; mov directly compares to 
zero

jl  lable; less -> jump to return
sub eax, []
neg eax  ; because we subtracted in 
the wrong order

lable:  ret


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-09 Thread anonymous via Digitalmars-d-learn

On Wednesday, 9 July 2014 at 17:13:21 UTC, H. S. Teoh via
Digitalmars-d-learn wrote:
For the comparison s < u, where s is a signed value and u is an 
unsigned

value, whenever s is negative, the return value of opCmp must be
negative.  Assuming 2's-complement representation of integers, 
this
means we simply copy the MSB of s (i.e., the sign bit) to the 
result. So

we can implement s < u as:

	enum signbitMask = 1u << (s.sizeof*8 - 1); // this is a 
compile-time constant

return (s - u) | (s & signbitMask); // look ma, no branches!


This is a problem, isn't it:

void main()
{
 assert(cmp(0, uint.max) < 0); /* fails */
}
int cmp(int s, uint u)
{
 enum signbitMask = 1u << (s.sizeof*8 - 1); // this is a
compile-time constant
 return (s - u) | (s & signbitMask); // look ma, no branches!
}


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-09 Thread Dominikus Dittes Scherkl via Digitalmars-d-learn
On Wednesday, 9 July 2014 at 17:13:21 UTC, H. S. Teoh via 
Digitalmars-d-learn wrote:
On Wed, Jul 09, 2014 at 04:24:38PM +, Dominikus Dittes 
Scherkl via Digitalmars-d-learn wrote:

/// Returns -1 if a < b, 0 if they are equal or 1 if a > b.
/// this will always yield a correct result, no matter which 
numeric types are compared.

/// It uses one extra comparison operation if and only if
/// one type is signed and the other unsigned but the signed 
value is >= 0

/// (that is what you need to pay for stupid choice of type).

[...]

Yeah, I don't see what's the problem with comparing signed and 
unsigned
values, as long as the result is as expected. Currently, 
however, this

code asserts, which is wrong:

uint x = uint.max;
int y = -1;
assert(x > y);

Yes, this is really bad.
But last time I got the response that this is so to be compatible 
with C.
That is what I really thought was the reason why D throw away 
balast from C,

to fix bugs.


   static if(Unqual!T == Unqual!U)


Nitpick: should be:

static if(is(Unqual!T == Unqual!U))

Of course.


[...]

   else static if(isSigned!T && isUnsigned!U)
   {
  alias CommonType!(Unsigned!T, U) C;
  return (a < 0) ? -1 : opCmp!(cast(C)a, cast(C)b);
   }
   else static if(isUnsigned!T && isSigned!U)
   {
  alias CommonType!(T, Unsigned!U) C;
  return (b < 0) ? 1 : opCmp!(cast(C)a, cast(C)b);
   }

[...]

Hmm. I wonder if there's a more efficient way to do this.
I'm sure. But I think it should be done at the compiler, not in a 
library.



{...]
opCmp is just a single sub instruction (this is why opCmp is 
defined the
way it is, BTW), whereas the "smart" signed/unsigned comparison 
is 4

instructions long.
[...]
you can see, the branched version is 5 instructions long, and 
always

causes a CPU pipeline hazard.

So I submit that the unbranched version is better. ;-)
I don't think so, because the branch will only be taken if the 
signed
type is >= 0 (in fact unsigned). So if the signed/unsigned 
comparison
is by accident, you pay the extra runtime. But if it is 
intentional
the signed value is likely to be negative, so you get a correct 
result

with no extra cost.
Even better for constants, where the compiler can not only 
evaluate expressions like (uint.max > -1) correct, but it should 
optimize them completely away!




(So much for premature optimization... now lemme go and actually
benchmark this stuff and see how well it actually performs in 
practice.

Yes, we should do this.

Often, such kinds of hacks often perform more poorly than 
expected due
to unforeseen complications with today's complex CPU's. So for 
all I

know, I could've just been spouting nonsense above. :P)
I don't see such a compiler change as a hack. It is a strong 
improvement IMHO.


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-09 Thread H. S. Teoh via Digitalmars-d-learn
On Wed, Jul 09, 2014 at 04:24:38PM +, Dominikus Dittes Scherkl via 
Digitalmars-d-learn wrote:
> On Wednesday, 9 July 2014 at 14:51:41 UTC, Meta wrote:
> >One of the uglier things in D is also a long-standing problem with C
> >and C++, in that comparison of signed and unsigned values is allowed.
> 
> I would like that, if it would be implemented along this line:
> 
> /// Returns -1 if a < b, 0 if they are equal or 1 if a > b.
> /// this will always yield a correct result, no matter which numeric types 
> are compared.
> /// It uses one extra comparison operation if and only if
> /// one type is signed and the other unsigned but the signed value is >= 0
> /// (that is what you need to pay for stupid choice of type).
[...]

Yeah, I don't see what's the problem with comparing signed and unsigned
values, as long as the result is as expected. Currently, however, this
code asserts, which is wrong:

uint x = uint.max;
int y = -1;
assert(x > y);


> {
>static if(Unqual!T == Unqual!U)

Nitpick: should be:

static if(is(Unqual!T == Unqual!U))


[...]
>else static if(isSigned!T && isUnsigned!U)
>{
>   alias CommonType!(Unsigned!T, U) C;
>   return (a < 0) ? -1 : opCmp!(cast(C)a, cast(C)b);
>}
>else static if(isUnsigned!T && isSigned!U)
>{
>   alias CommonType!(T, Unsigned!U) C;
>   return (b < 0) ? 1 : opCmp!(cast(C)a, cast(C)b);
>}
[...]

Hmm. I wonder if there's a more efficient way to do this.

For the comparison s < u, where s is a signed value and u is an unsigned
value, whenever s is negative, the return value of opCmp must be
negative.  Assuming 2's-complement representation of integers, this
means we simply copy the MSB of s (i.e., the sign bit) to the result. So
we can implement s < u as:

enum signbitMask = 1u << (s.sizeof*8 - 1); // this is a compile-time 
constant
return (s - u) | (s & signbitMask); // look ma, no branches!

which would translate (roughly) to the assembly code:

mov eax, []
mov ebx, []
mov ecx, eax; save the value of s for signbit extraction
sub eax, ebx; s - u
and ecx, #$1000 ; s & signbitMask
or  eax, ecx; (s - u) | (s & signbitMask)
(ret; this is deleted if opCmp is inlined)

which avoids a branch hazard in the CPU pipeline.

Similarly, for the comparison u < s, whenever s is negative, then opCmp
must always be positive. So this means we copy over the *negation* of
the sign bit of s to the result. So we get this for u < s:

enum signbitMask = 1u << (s.sizeof*8 - 1); // as before
return (u - s) & ~(s & signbitMask); // look ma, no branches!

which translates roughly to:

mov eax, []
mov ebx, []
sub eax, ebx; u - s
and ebx, #$1000 ; s & signbitMask
not ebx ; ~(s & signbitMask)
and eax, ebx; (u - s) & ~(s & signbitMask)
(ret; this is deleted if opCmp is inlined)

Again, this avoid a branch hazard in the CPU pipeline.

In both cases, the first 2 instructions are unnecessary if the values to
be compared are already in CPU registers. The naïve implementation of
opCmp is just a single sub instruction (this is why opCmp is defined the
way it is, BTW), whereas the "smart" signed/unsigned comparison is 4
instructions long. The branched version would look something like this:

mov eax, []
mov ebx, []
cmp ebx, $#0
jge label1  ; first branch
mov eax, $#
jmp label2  ; 2nd branch
label1:
sub eax, ebx
label2:
(ret)

The 2nd branch can be replaced with ret if opCmp is not inlined, but
requiring a function call to compare integers seems excessive, so let's
assume it's inlined, in which case the 2nd branch is necessary. So as
you can see, the branched version is 5 instructions long, and always
causes a CPU pipeline hazard.

So I submit that the unbranched version is better. ;-)

(So much for premature optimization... now lemme go and actually
benchmark this stuff and see how well it actually performs in practice.
Often, such kinds of hacks often perform more poorly than expected due
to unforeseen complications with today's complex CPU's. So for all I
know, I could've just been spouting nonsense above. :P)


T

-- 
Debian GNU/Linux: Cray on your desktop.


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-09 Thread Dominikus Dittes Scherkl via Digitalmars-d-learn

Of course without the ! after opCmp in the several cases.


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-09 Thread Dominikus Dittes Scherkl via Digitalmars-d-learn

On Wednesday, 9 July 2014 at 14:51:41 UTC, Meta wrote:
One of the uglier things in D is also a long-standing problem 
with C and C++, in that comparison of signed and unsigned 
values is allowed.


I would like that, if it would be implemented along this line:

/// Returns -1 if a < b, 0 if they are equal or 1 if a > b.
/// this will always yield a correct result, no matter which 
numeric types are compared.

/// It uses one extra comparison operation if and only if
/// one type is signed and the other unsigned but the signed 
value is >= 0

/// (that is what you need to pay for stupid choice of type).
int opCmp(T, U)(const(T) a, const(U) b) @primitive if(isNumeric!T 
&& isNumeric!U)

{
   static if(Unqual!T == Unqual!U)
   {
  // use the standard D implementation
   }
   else static if(isFloatingPoint!T || isFloatingPoint!U)
   {
  alias CommonType!(T, U) C;
  return opCmp!(cast(C)a, cast(C)b);
   }
   else static if(isSigned!T && isUnsigned!U)
   {
  alias CommonType!(Unsigned!T, U) C;
  return (a < 0) ? -1 : opCmp!(cast(C)a, cast(C)b);
   }
   else static if(isUnsigned!T && isSigned!U)
   {
  alias CommonType!(T, Unsigned!U) C;
  return (b < 0) ? 1 : opCmp!(cast(C)a, cast(C)b);
   }
   else // both signed or both unsigned
   {
  alias CommonType!(T, U) C;
  return opCmp!(cast(C)a, cast(C)b);
   }
}


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-09 Thread Meta via Digitalmars-d-learn

On Monday, 7 July 2014 at 23:47:26 UTC, Aerolite wrote:

Hey all,

I've not posted here in a while, but I've been keeping up to 
speed with D's progress over the last couple of years and 
remain consistently impressed with the language.


I'm part of a new computing society in the University of 
Newcastle, Australia, and am essentially known throughout our 
Computer Science department as 'the D guy'. At the insistence 
of my peers, I have decided to give an introductory lecture on 
the D Programming Language, in order to expose more students to 
the increasingly amazing aspects of D. I expect to cover the 
good, the bad, the awesome, and the ugly, in a 
complement-criticism-complement styled talk, and while I have 
my own opinions regarding each of these things, I'd like a 
broader view from the community regarding these aspects, so 
that I may provide as accurate and as useful information as 
possible.


So, if you would be so kind, give me a bullet list of the 
aspects of D you believe to be good, awesome, bad, and/or ugly. 
If you have the time, some code examples wouldn't go amiss 
either! Try not to go in-depth to weird edge cases - remain 
general, yet informative. E.g. I consider D's string mixins to 
be in the 'awesome' category, but its reliance on the GC for 
large segments of the standard library to be in the 'ugly' 
category.


Thanks so much for your time!


opDispatch is a mostly untapped goldmine of potential. Just take 
a look at this thread, where an (almost, depends on the compiler) 
no-cost safe dereference wrapper was implemented using it: 
http://forum.dlang.org/post/mailman.2584.1403213951.2907.digitalmar...@puremagic.com


opDisptach also allows for vector swizzling, which is really nice 
for any kind of vector work.


One of the uglier things in D is also a long-standing problem 
with C and C++, in that comparison of signed and unsigned values 
is allowed.


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-09 Thread H. S. Teoh via Digitalmars-d-learn
On Wed, Jul 09, 2014 at 07:51:24AM +0200, Philippe Sigaud via 
Digitalmars-d-learn wrote:
> On Tue, Jul 8, 2014 at 7:50 AM, H. S. Teoh via Digitalmars-d-learn
>  wrote 
> 
> Wow, what to add to that? Maybe you scared other from participating
> ;-)

I hope not. :)


[...]
> * I'd add static introspection to the mix: using static if,
> __traits(...) and is(...), clunky as the syntax is (there, one 'ugly'
> thing for you), is easy and very powerful:
[...]

Oh yeah, I forgot about that one. The syntax of is-expressions is very
counterintuitive (not to mention noisy), and has too many special-cased
meanings that are completely non-obvious for the uninitiated, for
example:

// Assume T = some type
is(T)   // is T a valid type?
is(T U) // is T a valid type? If so, alias it to U
is(T : U)   // is T implicitly convertible to U?
is(T U : V) // is T implicitly convertible to V? If so,
// alias it to U
is(T U : V, W)  // does T match the type pattern V, for some
// template arguments W? If so, alias to U
is(T == U)  // is T the same type as U?

// You thought the above is (somewhat) consistent? Well look at
// this one:
is(T U : __parameters)
// is T the type of a function? If so, alias U
// to the parameter tuple of its arguments.

That last one is remarkably pathological: it breaks away from the
general interpretation of the other cases, where T is matched against
the right side of the expression; here, __parameters is a magic keyword
that makes the whole thing mean something else completely. Not to
mention, what is "returned" in U is something extremely strange; it
looks like a "type tuple", but it's actually something more than that.
Unlike usual "type tuples", in addition to encoding the list of types of
the function's parameters, it also includes the parameter names and
attributes...  except that you can only get at the parameter names using
__traits(name,...). But indexing it like a regular "type tuple" will
reduce its elements into mere types, on which __traits(name,...) will
fail; you need to take 1-element slices of it in order to preserve the
additional information.

This strange, inconsistent behaviour only barely begins to make sense
once you understand how it's implemented in the compiler. It's the
epitome of leaky abstraction.


T

-- 
Do not reason with the unreasonable; you lose by definition.


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-08 Thread Philippe Sigaud via Digitalmars-d-learn
On Tue, Jul 8, 2014 at 7:50 AM, H. S. Teoh via Digitalmars-d-learn
 wrote 

Wow, what to add to that? Maybe you scared other from participating ;-)

* I'll second metaprogramming: the alliance between templates, CTFE
and mixins is really nice. It's *the* feature (or triplet of features)
I think of when Walter says that many D parts are kind of "what for?"
in isolation but really grow into something awesome by using one
another.

* I'd add static introspection to the mix: using static if,
__traits(...) and is(...), clunky as the syntax is (there, one 'ugly'
thing for you), is easy and very powerful: the idea to have code
selecting its flow or extracting information (is that a static array?
Does this aggregate have an '.empty' method?). This is the basis for
std.algorithm idiom of 'static interface' which allows us compile-time
duck typing, which I find very pleasing.

* unittests are nice of course, H. S. Teoh already said it
expressively enough :-)

* I'd add arrays and slice. They are wonderfully simple to use,
efficient, etc. I remember learning D by translating the Euler Project
problems from C++ to D and it was a joy.

Which brings me to another feature I like: ranges. The idea is nothing
new, but I find it quite well-realized in D, far easier than other
compiled languages alternatives and since they are pervasive in the
library, you can really plug components into one another nicely.

For example:
http://wiki.dlang.org/Component_programming_with_ranges#Case_Study:_Formatting_a_Calendar

* dub is good, and you can show code.dlang.org in your presentation.

* Bonus point: different compilers. I like that. I regularly use at
least two in parallel while developping (comparing results, speed,
etc). Kudos to all the guys involved!



As for the bad and ugly:

* it's frustrating not to have a big collection module in Phobos, but
then I didn't propose one, so I'll shut up.
* there are still some not so nice interaction between
const/shared/inout/ auto ref, though I rarely hit them these days
* The compiler still has some quirks: I find it strange you can put
unused qualifiers in many places.

But very honestly, it was already a joy to use a few years ago, and
it's becoming better and better.


Re: Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-07 Thread H. S. Teoh via Digitalmars-d-learn
On Mon, Jul 07, 2014 at 11:47:25PM +, Aerolite via Digitalmars-d-learn 
wrote:
[...]
> So, if you would be so kind, give me a bullet list of the aspects of D
> you believe to be good, awesome, bad, and/or ugly. If you have the
> time, some code examples wouldn't go amiss either! Try not to go
> in-depth to weird edge cases - remain general, yet informative. E.g. I
> consider D's string mixins to be in the 'awesome' category, but its
> reliance on the GC for large segments of the standard library to be in
> the 'ugly' category.
[...]

String mixins are a controversial feature. Many (including myself) think
they are definitely awesome, but that power also comes with the price of
being harder to maintain, and difficult to integrate with IDE features
(though the latter doesn't matter to me 'cos I don't use IDEs). Because
of that, some others think they are a misfeature, and have proposed to
remove them. But I don't think it's going away anytime soon, since
several key features depend on it.

Perhaps I may bring up a representative use case: operator overloading.
In C++, if you implement a custom number type, for example, you have to
overload operator+, operator*, operator/, operator-. And then you
realize you left out operator+=, operator*=, operator/=, ... and then
you need operator<, operator>, operator<=, operator>=, ad nauseum. In D?

// This covers +, -, *, /, +=, -=, *=, /=, etc..
auto opBinary(string op)(NumType n)
{
return NumType(mixin(this.val ~ op ~ n.val));
}

// And this covers all the comparison operators:
int opCmp(NumType n) {
return ... /* implementation here */
}

Without string mixins, you'd have to copy-n-paste a ton of boilerplate
to get the necessary operator overloadings.

//

About the GC, my opinion is that anti-GC sentiment is largely unfounded.
The GC greatly simplifies certain tasks (string manipulation, for one
thing, and returning recursive data structures like trees). There *are*
cases where the GC can cause trouble, like in game engines, esp. given
that the GC implementation in D leaves a lot of room for improvement,
but a lot of C/C++ programmers have knee-jerk reactions about GCs for no
other reason than unfamiliarity breeding contempt (I speak for myself,
since I come from a strong C/C++ background, and had the same reaction),
than any truly objective measurements. I'd say a large percentage of
applications don't need to manually micro-manage memory, and having a GC
eliminates an entire class of bugs and greatly improves productivity.

//

But anyway, not to dwell on a contentious issue, let's move on to
something oft overlooked (and sometimes even maligned): unittests. I
could sing praises of D's built-in unittest blocks all day. When I was
young and foolish, I prided myself on being the macho C/C++ programmer
whose code will work the first time after I write it. Who needs tests?
They are for the weak-minded, who cannot grasp their code in every last
detail of their programs in their head with absolute clarity. Real
Programmers can write code in their dreams, and it will work the first
time they run it.

Except that they don't. :P  When I first started learning D, I tried my
best to ignore unittests, telling myself that it's for weaker
programmers, but they just kept sitting their, right in the language,
and staring at me, all cute-like, until I was shamed into actually
writing a few of them just to quiet that nagging voice within. And lo
and behold, I started finding bugs in my "perfect" code -- corner cases
I missed (even though I painstakingly took care of all the *other*
corner cases), subtle typos that compiled but gave the wrong answers,
blatant errors that I missed due to obsession over tiny details, etc..
To my chagrin, I discovered that I was *not* the macho perfect
programmer that I imagined, and that these unittests for the weak were
singlehandedly improving the quality of my code by leaps and bounds.

Sometimes, the mere act of writing unittests would bring my attention to
a corner case that I'd missed, and I'd fix the code, long before my past
self would've noticed it (by finding it as a runtime bug much later).
When I revised the code later, I suddenly could have peace of mind that
if the unittests didn't break, then in all likelihood the new code is
still correct (or mostly so). Regressions were instantly noticed, rather
than weeks or months down the road when I suddenly needed a corner case
that I knew was previously working but had since been broken by a later
revision.

Unittests in the language == total win.  External unittest frameworks
may be more powerful, more flexible, etc., but I'd never use them, 'cos
they are too troublesome. I have to switch out of coding mode, open a
new file, and possibly shift gears to a different language, write the
test, then switch back, and later on it becomes too trouble to keep
switching back and forth (and maintaining the unittest

Opinions: The Best and Worst of D (for a lecture/talk I intend to give)

2014-07-07 Thread Aerolite via Digitalmars-d-learn

Hey all,

I've not posted here in a while, but I've been keeping up to 
speed with D's progress over the last couple of years and remain 
consistently impressed with the language.


I'm part of a new computing society in the University of 
Newcastle, Australia, and am essentially known throughout our 
Computer Science department as 'the D guy'. At the insistence of 
my peers, I have decided to give an introductory lecture on the D 
Programming Language, in order to expose more students to the 
increasingly amazing aspects of D. I expect to cover the good, 
the bad, the awesome, and the ugly, in a 
complement-criticism-complement styled talk, and while I have my 
own opinions regarding each of these things, I'd like a broader 
view from the community regarding these aspects, so that I may 
provide as accurate and as useful information as possible.


So, if you would be so kind, give me a bullet list of the aspects 
of D you believe to be good, awesome, bad, and/or ugly. If you 
have the time, some code examples wouldn't go amiss either! Try 
not to go in-depth to weird edge cases - remain general, yet 
informative. E.g. I consider D's string mixins to be in the 
'awesome' category, but its reliance on the GC for large segments 
of the standard library to be in the 'ugly' category.


Thanks so much for your time!