On Friday, 2 August 2013 at 05:26:05 UTC, H. S. Teoh wrote:
On Thu, Aug 01, 2013 at 10:34:24AM -0700, Walter Bright wrote:
On 8/1/2013 2:23 AM, John Colvin wrote:
>On Thursday, 1 August 2013 at 00:47:43 UTC, H. S. Teoh wrote:
>Add in some code examples and that could make a nice article.
Yes, please!
Alright, so I decided to prove my point about component
programming by
actually writing a fully-functional version of the calendar
layout
program, so that I have a solid piece of evidence that component
programming lives up to its promise. :) In addition, I decided
that for
maximum reusability, I want the output lines available in an
input
range, with absolutely no binding to writeln whatsoever (except
in
main() where the range is handed to writeln for output). In
retrospect,
that was perhaps a bit too ambitious... I ran into a few
roadblocks to
actually get the code working, so it took me a lot longer than I
anticipated to finish the code.
However, I *will* say that I'm very proud of the code: already
there are
a few pieces that, if properly cleaned up and refined, probably
deserve
inclusion in Phobos. Reusability FTW!! Now, just tell me if
you've ever
seen a calendar layout program made of straightforward,
reusable pieces.
I for sure haven't. I tried looking at the C code for the Unix
cal
program once... It looked frighteningly similar to an IOCCC
entry. :-/
My D version, however, built using ranges through and through,
has many
pieces that are easily reusable. For example, if you wanted to
output
only a single month instead, you could just call join("\n") on
the range
of formatted month lines that the full year layout algorithm
uses to
splice lines from multiple months together -- it's *that*
reusable.
Anyway. Enough hand-waving in the air. Let the actual code
speak for
itself:
https://github.com/quickfur/dcal/blob/master/dcal.d
Now, w.r.t. the roadblocks I alluded to.
When I first started working on the code, my goal was to
maximize usage
of existing Phobos facilities in order to show how many
batteries D
already comes with. As it turned out, I could only use basic
Phobos
components; some of the more complex pieces like
frontTransversal, which
would've been perfect for the bit that splices formatted month
lines
together, couldn't be used because it wasn't flexible enough to
handle
the insertion of fillers when some subranges are empty. In the
end, I
had to code that range by hand, and I can't say I'm that happy
with it
yet. But at least, it's nothing compared to the hairy
complexity of the
C version of cal.
Another place where I wanted to use existing Phobos components
was
chunkBy. There's probably a way to do it if you think hard
enough about
it, but in the end I felt it was simpler to just write the code
myself.
Might be a failure on my part to recognize how to put existing
Phobos
ranges in a clever enough way to achieve what I wanted. I did
try to do
something similar to byWeek(), but somehow it didn't do what I
wanted
and I decided to just code it by hand instead of investigating
further.
By far the biggest roadblock I ran into was that after I wrote
everything up to (and including) pasteBlocks, my unittests
refused to
work. Somehow, pasteBlocks kept repeating the first line of the
output
(the month names, if you look at the unittest) and refused to
advance
farther. Eventually I traced the problem to Lines.popFront(),
which
pops each subrange off the range of ranges. The problem is that
this
only works on some ranges, but not others; if you pass the
output of
formatMonths() straight to pasteBlocks(), it will NOT work.
Why? Because
pasteBlocks return a std.algorithm.Map object, which recreates
the
subrange each time, so Lines.popFront() is only popping a
temporary copy
of the subrange, not the real thing. I was about to give up and
try
another approach, when out of the blue I decided to try and see
if I
could stuff the range returned by formatMonths() into an array,
and then
pass *that* to pasteBlocks() -- and behold, it worked!!
This was a totally unexpected fix, that a newbie probably would
never
have thought of, so this is a potential trap for newcomers to D
who
expect components to just be pluggable. In retrospect, it makes
sense --
you need to somehow buffer the ranges of formatted month lines
*somewhere* in order to be able to splice them together out of
their
natural depth-first outer/inner range order. But this is not
obvious at
all from first glance; perhaps it's a sign of a leaky
abstraction
somewhere. We should probably look into why this is happening
and how to
fix it. And there should be a way to test for this in
pasteBlocks'
signature constraint so that future code won't fall into the
same trap,
but I can't think of one right now.
Once this last bit worked, though, everything fell into place
quickly.
After all unittests were passing, no more bugs were found!! The
program
can print beautifully laid out calendars with no problems
whatsoever.
I'm so in love with D right now... If I'd done this exercise in
C or
C++, I'd be spending the next 2 days debugging before I could
present
the code for the world to see. D ranges and unittest blocks are
t3h
k00l.
T
Good work! I've read the article yesterday. Very educational!