Okay. I am in no way trying to say anything negative about TDPL. In fact,
from what I've read so far, it's absolutely fantastic and quite possibly the
most entertaining programming book that I've read in addition to being quite
informative about D. However, no one's perfect (Andrei included), and there
are bound to be errors in the book which didn't get caught.
My thought was that we could point out any errors that we've found so that
Andrei can get them fixed in future printings and/or we can find out that
they aren't actually errors.
The only errors that I've found so far have been omissions in the list of
keywords on page 31. I'm listing them according to my understanding of
whether they're still keywords, since I think that some have been removed as
keywords or at least are no longer supposed to be keywords.
Definitely should be there
--------------------------
immutable
lazy
pure
nothrow
shared
I _think_ that it's supposed to be there
----------------------------------------
cent
ucent
I think that they might not supposed to be keywords anymore
-----------------------------------------------------------
cdouble
cfloat
creal
delete
idouble
ifloat
ireal
foreach_reverse
Everything under "definitely" appears to be used in TDPL as keywords but not
listed as them. cent and ucent aren't listed, but as far as I know are still
keywords (albeit not implemented yet). The ones that are missing which I
think have been removed are still listed in the online docs' list of
keywards but not in the book. IIRC, the c/i floating points got moved to
phobos; according to TDPL, delete was deprecated (though I hadn't picked up
on that); and I believe that foreach_reverse has been deprecated in favor of
using the combination of foreach and retro. So, TDPL is missing at least
some keywords in its list, and the online docs have too many.
In any case, I figured that it would be helpful if any errors in TDPL could
be pointed out, since it could be helpful to Andrei and could be helpful to
those reading it if the error isn't obvious. However, I certainly do _not_
want to in any way indicate displeasure with the book. It's quite good. It's
just that it does appear to have some errors in it that snuck through.
- Jonathan M Davis

Oh, right! Or even just:
foreach (i; retro(iota(10))) {}
But abstraction has a cost, see below. I have written three test programs.

Nice work.
iota() currently uses the formula initial + i * step to compute the ith
element. This is to make sure that iota works properly with floating
point numbers as well as integers.
We should specialize iota for integrals to use increment, which should
count for some efficiency gains (currently the optimizer cannot figure
out the equivalence, so it generates the multiply and add in the loop).
If efficiency is still sub-par, retro could detect that it's working
with iota and generate specialized code. That's not too difficult; for
integers, retro(iota(a, b)) could actually be a rewrite to iota(b - 1,
a, -1). Figuring out all corner cases, steps greater than 1, and what to
do for floating point numbers is doable but not trivial either, and
works against modularity. Anyway, it does look like it's all about an
implementation matter.
Andrei

If efficiency is still sub-par, retro could detect that it's working
with iota and generate specialized code. That's not too difficult;
for integers, retro(iota(a, b)) could actually be a rewrite to
iota(b - 1, a, -1). Figuring out all corner cases, steps greater
than 1, and what to do for floating point numbers is doable but not
trivial either, and works against modularity. Anyway, it does look
like it's all about an implementation matter.

If efficiency is still sub-par, retro could detect that it's working
with iota and generate specialized code. That's not too difficult;
for integers, retro(iota(a, b)) could actually be a rewrite to
iota(b - 1, a, -1). Figuring out all corner cases, steps greater
than 1, and what to do for floating point numbers is doable but not
trivial either, and works against modularity. Anyway, it does look
like it's all about an implementation matter.

I'm scared, I've heard that in C++ so many times... =)

Particularly in conjunction with iostreams. Their slow speed has been an
implementation issue for, what, 26 years and still going strong. :o)
Fortunately, in the case of iota, it's pretty clear what needs to be done.
Andrei

If efficiency is still sub-par, retro could detect that it's working
with iota and generate specialized code. That's not too difficult;
for integers, retro(iota(a, b)) could actually be a rewrite to
iota(b - 1, a, -1). Figuring out all corner cases, steps greater
than 1, and what to do for floating point numbers is doable but not
trivial either, and works against modularity. Anyway, it does look
like it's all about an implementation matter.

I'm scared, I've heard that in C++ so many times... =)

Writing an efficient library is a complex task. No language will ever
change that.
The beauty of D is that this complexity can be hidden from the user
(unlike in C++ where error messages become increasingly ugly).

Okay. I am in no way trying to say anything negative about TDPL. In fact,
from what I've read so far, it's absolutely fantastic and quite possibly the
most entertaining programming book that I've read in addition to being quite
informative about D. However, no one's perfect (Andrei included), and there
are bound to be errors in the book which didn't get caught.
My thought was that we could point out any errors that we've found so that
Andrei can get them fixed in future printings and/or we can find out that
they aren't actually errors.
The only errors that I've found so far have been omissions in the list of
keywords on page 31. I'm listing them according to my understanding of
whether they're still keywords, since I think that some have been removed as
keywords or at least are no longer supposed to be keywords.
Definitely should be there
--------------------------
immutable
lazy
pure
nothrow
shared
I _think_ that it's supposed to be there
----------------------------------------
cent
ucent
I think that they might not supposed to be keywords anymore
-----------------------------------------------------------
cdouble
cfloat
creal
delete
idouble
ifloat
ireal
foreach_reverse
Everything under "definitely" appears to be used in TDPL as keywords but not
listed as them. cent and ucent aren't listed, but as far as I know are still
keywords (albeit not implemented yet). The ones that are missing which I
think have been removed are still listed in the online docs' list of
keywards but not in the book. IIRC, the c/i floating points got moved to
phobos; according to TDPL, delete was deprecated (though I hadn't picked up
on that); and I believe that foreach_reverse has been deprecated in favor of
using the combination of foreach and retro. So, TDPL is missing at least
some keywords in its list, and the online docs have too many.
In any case, I figured that it would be helpful if any errors in TDPL could
be pointed out, since it could be helpful to Andrei and could be helpful to
those reading it if the error isn't obvious. However, I certainly do _not_
want to in any way indicate displeasure with the book. It's quite good. It's
just that it does appear to have some errors in it that snuck through.
- Jonathan M Davis

Good Idea!
I must admit I skipped over that table, didn't look overly interesting
as tables go (><) but good catch!
There is only one mention of lazy evaluation in the index and it doesn't
mention the lazy k/w at all. I seem to remember Andrei dislikes it, but
also that there is another way to get function params to be evaluated
lazily without using it.
immutable, nothrow, pure and shared are definitely oversights though.
I've not spotted anything D-specific myself yet, I'm not a particularly
speedy reader ^^
A...

also that there is another way to get function params to be evaluated =

=

lazily without using it.

Yeah, speaking of which - what happened to that proposal*?
*The proposal AFAIR was (correct me if wrong): if a function has a =
parameterless delegate as a parameter, then on the call site any =
expression in the delegate slot is implicitly turned into a delegate. I =
=
think the delegate ought to be pure to make the magic happen.
Yeah, speaking of which - do/will we have pure delegates?
Tomek

There is only one mention of lazy evaluation in the index and it
doesn't mention the lazy k/w at all. I seem to remember Andrei
dislikes it, but also that there is another way to get function params
to be evaluated lazily without using it.

Yeah, speaking of which - what happened to that proposal*?
*The proposal AFAIR was (correct me if wrong): if a function has a
parameterless delegate as a parameter, then on the call site any
expression in the delegate slot is implicitly turned into a delegate. I
think the delegate ought to be pure to make the magic happen.
Yeah, speaking of which - do/will we have pure delegates?
Tomek

This is the "trick" I was refering to, from a post by the programmer
formerly known as downs...
Amusing D facts: typesafe variadic arrays are lazy!

There is only one mention of lazy evaluation in the index and it doesn't
mention the lazy k/w at all. I seem to remember Andrei dislikes it, but
also that there is another way to get function params to be evaluated
lazily without using it.

lazy is quite, no, _very_ ill-defined. Walter and I decided to not
mention it in the book and leave room for either a better definition of
lazy or a feature that obviates it.
Andrei

I was biting my tongue on the subject, but on page 73 the grammar for
the do while loop has a semicolon at the end. AAHHHHHHHHHHHHH!!!!!!!!
THERE IS NOOOOOOO SEMICOLON AT THE END.
Wow. Sorry. This is a pet peeve of mine.

I was biting my tongue on the subject, but on page 73 the grammar for
the do while loop has a semicolon at the end. AAHHHHHHHHHHHHH!!!!!!!!
THERE IS NOOOOOOO SEMICOLON AT THE END.
Wow. Sorry. This is a pet peeve of mine.

I was biting my tongue on the subject, but on page 73 the grammar for
the do while loop has a semicolon at the end. AAHHHHHHHHHHHHH!!!!!!!!
THERE IS NOOOOOOO SEMICOLON AT THE END.
Wow. Sorry. This is a pet peeve of mine.

Can't help that, sorry...
Andrei

Well, while I, personally, would put a semicolon there (it feels naked to me
without one), dmd doesn't actually seem to require it. But TDPL says that
the semicolon is required. So, it does appear to be an error in the text. Of
course, there's no helping his pet peeve regardless, but the semicolon
doesn't appear to be required.
- Jonathan M Davis

I was biting my tongue on the subject, but on page 73 the grammar for
the do while loop has a semicolon at the end. AAHHHHHHHHHHHHH!!!!!!!!
THERE IS NOOOOOOO SEMICOLON AT THE END.
Wow. Sorry. This is a pet peeve of mine.

Can't help that, sorry...
Andrei

Well, while I, personally, would put a semicolon there (it feels naked to me
without one), dmd doesn't actually seem to require it. But TDPL says that
the semicolon is required. So, it does appear to be an error in the text. Of
course, there's no helping his pet peeve regardless, but the semicolon
doesn't appear to be required.
- Jonathan M Davis

Walter, was that intentional? The grammar has no semicolon but the
example does. That makes the example wrong because you agreed there is
no solitary semicolon statement in D, and TDPL does mention that.
IMHO the semicolon makes for more robust code. Consider:
do
{
... lotsa code ...
}
while (fun(i))
++i;
A maintainer might see the while and conclude that ++i; was meant to be
the loop, indent it, and call it a day. The absence of the semicolon
thus created a contextual dependency on the presence of the "do" keyword
upstream.
Walter, can we require a semicolon please?
Andrei

Yes. It shouldn't compile. Walter and I agreed that solitary semicolons
are useless (you can always use {} as an empty statement and that
actually makes things clearer to everyone), but it's a low-priority
issue so he hasn't implemented that yet.
Andrei

Yes. It shouldn't compile. Walter and I agreed that solitary
semicolons are useless (you can always use {} as an empty statement
and that actually makes things clearer to everyone),

I have had to use a LABEL:; in D code, to implement a finite state
machine. So I guess I'll have to write it like this: LABEL: {}
Bye,
bearophile

I think the ; is part of the label statement. If not, it should be
removed entirely.

This does not compile:
void main()
{
goto Lfoo;
Lfoo:
}
test.d(5): found '}' instead of statement
It seems to me like it should be valid code, but I'm not sure it's worth
making it a special case.
-Lars

Walter, was that intentional? The grammar has no semicolon but the
example does. That makes the example wrong because you agreed there is
no solitary semicolon statement in D, and TDPL does mention that.
IMHO the semicolon makes for more robust code. Consider:
do
{
... lotsa code ...
}
while (fun(i))
++i;
A maintainer might see the while and conclude that ++i; was meant to be
the loop, indent it, and call it a day. The absence of the semicolon
thus created a contextual dependency on the presence of the "do" keyword
upstream.
Walter, can we require a semicolon please?
Andrei

I have zero problem requiring a semicolon (I'd prefer it actually), but I
believe that a lone semicolon is generally a perfectly valid statement in D
as long as it's not where optional braces could be. Per the grammar:
Statement:
;
NonEmptyStatement
ScopeBlockStatement
I believe that all the constructs with optional braces have a
ScopBlockStatement for their body and therefore can't have a lone semicolon.
But a statement by itself can be a lone semicolon. Though why you'd do that,
I don't know. The only place that I've run into that being useful has been
with macros (which don't exist in D).
- Jonathan M Davis

Saving a keystroke, if it is in sound and clear way, is a plus.
At least the option to save the keystroke should be maintained.
I don't see now any advantages in using a pair of braces over a single
semicolon for a null statement, but why not keep it both ways?
I never liked the semicolon after while() but surely it wouldn't hurt to
interpret it as a null statement for those who are used to it.
By the way, I always glue the closing bracket to while
do{
. . .
}while( . . . ) // plus ";" if coding in C
Rick Trelles

Saving a keystroke, if it is in sound and clear way, is a plus.
At least the option to save the keystroke should be maintained.
I don't see now any advantages in using a pair of braces over a single
semicolon for a null statement, but why not keep it both ways?

We've all seen that requiring "{}" after the likes of if, while etc. are
very beneficial for readability. Then why not generalize that? As far as
I can tell the only vestigial use of the lone ";" is with labels that
don't precede any instruction (a very rare case). At this point, ";"
needs more justification to stay than to go.

I never liked the semicolon after while() but surely it wouldn't hurt to
interpret it as a null statement for those who are used to it.
By the way, I always glue the closing bracket to while
do{
. . .
}while( . . . ) // plus ";" if coding in C
Rick Trelles

That's great, except for those who enjoy "brace on its own line"
formatting style. For those, the "}while" would look jarring.
Andrei

Saving a keystroke, if it is in sound and clear way, is a plus.
At least the option to save the keystroke should be maintained.
I don't see now any advantages in using a pair of braces over a single
semicolon for a null statement, but why not keep it both ways?

We've all seen that requiring "{}" after the likes of if, while etc. are
very beneficial for readability. Then why not generalize that? As far as
I can tell the only vestigial use of the lone ";" is with labels that
don't precede any instruction (a very rare case). At this point, ";"
needs more justification to stay than to go.

I never liked the semicolon after while() but surely it wouldn't hurt to
interpret it as a null statement for those who are used to it.
By the way, I always glue the closing bracket to while
do{
. . .
}while( . . . ) // plus ";" if coding in C
Rick Trelles

That's great, except for those who enjoy "brace on its own line"
formatting style. For those, the "}while" would look jarring.
Andrei

I find that the "}while(..." style is the best way to highlight that you
are looking a a do...while and not a regular while loop, that it is
jarring is exactly the point ^^
Making the the semicolon on the end a requirement helps if you are
familiar with D and know it is not allowed on a regular while, but the
less enlightened might see a classic bug pattern where there isn't one
and try to "maintain" it away ><
In some ways I think it was a mistake in to reuse while in the first
place, but that was a decision that was made a loong time before D...
A...

In some ways I think it was a mistake in to reuse while in the first
place, but that was a decision that was made a loong time before D...

Pascal-like languages use repeat-until, but I prefer the C do-while because the
condition in repeat-until is the opposite of the one you use in a while loop,
and every time I use repeat-until I have to remember what's the correct
stopping condition to write.
Bye,
bearophile

because the condition in repeat-until is the opposite of the one you
use in a while loop, and every time I use repeat-until I have to
remember what's the correct stopping condition to write.
Quite the oposite here. When I have a loop like that, I think rather
about the exit condition (so in terms of "repeat/until"). This is why
I often write:
do{
//code
} while(!([exit_condition_here]));
I also think re-using while() in the do ... while loop is a mistake,
exactly because of difficulties arising in code maintenance.
I would like to have another keyword for that, or maybe the "repeat/
until" loop all-together.
It is a shame that compatibility issues prevail over quality and
improvement. Ditching the do...while loop could be unacceptable now,
but why do not propose a better alternative mechanism? That way, we
would have both compatibility and quality.
eles

It is a shame that compatibility issues prevail over quality and
improvement. Ditching the do...while loop could be unacceptable now,
but why do not propose a better alternative mechanism? That way, we
would have both compatibility and quality.
eles

Quality prevails over compatibility when the quality gain is deemed to
exceed the problems incurred by losing compatibility.
In this case, do-while works just fine. Lots of people are used to using it
and have never even heard of repeat-until, having never used Pascal or any
other language that used it. At this point, C's influence far outweights
Pascal's.
Also, AFAIK, do-while is not generally a major source of bugs. As such,
while another construct might be better, since the current one isn't much of
a problem, it's not worth breaking compatibility. If it were shown that do-
while was a big problem, then it might be. But at this point, do-while works
just fine, so it's not worth changing it.
- Jonathan M Davis

I did not try to start a polemic, but to give something to think about. There
are more opinions on the matter and, as you may see, mine differs of yours. Is
not a tragedy.
However, I think one should be more aware when supporting habit per se.
Maybe adding "until" as an aliasing for "while not" would you seem more
acceptable? Or, at least, accepting "do{/* code */}aslongas(/*condition*/);" as
an alternative "do{/* code */}while(/*condition*/);" would be less disruptive?
That way, people could use "do/while", but this will open the door for "do/
aslongas", if this is considered suitable. This will also avoid ambiguous cases
like pointed out in this thread (i.e. the danger of interpreting "while(/
*condition*/;i++" as a ";" bug). This will break no compatibility.
Robustness of relying solely on the code indentation ("}while();" instead of
"while();") is not persuasive for me. Why dismissing an opportunity to avoid a
potential bug?
eles
== Quote from Jonathan M Davis (jmdavisProg gmail.com)'s article

Quality prevails over compatibility when the quality gain is deemed to
exceed the problems incurred by losing compatibility.
In this case, do-while works just fine. Lots of people are used to using it
and have never even heard of repeat-until, having never used Pascal or any
other language that used it. At this point, C's influence far outweights
Pascal's.
Also, AFAIK, do-while is not generally a major source of bugs. As such,
while another construct might be better, since the current one isn't much of
a problem, it's not worth breaking compatibility. If it were shown that do-
while was a big problem, then it might be. But at this point, do-while works
just fine, so it's not worth changing it.
- Jonathan M Davis

Saving a keystroke, if it is in sound and clear way, is a plus.
At least the option to save the keystroke should be maintained.
I don't see now any advantages in using a pair of braces over a single
semicolon for a null statement, but why not keep it both ways?

We've all seen that requiring "{}" after the likes of if, while etc. are
very beneficial for readability. Then why not generalize that? As far as
I can tell the only vestigial use of the lone ";" is with labels that
don't precede any instruction (a very rare case). At this point, ";"
needs more justification to stay than to go.

I never liked the semicolon after while() but surely it wouldn't hurt to
interpret it as a null statement for those who are used to it.
By the way, I always glue the closing bracket to while
do{
. . .
}while( . . . ) // plus ";" if coding in C
Rick Trelles

That's great, except for those who enjoy "brace on its own line"
formatting style. For those, the "}while" would look jarring.
Andrei

I find that the "}while(..." style is the best way to highlight that you
are looking a a do...while and not a regular while loop, that it is
jarring is exactly the point ^^
Making the the semicolon on the end a requirement helps if you are
familiar with D and know it is not allowed on a regular while, but the
less enlightened might see a classic bug pattern where there isn't one
and try to "maintain" it away ><
In some ways I think it was a mistake in to reuse while in the first
place, but that was a decision that was made a loong time before D...
A...

+1
With the semi colon missing its as bad as:
for (;i<100;i++);
writeln(i);
esp. with large amount of code

Andrei

Rory

It's not that bad.
for(; i < 100; ++i);
writeln(i);
would be a bug (if it were legal) because you'd expect the writeln() to be
part of the loop when it isn't. The loop wouldn't be run like you'd expect
it to, and it's hard to catch.
do
{
...
} while(test)
does not have that problem. The problem you run into is this:
do
{
...
}
while(test)
{
...
}
If you're _really_ not paying attention, you'd think that the second set of
braces started a while loop. But since you'd have to have way more spacing
than makes sense for it to happen, it becomes way more obvious and is not
the same level of problem. However, I do think that it _is_ a problem, and
the above example could definitely be confusing if you're just quickly
scanning over code.
I don't see any real reason to allow the lack of semicolon, and I do think
that it's a problem, but I don't think that it's a problem on the same level
as allowing a semicolon to be used as an empty body for an if statement or
loop.
- Jonathan M Davis

Well, while I, personally, would put a semicolon there (it feels naked to me
without one), dmd doesn't actually seem to require it. But TDPL says that
the semicolon is required. So, it does appear to be an error in the text. Of
course, there's no helping his pet peeve regardless, but the semicolon
doesn't appear to be required.

Well, while I, personally, would put a semicolon there (it feels naked
to me without one), dmd doesn't actually seem to require it. But TDPL
says that the semicolon is required. So, it does appear to be an error
in the text. Of course, there's no helping his pet peeve regardless,
but the semicolon doesn't appear to be required.

I mistakingly assumed D followed C in that regard. Given the argument
stated in this thread (that absent semicolons require more context to be
absorbed while reading), do you agree that D should make the semicolon
required?
Andrei

Well, while I, personally, would put a semicolon there (it feels naked
to me without one), dmd doesn't actually seem to require it. But TDPL
says that the semicolon is required. So, it does appear to be an error
in the text. Of course, there's no helping his pet peeve regardless,
but the semicolon doesn't appear to be required.

I mistakingly assumed D followed C in that regard. Given the argument
stated in this thread (that absent semicolons require more context to be
absorbed while reading), do you agree that D should make the semicolon
required?
Andrei

I mistakingly assumed D followed C in that regard. Given the argument
stated in this thread (that absent semicolons require more context to be
absorbed while reading), do you agree that D should make the semicolon
required?

Well, while I, personally, would put a semicolon there (it feels naked
to me without one), dmd doesn't actually seem to require it. But TDPL
says that the semicolon is required. So, it does appear to be an error
in the text. Of course, there's no helping his pet peeve regardless,
but the semicolon doesn't appear to be required.

I mistakingly assumed D followed C in that regard. Given the argument
stated in this thread (that absent semicolons require more context to be
absorbed while reading), do you agree that D should make the semicolon
required?

I am only on page ten, I believe I saw a minor typo somewhere in the
preface, that's all so far. I look forward to pondering the rest in the
coming days.
oh yes.
Preface
"D is a language that attempts to consistently do the right thing within
the constraints it choose: sys...." etc
missing s, I gues.

I am only on page ten, I believe I saw a minor typo somewhere in the
preface, that's all so far. I look forward to pondering the rest in the
coming days.
oh yes.
Preface
"D is a language that attempts to consistently do the right thing within
the constraints it choose: sys...." etc
missing s, I gues.

Thanks for your note. The current text uses "chose", i.e. the past tense
of "to choose", so I think it is correct.
Andrei

I am only on page ten, I believe I saw a minor typo somewhere in the
preface, that's all so far. I look forward to pondering the rest in the
coming days.
oh yes.
Preface
"D is a language that attempts to consistently do the right thing within
the constraints it choose: sys...." etc
missing s, I gues.

Thanks for your note. The current text uses "chose", i.e. the past tense
of "to choose", so I think it is correct.
Andrei

[snip]
You are being too kind about this :o). Of course we need an errata list.
I was hoping I'd need to set it up later, but hey, that's a sign people
actually are reading the book and care about keeping everything in check.
I started an errata list in form of a community wiki at
http://www.erdani.com/tdpl/errata
and primed the errata with your report.
Thanks very much! I'll see to it that future printings fix the issues in
the errata list.
Andrei

Well, I didn't want to post on the main D list and come across as saying
that the new book is full of errors and sucks. wIt really is quite an
entertaining read (for a programming book anyway) with talk of jumping
through rings of syntactic fire, describing casts as well-intended genies,
and plenty of other humourous descriptions. And, of course, it's quite
instructive along the way. Now if I can only get my co-workers to read it...
- Jonathan M Davis

On page 82 of TDPL, it's talking about try/catch/finally statements, and it
says that "all controlled statement must be block statements; that is they
must be enclosed with braces." However, dmd does not seem to require that
try/catch/finally blocks have braces, which is what I would expect that
sentence to mean. C++ has that restriction for reasons that I don't
understand, but as far as I can tell, D does not - certainly dmd doesn't
appear to require it. Is the statement correct but refers to something else,
or is it in error?
- Jonathan M Davis

On page 82 of TDPL, it's talking about try/catch/finally statements, and it
says that "all controlled statement must be block statements; that is they
must be enclosed with braces." However, dmd does not seem to require that
try/catch/finally blocks have braces, which is what I would expect that
sentence to mean. C++ has that restriction for reasons that I don't
understand, but as far as I can tell, D does not - certainly dmd doesn't
appear to require it. Is the statement correct but refers to something else,
or is it in error?
- Jonathan M Davis

On page 82 of TDPL, it's talking about try/catch/finally statements,
and it
says that "all controlled statement must be block statements; that is
they
must be enclosed with braces." However, dmd does not seem to require that
try/catch/finally blocks have braces, which is what I would expect that
sentence to mean. C++ has that restriction for reasons that I don't
understand, but as far as I can tell, D does not - certainly dmd doesn't
appear to require it. Is the statement correct but refers to something
else,
or is it in error?
- Jonathan M Davis

On page 82 of TDPL, it's talking about try/catch/finally statements,
and it
says that "all controlled statement must be block statements; that is
they
must be enclosed with braces." However, dmd does not seem to require
that
try/catch/finally blocks have braces, which is what I would expect that
sentence to mean. C++ has that restriction for reasons that I don't
understand, but as far as I can tell, D does not - certainly dmd doesn't
appear to require it. Is the statement correct but refers to
something else,
or is it in error?
- Jonathan M Davis

On page 82 of TDPL, it's talking about try/catch/finally statements,
and it
says that "all controlled statement must be block statements; that is
they
must be enclosed with braces." However, dmd does not seem to require
that
try/catch/finally blocks have braces, which is what I would expect that
sentence to mean. C++ has that restriction for reasons that I don't
understand, but as far as I can tell, D does not - certainly dmd
doesn't
appear to require it. Is the statement correct but refers to
something else,
or is it in error?
- Jonathan M Davis

On page 82 of TDPL, it's talking about try/catch/finally statements,
and it
says that "all controlled statement must be block statements; that is
they
must be enclosed with braces." However, dmd does not seem to require
that
try/catch/finally blocks have braces, which is what I would expect
that
sentence to mean. C++ has that restriction for reasons that I don't
understand, but as far as I can tell, D does not - certainly dmd
doesn't
appear to require it. Is the statement correct but refers to
something else,
or is it in error?
- Jonathan M Davis

[snip]
You are being too kind about this :o). Of course we need an errata list. I
was hoping I'd need to set it up later, but hey, that's a sign people
actually are reading the book and care about keeping everything in check.
I started an errata list in form of a community wiki at
http://www.erdani.com/tdpl/errata
and primed the errata with your report.
Thanks very much! I'll see to it that future printings fix the issues in the
errata list.

Will you fix all the god-awful puns too if we add those to the list? :-)
Just kidding, I'm sure suffering through them helps build character or
something.
--bb

[snip]
You are being too kind about this :o). Of course we need an errata list. I
was hoping I'd need to set it up later, but hey, that's a sign people
actually are reading the book and care about keeping everything in check.
I started an errata list in form of a community wiki at
http://www.erdani.com/tdpl/errata
and primed the errata with your report.
Thanks very much! I'll see to it that future printings fix the issues in the
errata list.

Will you fix all the god-awful puns too if we add those to the list? :-)
Just kidding, I'm sure suffering through them helps build character or
something.

[snip]
You are being too kind about this :o). Of course we need an errata list=

.

I
was hoping I'd need to set it up later, but hey, that's a sign people
actually are reading the book and care about keeping everything in chec=

k.

I started an errata list in form of a community wiki at
http://www.erdani.com/tdpl/errata
and primed the errata with your report.
Thanks very much! I'll see to it that future printings fix the issues i=

n

the
errata list.

Will you fix all the god-awful puns too if we add those to the list? =A0=

:-)

Just kidding, I'm sure suffering through them helps build character or
something.

If my puns suck I sure want to know which!
Andrei

They're fine. I'm just kidding.
"A pun is the lowest form of humor--
if you didn't think of it first." -- Oscar Levant
--bb

[snip]
You are being too kind about this :o). Of course we need an errata
list. I was hoping I'd need to set it up later, but hey, that's a sign
people actually are reading the book and care about keeping everything
in check.
I started an errata list in form of a community wiki at
http://www.erdani.com/tdpl/errata
and primed the errata with your report.
Thanks very much! I'll see to it that future printings fix the issues
in the errata list.

Will you fix all the god-awful puns too if we add those to the list?
:-)
Just kidding, I'm sure suffering through them helps build character or
something.

[snip]
You are being too kind about this :o). Of course we need an errata list.
I was hoping I'd need to set it up later, but hey, that's a sign people
actually are reading the book and care about keeping everything in check.
I started an errata list in form of a community wiki at
http://www.erdani.com/tdpl/errata
and primed the errata with your report.
Thanks very much! I'll see to it that future printings fix the issues in
the errata list.

Will you fix all the god-awful puns too if we add those to the list? :-)
Just kidding, I'm sure suffering through them helps build character or
something.
--bb

Aw. Those were awesome. Of course, I love puns, so I'm not about to complain
about them unless they're really bad, and Andrei's were good.
- Jonathan M Davis

That's not too difficult; for integers, retro(iota(a, b)) could actually be a
rewrite to iota(b - 1, a, -1).<

This is good. In my dlibs the xchain converts xchain(xchain(x, y), z) and
similar into xchain(x, y, z).

Figuring out all corner cases, steps greater than 1, and what to do for
floating point numbers is doable but not trivial either, and works against
modularity.<

I suggest to focus on the most important case, integer/uint indexes (and +1 or
-1 increment).
My post has shown some different problems:
- A longer compilation time (and binary size)
- A 1/10 performance when the code is compiled in standard way, this is bad
- a smaller performance when the code is compiled in optimized mode.
The asm of the opt version shows calls to two or more functions inside the
loop, and one of those functions is not so small, this probably reduces
performance more than an extra inlined product. So in my opinion getting rid of
those calls (inlining them) is more important if you want a faster retro(iota).
------------------------
I have added your last version:
// test4
import std.c.stdio: printf;
import std.range: iota;
void main() {
enum int N = 100_000_000;
int count;
foreach (i; iota(N - 1, 0, -1))
count++;
printf("%d\n", count);
}
Running time, best of 3, seconds:
test1: 0.31
test1 opt: 0.07
test2: 0.31
test2 opt: 0.12
test3: 6.38
test3 opt: 0.52
test4: 4.70
test4 opt: 1.25
not opt version = dmd (no other option)
opt version = dmd -O -release -inline
Compile times opt version, seconds:
test1: 0.05
test2: 0.05
test3: 0.28
test4: 0.29
The compilation time is the same, the not-opt test4 is faster than not-opt
test3, but opt test4 is quite slower than opt test3.
Bye,
bearophile

That's not too difficult; for integers, retro(iota(a, b)) could actually be a
rewrite to iota(b - 1, a, -1).<

This is good. In my dlibs the xchain converts xchain(xchain(x, y), z) and
similar into xchain(x, y, z).

Figuring out all corner cases, steps greater than 1, and what to do for
floating point numbers is doable but not trivial either, and works against
modularity.<

I suggest to focus on the most important case, integer/uint indexes (and +1 or
-1 increment).
My post has shown some different problems:
- A longer compilation time (and binary size)
- A 1/10 performance when the code is compiled in standard way, this is bad
- a smaller performance when the code is compiled in optimized mode.
The asm of the opt version shows calls to two or more functions inside the
loop, and one of those functions is not so small, this probably reduces
performance more than an extra inlined product. So in my opinion getting rid of
those calls (inlining them) is more important if you want a faster retro(iota).
------------------------
I have added your last version:
// test4
import std.c.stdio: printf;
import std.range: iota;
void main() {
enum int N = 100_000_000;
int count;
foreach (i; iota(N - 1, 0, -1))
count++;
printf("%d\n", count);
}
Running time, best of 3, seconds:
test1: 0.31
test1 opt: 0.07
test2: 0.31
test2 opt: 0.12
test3: 6.38
test3 opt: 0.52
test4: 4.70
test4 opt: 1.25
not opt version = dmd (no other option)
opt version = dmd -O -release -inline
Compile times opt version, seconds:
test1: 0.05
test2: 0.05
test3: 0.28
test4: 0.29
The compilation time is the same, the not-opt test4 is faster than not-opt
test3, but opt test4 is quite slower than opt test3.
Bye,
bearophile

I optimized things such that the commonly used path (many calls to
empty, front, and popFront) is as fast as possible. The initial work
will be amortized for most loops.
On my machine, test4 is still 2x slower than foreach_reverse in an
optimized build. The disassembly reveals that the compiler refuses to
inline some calls, which is disappointing as their bodies are very simple.
Andrei

If you would, file bugs with reduced tests and I'll work on the inliner.

...
In my opinion an empty loop is a valid loop, just as this is valid both
syntactically and logically:
for (int i = 10; i < 10; i++) {}
for (int i = 20; i < 10; i++) {}
So I suggest to replace that line in the iota() costructor with:
enforce(step != 0);
And then create an empty generator if pastLast <= current (and the step is 1).
Bye,
bearophile

Can someone sanity check me on the code on pages 334-5?
Does the method push really need !empty in its in contract?
I might not be fully awake yet><
A...

It has to be an error. If you couldn't push onto an empty stack, then
you'd never be able to put anything on the stack.
- Jonathan M Davis

That's what I thought, but it's there in the example over the page as
well, so I though maybe I was missing something ><
A...

I haven't look at it in detail yet, but it definitely looks like it's a
copy-paste error, and it makes no sense for a push function to insist that
something have already been pushed before you can push anything onto the
stack.
- Jonathan M Davis

Does anyone else feel that the following is a fair clarification?
Page 396
...
This means that communicating processors "forget" the exact order in
which data was written: one tread writes x and then y and for a while
another thread sees the new y but only the old x.
vvv
This means that communicating processors "forget" the exact order in
which data was written: one tread writes to x and then to y and for a
while another thread sees the new value of y but only the old value of x.
...
Once I got my head around what is being claimed, I realised that it is
correct as it appears, but it took me several reading to get there. It
might just be because I'm a bit of a concurrency novice, but I'm sure
I'm not the only one with a copy of tDPL ^^
A...