Friday, October 13, 2017

Buying an Old Thinkpad

I've recently been bitten by the theoretical Thinkpad bug. That is, I can see the attraction of these machines but I have no practical experience with them, especially not with the weird red dot on their keyboards. I also hate my current Lenovo laptop with a passion, so spending a lot of money without knowing if I'll like the machine seemed ill-advised indeed.

The good news is that the entire Thinkpad line seems to thrive on a myth of "eternal stability" meaning they simply don't change much (on the outside anyway) from year to year. So I figured I should just buy an older model for less money and see how it handles. If I end up liking it, I'll eventually shell out the dough for a new one.

I am obviously not the first person with that idea. Here's a reddit page that explains why older Thinkpads are a great way to go and there's even a buyer's guide specifically targetting older machines. So this post is less about pitching the idea of older Thinkpads and more about my own deliberations on which old model to get.

I decided to play the role of a "serious" user so the only Thinkpads I even considered were the T series and the X series. The strange sliver of my personality that made me an Apple fanboy back in the 1998-2004 time frame is immensely attracted to the X series. Thin, light, compact, sexy, wow! However, the newly discovered grown-up in me tends to prioritize things differently. I need a machine that I can work on comfortably for hours at a time which means I need a big display, good hand rests, a spacious keyboard, etc. If I really fall in love with Thinkpads during this experiment, no doubt I'll eventually own an X series product as well. But for now I'll stick to the T series.

Within the modern T series we have the bigger T5xx models (15.6" display, numeric pad) as well as the smaller T4xx models (14" display, no numeric pad). This is sort of a toss-up for me: A larger display is nice, but a numeric pad is completely useless. In terms of weight we're looking at 4.5 to 6 pounds for T5xx machines versus 3 to 5 pounds for T4xx machines (across the 2008-2017 time frame that is).

Just looking at the numbers above I couldn't really make up my mind until I remembered that my current Lenovo laptop is in the T5xx class when it comes to size and weight. And I have to admit that something a little lighter would be a lot nicer to carry around. So the entire T4xx line qualifies, but only the relatively recent T550, T560, or T570 models are light enough in the T5xx line. The crappy current laptop also helped me determine my "historical" cut-off point: I have a 2012 laptop now and I don't want to go earlier than that in terms of technology.

So now I am left with the T430-T470 as well as the T550-T570 in terms of my various criteria. But wait, the whole point was to buy an older, cheaper Thinkpad! That narrows the field further to just three realistic categories:

  • 2012: T430, T430s, T430u
  • 2013: T431s, T440, T440s, T440p
  • 2015: T450, T450s, T550

So I started looking into all these models and the first thing I noticed was that the T431s and all the T440 models have a very different trackpad setup from the rest (and indeed from all Thinkpads before and since). That takes all of these machines out of the race as well, after all I want to have a "true Thinkpad experience" and not some strange aberration that only existed for a brief instant.

For the remaining models, I decided to read all the in-depth reviews I could find. But after doing that for a while, I realized that there's really only one review site worth reading, so here are the links to all those reviews, followed by my own comments:

  • T430: The i7 tends to run hot at 55C. Granted, only under load, but I don't like the idea of a runaway computation setting my couch on fire. So it's out.
  • T430s: The i5/620M tends to run warm at 43C, the i7/4000 tends to run warm at 46C, neither of which is terrible. Despite being smaller and lighter than the T430 this thing still has an optical drive bay, something I like. Depending on condition and features, these ran between $170 (crappy) and $400 (like new).
  • T430u: The i5 tends to run warm at 45C. It's the only older model with HDMI, a big plus in retrospect. However, it's also the one model with only a fixed battery and that's a big minus. But worst of all the screen is terrible, so it's out.
  • T450: The i5 tends to run warm at 47C and I'd be okay with that. What I wasn't okay with is that these started at $520 which I deemed too expensive.
  • T450s: The i7/5500 is a dream and runs cool at 36C even under load! The i5/940M on the other hand runs hot at 52C which is too much for me. Alas it didn't matter, also too expensive at $600+.
  • T550: The i7 tends to run cool at 39C. Sadly most of these were also very expensive ($750+), the only affordable one I could find (at $379) had a locked BIOS.

I am obviously a little obsessed with temperatures. Everybody has their thing and I've had plenty of "laptop too hot for my lap" experiences that I don't care to repeat.

You can probably guess that I finally settled on the T430s for my experiment. The one I ordered was a little more expensive than I had hoped for ($265 instead of $250) but that's close enough. What convinced me in the end was that the seller actually wrote a long reply, covering all the details that were important to me.

Once it gets in, I'll try to do another post (maybe with pictures) about the machine and then it's on to actually testing it by replacing my current laptop for a week or three of lectures. Oh, and I need to order an HDMI adapter because we mostly have HDMI at Johns Hopkins these days.

Playing id Classics on Linux

Just for reference since I've been going through a somewhat extreme retro-gaming phase lately. Of course you'll need the actual game data for all of these. Luckily I have old CD-ROMs that still work fine (even though I of course have backups as well). The details for extracting the data vary, maybe that's a topic for a future post. But if you can extract it, here's what to run the games with:

  • DOOM, DOOM 2, ...: For the best "retro-feeling" I recommend Chocolate Doom. If your distro doesn't package it, just grab the source and build it yourself, it's not hard. Youngsters may not like it because it's designed to give you an "accurate" 1993 experience (pixels!), but that's how it's meant to be played so deal with it.
  • Quake, ...: The only thing I can recommend is the DarkPlaces engine. Don't get me wrong, it's an awesome effort and it does indeed look a lot better than the original. But my vintage 2009 graphics card actually has trouble keeping up which is a little sad for what is ostensibly a 1996 game. Alas, every other source port I tried didn't actually work, so... Also surprisingly easy to build, just grab the source and hit make. Just be prepared to wait a little longer than for the Chocolate Doom build.
  • Quake 2, ...: Even fewer choices for Quake 2, but luckily Yamagi Quake 2 is excellent and they didn't overdo it with the graphical polish. I can play this at full speed with all the options turned up and it looks more or less like it should. Once again a pretty easy build, just grab the source and boom there it is. One minor quibble: They don't have a command line flag to configure the base path, so if you don't want to throw the executable into your data partition you'll have to change two lines in the Makefile.

There you have it, the stuff I've been playing with over the last two weeks or so. Good clean retro fragging, who can resist? I may update this post with more details if there's interest, so leave a comment if you prefer another engine that I missed or if you can't get the above to work for you.

Sunday, July 23, 2017

Protecting Weaker Party Members

I am currently running a B/X game that started with the sentence "We've all decided to play magic-users!" from one of the players. Luckily my adopted variant of "3d6 in order" prevented that outcome and they're a pretty typical "murderhobo" party now: two magic-users, two fighters, one thief, and one blood-thirsty halfling with suspenders but no shirt. (There was a cleric, but that's another story.)

In any case, one of the players was "completely new" and of course I recommended that she play a fighter. I sold this mostly with "it's the easiest class to run" but also with "all those robes-and-silly-hats people will need protection" which is what me got thinking:

There is no "rule" in B/X that would allow a fighter to protect a magic-user or anyone else for that matter.

Seemed like something important to offer at the time, but looking around the many B/X house rule documents out there I couldn't find the problem addressed by anyone. Even Delta who has probably forgotten more D&D than I will ever know left me out in the cold on this topic (aside from mentioning the need to have lots of fighters to protect magic-users in mass battles).

I wanted a simple mechanic that would make the protected character "safer" in some way (benefit) while also "exposing" the protecting fighter more (cost). I quickly settled on two "ground rules" for this:

  • The fighter must be "close" (within 5' say) to the character they protect.
  • The fighter must be "aware" of an attack in order to protect against it.

I guess you could say that I am treating the fighter as an "intelligent shield" of sorts? That approach seems fair to me because I don't want a fighter to simply throw themselves over the character they are trying to protect. I very much want "protector" and "protectee" to still be "in combat" instead of changing the focus to a Secret-Service-style "Get him out of here!" and nothing else.

The simplest thing I could come up with was to simply let the fighter take the damage. So as a rule, I'd phrase it something like this:

Fighters can choose to protect one character within 5' of them. If the protected character gets hit by an attack the fighter is aware of, the fighter can choose to take the damage instead.

Not too shabby in terms of simplicity, is it? But as always the "devil" is in the details. First of all this conveys almost complete immunity to the protected character. But the goal was to make them "safer" only, not "safe". In other words, I still want the magic-user to worry about maybe having their spell interrupted. Also we have reduced the fighter to an actual meat shield ("bag of hit points") and nothing else: Regardless of how good their AC is, they now get skewered because of the (presumably) much worse magic-user AC. That's not fun at all for whoever is playing the fighter. Finally, if we have multiple fighters protecting one character, the "immunity" could actually be complete and it could go on for a long time indeed. This may work great for Delta's mass combat scenario, but on the "party in a dungeon" scale I wouldn't want to deal with it.

So instead of letting the fighter take all the damage, let's try to do something about getting hit in the first place. Here's what I came up with next:

Fighters can choose to protect one character within 5' of them. Attacks against the protected character that the fighter is aware of suffer a -8 penalty. Attacks against the fighter, however, gain a +2 bonus.

The numbers are subject to debate of course, but this seemed fair to me. Presumably the kobold archers trying to hit the magic-user would notice that the fighter brushed away all their arrows, so next round they'd redirect their fire. But as so often when you design a rule with fixed penalties/bonuses, there's an exploit: Two or more fighters protecting each other would impose a -6 penalty against most attacks, clearly not the application that was intended. Except for that terrible flaw, however, we would get the desired effects: The protected character still has to worry about getting hit, and the fighter now makes for an easier target because they are "out there" trying to distract attackers.

What finally made me think of a better solution is this: With the fixed penalty, a naked fighter could protect a character in plate and shield. That makes no sense! What should happen instead is something like this:

Fighters can choose to protect one character within 5' of them. Attacks against the protected character that the fighter is aware of must hit the fighter's armor class instead (provided that armor class is better). Any damage is still suffered by the protected character. Attacks against the fighter also gain a +2 bonus.

I am reasonably happy with this rule. It's a little more complex, but not overly so. It does allow protecting weaker party members if they have a worse armor class, but it doesn't make them (almost) completely safe. It also has no obvious exploit that I can think of, mostly by virtue of letting the better armor class prevail. I'd also say that a spell like Protection from Normal Missiles cannot be used with this rule as it doesn't affect armor class, but your mileage may vary.

I've added the last version as a house rule to said B/X game, but after two sessions so far it has not seen any use yet. Admittedly the characters were not really in situations where it made much sense to try yet, but here's to hoping someone will attempt it sometime soon. In the meantime, I'd love to hear what everybody out there thinks of what I did here.

BTW, I am aware of the 5th edition Protection Fighting Style thing but it's just too horribly complicated and too far from B/X to try to convert. It's interesting however that the 5e folks also thought that the "big fighter protecting small magic-user" trope needed "a bit more rule" behind it.

Thursday, December 29, 2016

Editions of Armor Class Part 1: No Armor

A few weeks ago one of Zenopus' excellent posts made me think about "armor class" in D&D again. On Google+ I even commented "I like that booklet too, but I really couldn't care less about AD&D armor classes. The original system just makes so much more sense. I feel a blog post coming on..." Sadly life happened and I couldn't find the time to write about the crazy in my head then. But now that I am safely tucked away in Germany for my year-end vacation, I figured I'd give it a quick shot.

Disclaimer: In true grognard-style I shall only consider "descending armor class" in the following. I don't care about "ascending armor class" systems, especially since the usual "selling point" of those doesn't apply once you use Delta's Target20 mechanic.

Let's begin with the classic AC 9 versus AC 10 debate: Should an average character who is not wearing any armor be AC 9 as OD&D, B/X, and BECMI profess, or AC 10 as AD&D wants us to believe? For me, any answer that doesn't immediately consult the combat tables is of a purely religious nature. It really doesn't matter whether we start at AC 9 or AC 10, what matters is whether there's a difference in running a combat. Here I prefer the B/X (and to some extent BECMI) story:

Two average, unarmored, untrained, "normal humans" should have a 50% chance per round to hit (and most likely kill) each other.

I don't know about you, but that seems perfectly reasonable to me. It certainly shouldn't be more than 50% because that would imply skill where (by definition) none should exist. And if it was less than 50% things would just drag out longer.

If you consult the B/X Basic Set, page B27, you'll see that a "normal man" indeed needs an 11+ to hit AC 9, a chance of 50% on a d20 roll. Perfect! As for the "most likely kill" part: On page B25 we find that a successful attack does 1-6 points of damage (average 3.5) while page B40 explains that "normal humans" have 1-4 hit points (average 2.5). Death incarnate!

In the BECMI Expert Set (but not the Basic Set, go figure!) we find the same "11+ to hit AC 9" for "normal man" on page 29; alas the BECMI Basic Set says that "normal humans" have 1-8 hit points (average 4.5) so things are a little less deadly (never mind the other problems this change causes, sigh).

What about OD&D? On page 19 of "Men and Magic" we find (perhaps surprisingly?) that "normal men equal 1st level fighters" which means they only need a 10+ to hit AC 9, a chance of 55% on a d20 roll. Granted, it's only a 5% difference, but that seems wrong to me. Why would an untrained combatant have the same skill as a trained one?

AD&D does away with "normal men" for the most part, replacing it with the notion of "0 level" characters (for which only humans and halflings qualify?). AD&D also recalibrates to AC 10 for "no armor" of course. Well, at least starting in the Player's Handbook it does, the Monster Manual seems to be written to the original AC 9 for "no armor" instead. But at least Gary manages to stay somewhat true to my B/X story: On page 74 of the Dungeon Master's Guide we learn that "0 level" characters need 11+ to hit AC 10, a chance of 50% on a d20 roll again. Of course average hit points are "off" as in BECMI, see page 88 of the Dungeon Master's Guide.

So then... What should "no armor" be, AC 9 or AC 10? As I said before, it doesn't matter! B/X (and to a lesser degree BECMI and even AD&D) get it "right" as far as I am concerned, only OD&D has it "wrong" since it doesn't distinguish trained from untrained combatants.

Of course there is one difference after all: I strongly prefer single-digit AC values, and so in the final analysis, AD&D is out. But I have to admit that this preference is mostly "religious" as well, not truly "technical" as it were. True, Target20 is easier with single-digit AC values, but since that's not a standard mechanic I can't really use it to "rationalize my irrationality" too much. Does "neater table layout with single digits" count?

Friday, October 14, 2016

The S.M.A.R.T. Overflow

Before today, I didn't realize that S.M.A.R.T. has overflow issues. I should say that I spent most of yesterday replacing three disks in my home machine's RAID-10, so I constantly was using smartctl to double-check stuff. So when I got to work today, I poked around the disks in my server. And I noticed this:

SMART Self-test log structure revision number 1
Num  Test_Description ... LifeTime(hours) ...
# 1  Short offline    ... 2484            ...

# 2  Short offline    ... 2469            ...
# 3  Short offline    ... 2445            ...

This made absolutely no sense, because I knew that most of the disks in there were certainly older than a few months. After some digging, I came to realize what the problem is:

SMART Self-test log structure revision number 1
Num  Test_Description ... LifeTime(hours) ...
# 1  Short offline    ... 1276            ...
# 2  Short offline    ... 62136           ...
# 3  Short offline    ... 61776           ...


The actual value for Power_On_Hours is 66812 for that disk. Let's subtract 1276 from that and what do we find? 65536 of course. So the life time recorded with each self-test is apparently an unsigned 16-bit value, whereas the total hours recorded for the disk itself is stored as something bigger. Here's to hoping that people who write scripts telling them which disks to swap are aware of this...

Wednesday, June 29, 2016

Choice of Class: Enforce or Encourage?

For as long as I can remember, I've been a fan of "minimum ability score"-type requirements for classes. See, for example, my "addendum" at the bottom of this post or, more recently, these posts.

It just made sense to me that fighters should have (at least) average strength and that wizards should have (at least) average intelligence. In case of the wizard, I still think it makes perfect sense even from an "in game" perspective: Why would that archmage waste his or her time training a moron? In case of the fighter, well, maybe strength is not so important: After all, one purpose of having many soldiers (whether weak or strong) on the field is just so that more can die before whatever lord commands them actually loses a battle. (The term "cannon fodder" exists for a reason.)

But more importantly, I just couldn't see why anyone would want to play a weak fighter or a dumb wizard. So the idea of using "minimum scores" to control what classes a player can pick seemed perfectly alright, even if it meant that there would be a small percentage of characters who can't qualify for any class at all: Just re-roll those, problem solved.

However, it recently dawned on me that I don't speak for everybody. (Shocker!) What if there is a player who does enjoy that weak fighter? Maybe that fighter, while physically not "up to snuff" as it were (low strength), is a master strategist and leader (high intelligence and charisma)? True, the player could choose a wizard instead, but that would make for a very different type of leader, maybe not what they were going for.

(Things get worse if you allow players to multi-class. Now the ability scores would have to be good enough for two classes, something that's not particularly likely with 3d6 even if the minimum requirements are low-ish.)

Instead of enforcing the set of classes that a given character can choose from, it may be preferable to encourage certain choices (and to discourage others of course). The "good news" is that we already have a mechanic that does just that, and it has existed in all relevant versions of D&D since 1974: Experience point adjustments for prime requisites. Here's what the B/X rules say:

The ability most important to a class is called the prime requisite for that class. The higher the prime requisite score, the more successful that character will be in that class.
Basic Rulebook, Page B6

The details are not horribly important, and you probably know them anyway, so let's just summarize that the score in a prime requisite results in a bonus (or penalty) of up to +10% (or -20%) on the experience points earned by the character. Now I should first point out that I used to hate these adjustments. The math is not too horribly bad, but we're still left with a bag of questions:

  • Exactly what ability score counts for the experience adjustment? The initial score when the character was created? The current score which might be adjusted by a magic item or a curse?
  • Does a change in a prime requisite ability score retroactively affect the current experience point total? If so, raw experience points and bonus experience points should be tracked separately.
  • What is the "in game" justification for the bonus (or penalty) on experience points? The D&D experience system is already on pretty weak grounds (exactly how does getting richer make someone better at picking locks again?) and it seems these adjustments make even less sense in that light.

Note, however, that minimum ability score requirements actually raise many of the same questions: If you're a fighter with strength 9 but get cursed to have strength 6, are you still a fighter? If you rolled a character with strength 4 but the DM grants you Gauntlets of Ogre Power at character creation, can you pick the fighter class? And what happens when you lose those gauntlets? Also, regardless of whether we consider XP = GP good or bad, that's still the default in old-school D&D, so why worry when the system gets a little more insane? It's already nuts but most people don't really mind.

What I came away with after thinking about this for the past week or so was mildly surprising to me:

After 20+ years of disliking the prime requisite experience adjustment rule, I now think it's a stroke of genius!

It allows the player to make more choices for his or her character, and that's never a bad thing (well, almost never, let's hope newbies will have a supportive DM to cut down the number of choices a little). It encourages certain choices, but it doesn't force anything. In fact, coming up with a background that explains why that dunce with intelligence 6 was able to get training as a wizard actually sounds like a lot of fun.

In my particular house rules, things get even better. Say someone wants to multi-class as a fighter/priest to approximate a paladin-like character. If they have average strength and wisdom, they simply progress on the slower XP table for characters with two classes. If they have excellent strength and wisdom for a +10% bonus on XP each, they actually progress with +20% on that harder/slower table. If they are weak (-10% penalty due to low strength) but pretty wise (+10% bonus due to high wisdom) things cancel out: It's still a viable paladin-like character.

So I am off to rewriting my house rules yet again: No more minimum ability score requirements for classes! (Hmm, should I add some for races now?)

Friday, April 22, 2016

Tracking Positions in Go: Why Composition Rocks

In this post I discuss the genesis of the streampos package for Go that I recently posted on github. I don't normally write about code I post, but in this case I learned something cute that I wanted to share. Maybe you'll find it enjoyable too?

The story starts with me writing some Go code to process XML files. Of course I used the encoding/xml package from the Go standard library to do this. A few hours into the job I needed to generate error messages when something is not quite right in the XML file. (If you've dealt with large-ish XML files that get edited manually, you probably know that it's quite easy to make the occassional mistake that syntax highlighting alone will not protect you from.) In order for those error messages to be useful, they should tell the user where in the XML file the problem was detected. And that's when I ran into trouble: There's no straightforward way to get, say, line numbers out of encoding/xml!

Sure, their code tracks line numbers in support of their error messages (check
SyntaxError for example) but if you have your own errors that go beyond what the standard library checks, you're out of luck. Well, not completely out of luck. If you're using Decoder to do stream-based XML processing, you can get something moderately useful: the InputOffset method will give you the current offset in bytes since the beginning of the stream.

What do you have to do to turn that into error messages of the kind users expect, so error messages in terms of line (and maybe column) numbers? First you somehow have to get your hands on the raw input stream. Then you look for newlines and build a data structure that allows you to map a range of offsets in the stream into a line number. With a little more code on top, you even get out column numbers if you want them. Sounds like fun, but just how should we do it?

To use Decoder you have to give it a Reader to grab input from. This is promising because Go very much prides itself in the power that simple interfaces like Reader and Writer provide. Indeed, the pattern of wrapping one Reader inside another (or one Writer inside another) is fundamental in much of the standard I/O library. But we can also find those interfaces in "unrelated" parts of the library: The Hash interface, for example, is a Writer that computes hashes over the data written to it.

So the Decoder wants a Reader, but thanks to interfaces any Reader will do. It's not a big leap to think "Hey, I can hack my own Reader that tracks line numbers, and I'll pass that one to Decoder instead of the original Reader!" That's indeed what I considered doing for a few minutes. Luckily I then realized that by hacking a Reader I am actually making myself more problems than I had to begin with.

I want to solve the problem of building a mapping from offsets to line numbers. However, as a Reader, I also have to solve the problem of providing data to whoever is calling me. So whatever the original problem was, as soon as I decide to solve it inside a Reader, I immediately get a second problem. And it's not an entirely trivial problem either! For example, I have to consider what the code should do when asked to provide 37 bytes of data but the underlying Reader I am wrapping only gave me 25. (The answer, at least in Go land, is to return the short read. But notice that it's something I had to think about for a little while, so it cost me time.) On a more philosophical level, inserting a Reader makes my code an integral part of the entire XML thing. I never set out to do that! I just wanted a way to collect position information "on the side" without getting in anybody's way.

In the specific case of encoding/xml things actually get even funnier. It turns out that Decoder checks whether the Reader we hand it is actually a ByteReader. If it's not, Decoder chooses to wrap the Reader we hand it again, this time in a bufio.Reader. So either I have to implement a ByteReader myself, or I have to live with the fact that plugging in my own Reader causes another level on indirection to be added, unnecessarily to some extent. (That's yet another problem I don't want to have to deal with!) There really should be a better way.

And there is: Instead of hacking a Reader, just hack a Writer! I can almost see you shaking your head at this point. "If the Decoder wants a Reader, what good is it going to do you to hack a Writer?" I'll get to that in a second, first let's focus on what being a Writer instead of a Reader buys us.

The most important thing is that as a Writer, we can decide to be "the sink" where data disappears. That is, after all, exactly what the Hash interface does: It's a Writer that turns a stream into a hash value and nothing else, the stream itself disappears in the process. What's good enough for Hash is good enough for us: We can be a Writer that turns a stream into a data structure that maps offsets to line numbers. Note that a Reader doesn't have this luxury. Not ever. True, there could be Readers that are "the source" where data appears out of thin air, but there are no (sensible) Readers that can be "the sink" as described above.

A secondary effect of "being the sink" is that we don't have to worry about dealing with an "underlying Writer" that we wrap. (As a Reader, we'd have to deal with "both ends" as it were, at least in our scenario.) Also, just like in the case of Hash, any write we deal with cannot actually fail. (Except of course for things like running out of memory, but to a large degree those memory issues are something Go doesn't let us worry about in detail anyway.) This "no failures" property will actually come in handy.

Okay, so those are all nice things that will make our code simpler as long as we hack a Writer and not a Reader. But how the heck are we going to make our Writer "play nice" with the Decoder that after all requires a Reader? Enter a glorious little thing called TeeReader. (No, not TeaReader!) A TeeReader takes two arguments, a Reader r and a Writer w, and returns another Reader t. When we read from t, that request is forwarded to r. But before the data from r gets returned through t, it's also written to w. Problem solved:

lines := &streampos.Writer{}
tee := io.TeeReader(os.Stdin, lines)
dec := xml.NewDecoder(tee)

There's just one small problem with TeeReader: If the write we're doing "on the side" fails, that write error turns into a read error for the client of TeeReader. Of course that client doesn't really know that there's a writer involved anywhere, so things could get confusing. Luckily, as I pointed out above, our Writer for position information never fails, so we cannot possibly generate additional errors for the client.

I could end the post here. But I don't want to sweep under the rug that there's a little cheating going on. Where? Well, you see, TeeReader is not a ByteReader either. So regardless of how nice the code in our Writer is, and regardless of how cute the setup above is, we incur the cost of an extra indirection when NewDecoder decides to wrap the TeeReader. What we're doing is shoving the problem back to the standard library. It's possible that TeeReader will eventually grow a ReadByte method at which point the needless wrapping would cease. However, that's not very likely given what TeeReader is designed to do. But note that this concern arises specifically in connection with encoding/xml. There are probably many applications that do not require methods beyond the Reader interface.

Speaking of other applications. In the Go ecosystem, interfaces such as Reader and Writer are extremely prominent. Lots of people write their code to take advantage of them. The nice thing is that streampos.Writer coupled with TeeReader provides a generic way to handle position information for all applications that use a Reader to grab textual data. Of course not all applications do, and not all applications will be able to take full advantage of it. But if you're writing one that does, and if you want to have position information for error messages, well, it's three lines of code as long as you already track offsets. And you have to track something yourself because after all only your application knows what parts of a stream are interesting.

I very much like that Go encourages this kind of reusability by composing small-ish, independently developed pieces. (Actually, that even confirms a few of the claims I made in my 2003 dissertation. Yay me!) The only "trouble" is that there are already a few places in the standard library where similar position tracking code exists: At the very least in text/scanner and in the Go compiler itself. Whether that code could use my little library I don't know for sure, maybe not. But I guess it should be a goal of the standard library to refactor itself on occasion. We'll see if it does...

One last note: I've been teaching a course on compilers since 2001, and since about 2003 I've told students to use byte offsets as their model of positions. I've always sold this by explaining that offsets can be turned into lines and columns later but we don't have to worry about those details in the basic compiler. Strangely enough I never actually wrote the code to perform that transformation, until now that is. So once I teach the course mostly in Go, I can use my own little library. Neat. :-)