Latest articles

Combine Coroutines and Input Ranges for Dead-Simple D Iteration

Note: See the updates at the bottom of this article.

First, a little teaser (in the D programming language):

// testInputVisitor.d // Requires DMD compiler v2.059 or up import std.algorithm; import std.range; import std.stdio; import libInputVisitor; struct Foo { string[] data = ["hello", "world"]; void visit(InputVisitor!(Foo, string) v) { v.yield("a"); v.yield("b"); foreach(str; data) v.yield(str); } void visit(InputVisitor!(Foo, int) v) { v.yield(1); v.yield(2); v.yield(3); } } void main() { Foo foo; // Prints: a, b, hello, world foreach(item; foo.inputVisitor!string) writeln(item); // Prints: 1, 2, 3 foreach(item; foo.inputVisitor!int) writeln(item); // It's a range! Prints: 10, 30 auto myRange = foo.inputVisitor!int; foreach(item; myRange.filter!( x => x!=2 )().map!( x => x*10 )()) writeln(item); }

Rewinding back to the start, here's our original problem: We want to iterate over a collection.

Ever since the D1 days, D has had foreach's parter, opApply, as the standard way to do this:

// testOpApply.d import std.stdio; struct Foo { string[] data = ["hello", "world"]; int opApply(int delegate(string) dg) { int result=0; result = dg("a"); if(result) return result; result = dg("b"); if(result) return result; foreach(str; data) { result = dg(str); if(result) break; } return result; } } void main() { Foo foo; // Prints: a, b, hello, world foreach(item; foo) writeln(item); }

The logic within opApply is mostly natural and straightforward, but it is complicated a bit by dealing with that result variable.

Later on, D2 came along and introduced ranges - D's clean, powerful answer to C++/STL's iterators. The concept of ranges and their motivations and benefits are well explained in Andrei Alexandrescu's classic article On Iteration (and further information is in this article), so I won't go into that.

Here's an input range version of the above examples:

// testInputRange.d import std.stdio; struct Foo { string[] data = ["hello", "world"]; @property auto inputRange() { struct FooRange { private Foo foo; private size_t index=0; @property bool empty() { return index >= foo.data.length + 2; } @property string front() { if(index == 0) return "a"; if(index == 1) return "b"; return foo.data[index-2]; } void popFront() { index++; } } return FooRange(this); } } void main() { Foo foo; // Prints: a, b, hello, world foreach(item; foo.inputRange) writeln(item); }

Unfortunately, that's more complex than the old opApply style, but it provides some very good benefits, particularly when used together with Phobos's std.algorithm and std.range.

But if all you need is basic foreach-style input range capabilities, that can be a lot of boilerplate. Plus, it doesn't lend itself to complex internal logic quite as naturally as opApply does. And opApply itself could stand to be easier to write.

If you've looked carefully, you may have noticed that opApply is essentially a poor man's coroutine. That's why the flow of logic is more natural in opApply than in the range version. For those unfamiliar with coroutines, see this old-but-excellent set of slides and the accompanying video. Here's the upshot: Coroutines are like a combination of traditional iterators and the visitor pattern - combining the best of both worlds.

D offers coroutines via Fiber. Looking at the docs, one of the two main ways to use Fiber is by subclassing it. Hmm...a class...easy coroutine iteration...What if we designed a Fiber subclass that was also a range?

As it turns out, fiber coroutines and input ranges make a fantastic combination that combines the power and flexibility of an input range with opApply's natural flow of logic, but without the boilerplate of opApply's messy result variable. You've already seen in the first example how easy it is to use this InputVisitor, as I call it. The source is very short, and available in the file libInputVisitor.d.

There is one small enhancement we can make to the original example, though. Compared to the opApply version, the foreach when using InputVisitor is a little more verbose:

foreach(item; foo.inputVisitor!string)

We can trim that down by adding one member to the Foo struct:

... struct Foo { ... @property auto visitor() { return this.inputVisitor!string; } ... } void main() { Foo foo; // Prints: a, b, hello, world foreach(item; foo.visitor) writeln(item); ... }

Another member can also be added to handle the int version.

The downside of this is that it doesn't lend itself very well - if even at all - to any ranges more powerful than the basic input range. Execution of a function doesn't flow in reverse, so bidirectional ranges are out. And certainly no random access. Forward range might be possible in theory, but it would depend on being able to clone a halted fiber - I don't know whether that's possible.

Beware, I haven't done any benchmarks on this, so I have no idea how much of an overhead this InputVisitor might have over the opApply or input range versions. Could be a lot, could be minimal - I don't know.

UPDATE (2012-05-01): Changed libInputVisitor license to WTFPL. For something this trivial, even the super-permissive zlib/libpng license is overkill.

UPDATE (2012-05-10): This D newsgroup thread revealed that the InputVisitor approach does in fact have a non-trivial performance overhead. Oh well :(

But Adam Ruppe pointed out a nice trick for improving the syntax of the opApply version. His approach, used like mixin(yield("someVar"));, isn't quite as nice looking as v.yield(someVar); and doesn't result in an actual range (you can only foreach over it). But when you just need a basic generator, I think it provides the best balance between syntax and speed.

Read more


Goldie Parsing System v0.9 - Tools

Goldie v0.9 is now released.

Goldie is a series of open-source parsing tools, including an optional D programming language library called GoldieLib. Goldie is compatible with GOLD Parser Builder and can be used either together with it, or as an alternative to it.

In this version:

(Tested to work on: DMD 2.055 - DMD 2.059, and partially DMD 2.057 as described here)

  • Added support for DMD 2.059.
  • Dropped support for DMD 2.054 and below.
  • GoldieLib: Renamed Language.loadCGT to Language.load.
  • GRMC: Grammar Compiler: Major bugs fixed (see ChangeLog)
  • New tool: AlterCGT.
  • Command line options for all tools are now processed with getopt and use standard Unix conventions (even on Windows). This means what used to be -foo:bar or /foo:bar must now be written as --foo=bar or --foo bar. (But /? is still supported as an alternative to --help.)
  • Misc changes/updates to various tools (see ChangeLog)

Links:

Read more


Porn/Prostitution Laws Are Weird

Here's what I don't get:

The exchange of money for sex is typically illegal...unless you sell a videotape of it. Then it's ok.

Read more


Aspect Ratios Are Easy, Yet They're Botched More Than Ever

Ever since widescreens appeared, botched up aspect ratios have only gotten more and more common. Go into any restaurant, for example, and if they have TVs up (an annoying practice anyway), chances are 50/50 it'll be displaying an image bizarrely distorted by stretching/squishing, or an image with big black borders on all four sides (which I'm going to call "double-letterboxed", because that's what's happening).

This is two-thousand-goddamn-twelve. In this age of palm-sized supercomputers and HD, that's just inexcusable. Especially considering how simple it would have been to avoid.

Since industry can't seem to get it right, I'll spoon-feed them the solution they should have been able to standardize on from day one of the first widescreen TV:

  1. Your video feeds support metadata. Even over-the-air broadcasts. If you know how to stick copyright flags and program information into a video stream (and for the record, you do), then you already know how to embed an aspect ratio. Do it, if you're not already. And standardize the damn thing, if it isn't already.

  2. When the video feed changes to a different aspect ratio, change the damn aspect ratio information.

  3. From the original video author's desktop to the user's home system, do NOT fuck with the aspect ratio. Do NOT change the aspect ratio flag. Do NOT letterbox the feed. Do NOT crop the feed. And for the love of god, do NOT stretch/squish the feed non-uniformly.

  4. A TV should already know its own dimensions. It's not as if they're ever going to change. So when it receives video, have it UNIFORMLY scale the video so that one dimension matches the screen, and the other dimension is smaller than the screen and centered.

  5. If you must, let the user optionally choose "crop" (the same as above, but using the other dimension to match to the screen) or "stretch" (ie, non-uniform scale-to-fit). But don't make that stupid shit the default. It should never be the default. Never, never, never.

  6. What you say? Non-digital, non-smart TVs? Simple: Any device that outputs to a TV - a cable box, digital tuner, DVD player, game console, whatever - is told by the user what aspect ratio their screen is. Show them a square and a circle to make sure their choice is correct. Better yet, show them various squares/circles/rectangles/ellipses, and when they choose which pair looks like a square and circle: Guess what? You know their aspect ratio!

  7. To minimize user setup and screwup, set-top boxes (whatever set-top box it may be) should attempt to auto-detect whether the screen handles aspect ratios on its own and, if not, what aspect ratio the screen is. Do NOT make assumptions. Whenever there's any doubt, don't pull a "salesman" and grab an answer out of your ass: Have the user choose their own "square and circle".

  8. What? Auto-detect? Yes. Even VGA had ways to send monitor information to the source device. Surely HDMI and DVI should be capable of the same. And if RCA cables are being used, that just means you're back in user-selection land.

So there. You've now been told how to not fuck up aspect ratios, so after years of shitty distorted and double-letterboxed video feeds, let's start getting it right.

Read more


Restaurant TVs Are Annoying

Movement, such as that on TVs, automatically draws the eye. So if a nearby TV is on, it's difficult not to watch it.

But what's the point of eating out if your attention's just gonna get drawn to a screen? You have screens at home. May as well have gotten takeout - it ends up amounting to the same thing. Defeats the point of eating out, if you ask me.

Sports bars are a different matter, of course. But then, I like neither bars nor sports. Nor drunks. Nor 30/40-somethings pathetically clinging to their frat days. I do, however, like bar food and good booze. Who doesn't? So...umm, what was I saying? Something about wristwatches, I think?

Read more


Fixed Comment Styles

It had been bugging me forever, but I've finally gotten the CSS for comments fixed. I think it got screwed up (along with many other things) waaay back when I upgraded TangoCMS.

So anyway, now the article comments don't look like crap anymore! Yay!

Read more