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 -- It always amuses me that Windows has a Safe Mode during bootup. Does that mean that Windows is normally unsafe?