Eagle's Path

Passion and dispassion. Choose two.

Larry Wall

2019-12-11: Astronomy!

Starting in January of 2020, I will be joining the Data Management team (specifically the Science Quality and Reliability Engineering team) for the Large Synoptic Survey Telescope.

There's a much longer description at the above Wikipedia link and also at LSST's own site, but the short version is that the mission of LSST is to survey the entire southern night sky about twice a week for ten years. This in turn will provide vast amounts of data that will be used to do wide-ranging research in astronomy. All of that data requires indexing and processing so that scientists can use it. The team I'm joining is applying current software engineering techniques (containers, Jupyter notebooks, continuous integration, and so on) to that problem.

For me, this is an opportunity to return to the academic, non-profit world that's always been my first love. It's also an opportunity to learn a bunch of new things (astronomy, for the most obvious, and scientific research computing more generally, but also some areas of technology that I've never had enough time to explore). Even better, everything my new team does is free software and is developed on GitHub, which means I'll be returning to a job where free software is at the center of the work instead of an optional side project for which there's rarely time.

Dropbox has been a great employer and I wasn't looking to leave, but this was too good of an opportunity to pass up.

I'm going to be staying in the Bay Area and working remotely, which also means that I'm going to get about six hours a week back from the commute, and have an opportunity to wander around the area and find interesting places from which to work, something that I'm looking forward to.

When I went to Dropbox, I thought that would mean a bit more time for Debian, and was sadly completely incorrect. No promises this time, but I have some reasons to hope that I'll at least be able to get back to the levels of involvement I had at Stanford. Even if the new job, since it's scientific computing, is using CentOS....

2019-12-09: More fun with Jinja2 templates

When last I left this discussion, I was advocating using Python 3 dataclasses to wrap Jinja2 templates. I had another idea and a chance to experiment with it, and I was reasonably happy with the results.

Can the dataclass corresponding to the Jinja2 template be used by the test suite to check that all required parameters for a template are present in the dataclass?

The answer is mostly yes, although unfortunately there are some substantial caveats because Jinja2 doesn't provide all of the tools that one would like to analyze parsed templates.

The basic idea is to ask Jinja2 for all the variables used in a template. It provides a function for doing this:

from jinja2.meta import find_undeclared_variables

def get_template_variables(environment: Environment, name: str) -> Set[str]:
    source = environment.loader.get_source(environment, name)[0]
    ast = environment.parse(source)
    return find_undeclared_variables(ast)

Then, use the dataclass introspection features to see what variables it defines, and compare that to the variables used in the template:

from dataclasses import fields

template_fields = fields(template_class)
expected: Set[str] = set()
for template_field in template_fields:
    if template_field.name == "template":
        template = template_field.default
assert template

wanted = get_template_variables(environment, template)
assert expected == wanted, f"fields for {template}"

This uses the convention (defined in the previous post) that the name of the template is stored in an InitVar field of the dataclass named template as the default value.

Then, this can be turned into a test by putting it in a for loop:

for template_class in BaseTemplate.__subclasses__():

This works and diagnoses template variables that aren't defined by the dataclass, catching problems that otherwise would have to rely on test coverage and allowing more confidence in enabling the Jinja2 StrictUndefined option for all your templates if (like this project) you made the mistake of not starting that way.

In my actual code, I pass in a set of variables into every template in addition to the ones in the dataclass. If you also use that (common) pattern, the easiest way to handle it is to have the test include a set of names of variables that are always set and remove those from the result of get_template_variables before comparing it against the expected list.

There are just a few problems, all of which I think are bugs in Jinja2 (although I've not yet written them up properly):

  1. find_undeclared_variables does not understand macro inclusion (such as from 'macros.html' import dropdown). All the macros are returned as undeclared variables. I tried various approaches to fix this, such as concatenating the files that define macros together with the files that use them before doing the parse, none of which worked. I had to list all the macros and exclude them from the returned template variables to work around this.

  2. The approach above only parses a file in isolation, so it doesn't expand include directives and doesn't support extends. The latter isn't much of a problem for this code base because there is only one level of extends, and the base template only uses default variables. I worked around the lack of support for include by inlining the previously-included templates, which is a bit irritating from a code reuse standpoint.

These are unfortunate problems and make this technique a bit less clean than it otherwise would be, but they're relatively minor. I think it's worth it for being able to test the consistency between dataclass wrappers and underlying templates. Hopefully Jinja2 will provide some better utility functions for doing this sort of testing in the future.

2019-11-16: Review: Rift in the Sky

Review: Rift in the Sky, by Julie E. Czerneda

Series Stratification #3
Publisher DAW
Copyright July 2009
ISBN 1-101-13317-1
Format Kindle
Pages 419

This is the third and final book of the Stratification trilogy and the bridge between it and the Trade Pact trilogy, of which it is a prequel. It's readable without reading the Trade Pact (although that series is overall better), but not very readable without the rest of the Stratification series.

Be warned that the publisher's summary of this book, found on Goodreads, Amazon, and other places you might read about it, is a spoiler for nearly all of the plot. It's not a very surprising spoiler if you've read the Trade Pact books, but still, publishers, don't do this.

Riders of the Storm left off with Aryl with a more stable situation, a new negotiated compromise with the Oud, and more information about the Om'ray, including the mysterious distant clan that no one visits. The origins of her world's balance of powers, and the goals of the outsider presence on her world, are both still opaque, but she's settled near the human Marcus and his archaeological site, so I was optimistic that we were about to learn more.

We don't. What the reader gets instead is more clan politics, worries about the new Om'ray powers that are eagerly adopted by the children of Aryl's clan, and only a few faint additional hints at the nature of the Cloisters. Aryl has more interactions with the Tiktik that finally lead to understanding more of the Agreement from the perspective of the other two races of Cersi (with rather dramatic consequences), and we learn quite a bit more about Om'ray reproduction. But there's next to nothing about how any of this strange and clearly artificial balance was created in the first place, or what the civilization that Marcus is so fascinated by truly is, or how it relates to the Om'ray, or even why the Om'ray clearly had advanced technology at one point in time that they no longer understand.

I hope the information that I wanted from this series is in the following Reunification series, since right now I'm irritated. (Although apparently not irritated enough to stop reading.)

On top of the frustrating lack of answers to nearly every question I had from the first book, this novel has a very odd structure, exacerbated by some strange decisions in how the Kindle version is configured. The last 100 pages are marked in the table of contents as "Teaser chapter" and appear after the dramatis personae. The Kindle reader even pops up the "rate this book" screen before that chapter, exactly as if the novel were over and this material were preview chapters of some subsequent book. I assumed it was a (surprisingly long) excerpt from the start of This Gulf of Time and Stars, the first book of the next trilogy.

It's not, although I admit I bought that book just to check for this review (I was going to buy it eventually anyway). That's a very good thing, since that last hundred pages was the only thing that salvaged this story for me, even though it uses my least favorite fictional trope.

The conclusion of the main story is weirdly abrupt. After lots of Aryl navigating the leadership of her clan, there's a very abrupt political shift (for magical reasons that I never found adequately explained) and the intensity of the pace picks up dramatically. Following some developments in the subplot with Marcus and some dramatic changes in the Agreement, Aryl and her people take drastic steps to resolve the situation, leading to the event that series readers will have been anticipating for the trilogy. But it's sudden and entirely unsatisfying, and if the book had actually ended there, this review would be much more negative.

Instead, the last part of the book takes us back into the science fiction setting of the previous trilogy and a reasonably entertaining cultural conflict. It felt much more like a Trade Pact story, and since my enjoyment of those stories is why I was reading this trilogy in the first place, I'm totally fine with that. Unfortunately, it's also infuriating because Czerneda forecloses on any answers to the historical questions I've been puzzling over for the whole trilogy. The details of how are spoilers, so I won't get into it except to say that I thought it was a rather cheap and unsatisfying device.

If you came this far in this trilogy, you may as well read the ending. Some pieces of it were moving, and I did enjoy the last part of the story. But this whole trilogy focused on the parts of Aryl's life that I found the least interesting and consistently avoided the questions that Marcus was asking and that I really wanted answered. I hope the following Reunification trilogy revisits this world and provides some resolution.

This is the last book of its trilogy, but there is a sequel trilogy to the earlier Trade Pact trilogy in the same universe that starts with This Gulf of Time and Stars, and it promises to tie the two trilogies together.

Rating: 5 out of 10

2019-11-10: Review: Mary Poppins

Review: Mary Poppins, by P.L. Travers

Series Mary Poppins #1
Illustrator Mary Shepard
Publisher Houghton Mifflin Harcourt
Copyright 1934
Printing 2014
ISBN 0-544-57475-3
Format Kindle
Pages 202

I read this book as part of Mary Poppins: 80th Anniversary Collection, which includes the first four books of the series.

I have a long-standing irritation with movies that become so famous that they swallow the book on which they were based, largely because of three examples from my childhood: Bambi (the book is so much better), The Wizard of Oz (the book is... not really better, but the rest of the series certainly is), and Mrs. Frisby and the Rats of NIMH (the book is so much more). That irritation is sometimes misplaced, however. Even Disney has been known to make a mediocre book into a better movie on occasion (The Hundred and One Dalmations). When Mary Poppins came up recently (a movie I adored as a kid), I vaguely remembered having read the book long ago, couldn't remember anything about it, and wondered what side of the fence it would come down on. Since an anniversary collection of the first four books was free with Amazon Prime, it was easy to find out.

Answer: perhaps the series improves in later books, but the movie is totally different in tone from the first book, and much better.

I am surprised that I'd forgotten as much about this book as I had, even though it's been at least thirty years since I've read it, since it is extremely odd. I suspect the highly episodic structure is to blame. Mary Poppins never develops into a proper story; instead, it's a series of vignettes about the titular character, the Banks family, and other people on Cherry-Tree Lane. Some of these stories will be familiar from the movie (Uncle Albert floating up into the air because of his laughter). Some will definitely not be, such as a (very brief) trip around the world via a magical compass, a visit from a star who is Christmas shopping, or a truly bizarre birthday celebration for Mary Poppins in the zoo. Unlike the movie, there is no unifying theme of Mary Poppins fixing the Banks's family problems; quite to the contrary, she seems entirely uninterested and even oblivious to them.

This is not Julie Andrews's kind, gentle, and magically competent nurse. There aren't two separate advertisements for her job; this Mary Poppins appears after Mrs. Banks sent letters to the papers advertising for a position and blithely dismisses her request for references. She is neither kind nor gentle, although by the end of the book one gets the feeling she's brought a sort of gruff stability to the household. Like the movie character, she does take the children on adventures, but they seem almost accidental, a side effect of being around Mary Poppins and thus inadvertantly involved in her business (which she rarely, if ever, explains). It's a more intimidating and fae strangeness, only slightly explained by a chapter that reveals that all children know how to talk to the sun and the wind and the animals but forget when they turn one, except for Mary Poppins. (Ode: Intimations of Immortality has a lot of depressing philosophy to answer for.)

Perhaps the oddest difference from the movie for me is that Travers's Mary Poppins is endlessly vain. She's constantly admiring herself in shop windows or finding just the right clothes, much to the frequent boredom of the children. It's an amusing take on a child's view of adult shopping trips, but the vanity and preening feels weirdly out of place for such a magical character.

There is no change in the Banks household in this book; perhaps there is more material in the later books. (The whole series appears to be eight volumes.) When the wind changes, Mary Poppins disappears as mysteriously as she appears, not even saying goodbye, although she does leave some gifts. By that point, Jane and Michael do seem fond of her, although I'm not entirely sure why. Yes, there are adventures, but outside of them, and even during them, Mary Poppins is short, abrupt, demanding, and fond of sharp and dismissive aphorisms. Gregory Maguire proclaims his preference for the books in the foreword on the grounds that they show more glimmers of mystery and danger, and I can see that if I squint. But I mostly found her unpleasant, dictatorial, irritating, and utterly unwilling to explain anything to curious children.

On this point, I'll dare to disagree with Maguire and prefer the Disney version.

A few of the stories here were fun and entertaining in ways not provided by the movie, particularly "Miss Lark's Andrew" (the successful escape of a neighbor dog from enforced isolation and unnatural pampering) and "Christmas Shopping" (I do hope the Pleiades liked their presents!). But when I get the urge for Mary Poppins, I think I'll turn to the movie with no regrets. This is an interesting curiosity, and perhaps subsequent books add more depth (and make Mary less obnoxious), but I don't think it's worth seeking out.

Followed by Mary Poppins Comes Back.

Rating: 6 out of 10

2019-11-09: Review: Happy Ever After

Review: Happy Ever After, by Paul Dolan

Publisher Penguin
Copyright 2019
ISBN 0-241-28445-7
Format Kindle
Pages 186

Paul Dolan is a professor of behavioral science at the London School of Economics, but grew up a working-class kid in a council estate (UK public housing; the US equivalent is the projects) in London. This intentionally provocative book looks at a list of nine things that we have been taught to believe will make us happy and presents evidence against that assumption. Dolan's goal is to question social narratives of success and to advocate for a different philosophical basis for life decisions: minimizing misery (negative utilitarianism), rather than trying to maximize happiness.

Happy Ever After is an argument against rules and, specifically, against judging people by rules rather than outcomes:

There is nothing inherently good or bad in a social narrative in itself; it can only ever be judged according to the costs and benefits of adhering to it in a given context. I therefore adopt a consequentialist position in contrast to a deontological one. A consequentialist's view on theft would be that it is only ever wrong when it causes more misery than it promotes happiness, whereas a deontologist would be duty bound to argue that theft is always wrong because moral value lies in certain rules of conduct. A deontological perspective typically does not allow for the importance of context. And yet I would contend that it is morally right to steal to feed your hungry child.

This is obviously a drastically simplified explanation of a complex philosophical debate, but those of you who know my political beliefs probably see why I picked up this book.

Before I dive into the details, though, one note about accuracy. One of Dolan's most provocative claims is that marriage does not, on average, make women happy, a claim repeated in an article in The Guardian (now amended to remove the claim). This claim is not supported by the data he references in this book. It was based on a misunderstanding of the coding of results in the American Time Use Survey and has been subsequently retracted by Dolan.

This is a good caution to have in the back of your mind. Dolan, as is typical for a book of this sort, cites a lot of surveys and statistics. At least some of those citations are wrong. Many more are probably unreproducible. This is not a problem unique to Dolan; as the Vox article points out, most books are fact-checked only by the author, and even academic papers have fallen prey to the replication crisis. Hard factual data, particularly about psychology, is hard to come by.

How fatal this is for Dolan's book is a judgment for the reader. Personally, I'm dubious of most psychological studies and read books like this primarily for opportunities of insight into my own life and my own decision-making processes. Whether or not statistics say that marriage makes women happier on average, Dolan's actual point stands: there is no reason to believe that marriage will necessarily make any specific woman happier, and thus pursuit of marriage as a universal life goal is on dubious ground. The key contention of Happy Ever After, in my reading of it, is that we measure ourselves and others against universal social narratives and mete out punishment for falling short, even if there is no reason to believe that social narrative should be universal. That in turn is a cause of unnecessary misery in the world that we could avoid.

Dolan divides his material into three meta-narratives, each with three sub-narratives: reaching (composed of wealthy, successful, and educated), related (married, monogamous, and children), and responsible (altruistic, healthy, and volitional). For each, he provides some data questioning whether following that narrative truly makes us happy, and looks at ways where the narrative itself may be making us unhappy. Each chapter starts with a simple quiz that asks the reader to choose between a life (first for oneself and then for one's friend) that fulfills that narrative but makes them feel miserable frequently and a life that does not fulfill that narrative but in which they rarely feel miserable. At the end of each section, Dolan shows the results of that survey, all of which show at least some support (surprising to me) for choosing the narrative despite the cost of being miserable.

Some of these chapters I found unsurprising. I'm unmarried and don't intend to have children, so the chapters on marriage and children struck me as relatively obvious. Similarly, the lack of positive happiness benefit of wealth beyond a rather modest level is well-known, although I thought Dolan failed to engage sufficiently with the risk of misery from being poor. A significant motivation for pursuing modest wealth for many people is to acquire a form of self-insurance against financial disasters, particularly in the US with our appalling lack of a safety net.

I had the most mental arguments with Dolan over education. Apparently (and not very surprisingly) this is the social narrative that I buy into the most strongly. But Dolan makes good points about how pushing a working-class kid to go to a middle-class or upper-class university can sever them from their friendship ties and emotional support network and force a really miserable adjustment, and it's not clear that the concrete benefits of education in their life are worth that. This would be even clearer if we hadn't started using college degree attainment as a credentialing system for many jobs that are not reliant on specialized education only attainable in college. (I'm looking at nearly the entire field of computing, for example.) Dolan goes farther than I would in arguing that no college education should be state-subsidized because it's inherently unfair for working-class people to be taxed to pay for middle-class educational structures. Still, I keep thinking back to this chapter during US political discussions about how important it is that we create some economic path for every US child to attend college. Is that really the correct public education policy? (See also Hofstadter's point in Anti-Intellectualism in American Life that some of the US obsession with college education is because, by comparison to Germany, our high-school and middle-school education is slow, relaxed, unchallenging, and insufficient.)

Altruistic and volitional may require a bit of additional explanation. Dolan's point with altruism is that we value a social narrative of giving for its own sake, without ego or reward. (I personally would trace this to Christianity; this was the interpretation of Matthew 6:6 that I was taught.) He argues that letting people show off their good deeds encourages more good deeds and helps others increases personal happiness. People who are more self-oriented in their motivations for volunteering stick with volunteer projects for longer. I thought of free software here, where self-interested reasons for volunteering are commonplace and accepted (scratching your own itch) rather than socially shunned, and considered part of the healthy texture of the community.

The chapter on volition recaps some of the evidence (which I've also seen in other books) that less of our life and our decisions stem from individual choice than we would like to think, and that some of our perception of free will is probably a cognitive illusion. Dolan isn't too interested in trying to undermine the reader's own sense of free will, but does want to undermine our belief in the free will of other people. His target here is the abiding political belief that other people get the life outcomes they deserve, and that poor people are poor because they're lazy or make bad choices. If we let go of the social narrative of volition and instead judge interventions solely by their results, we have fewer excuses to not collectively tackle problems and fewer justifications for negatively judging other people for their own misery.

I'm not sure I recommend this whole book. It's delightfully contrarian, but somewhat slim on new ideas (particularly if you've read broadly about happiness and life satisfaction) and heavy on studies that you should be somewhat dubious about. I'm still thinking about the chapter on education, though. How much you get out of it may depend on how many of Dolan's narratives you agree with going into the book.

Also, although I didn't discuss it in detail, mad props to Dolan for taking on the assumption that striving to be healthy is a life goal that should override happiness. We need a lot more questioning of that specific narrative.

Rating: 7 out of 10

2019-11-09: Python dataclasses and typing

I'm going to preach the wonders of Python dataclasses, but for reasons of interested to those who have already gone down the delightful rabbit-hole of typed Python. So let me start with a quick plug for mypy if you haven't heard about it.

(Warning: this is going to be a bit long.)

Type Checking

mypy is a static type-checker for Python. In its simplest form, instead of writing:

def hello(name):
    return f"Hello, {name}"

you write:

def hello(name: str) -> str:
    return f"Hello, {name}"

The type annotations are ignored at runtime, but the mypy command makes use of them to do static typing. So, for instance:

$ cat > t.py
def hello(name: str) -> str:
    return f"Hello {name}"
$ mypy t.py
t.py:3: error: Argument 1 to "hello" has incompatible type "int"; expected "str"

If you're not already using this with your Python code, I cannot recommend it highly enough. It's somewhat tedious to add type annotations to existing code, particularly at first when you have to look up how mypy represents some of the more complicated constructs like decorators, but once you do, mypy starts finding bugs in your code like magic. And it's designed to work incrementally and tolerate untyped code, so you can start slow, and the more annotations you add, the more bugs it finds. mypy is much faster than a comprehensive test suite, so even if you would have found the bug in testing, you can iterate faster on changes. It can even be told which variables may be None and then warn you if you use them without checking for None in a context where None isn't allowed.

But mypy can only help with code that's typed, so once you get the religion, the next goal is to push typing ever deeper into complicated corners of your code.

Typed Data Structures

Python code often defaults to throwing any random collection of data into a dict. For a simple example, suppose you have a paginated list of strings (a list, an offset from the start of the list, a limit of the number of strings you want to see, and a total number of strings in the underlying data). In a lot of Python code, you'll see something like:

strings = {
    "data": ["foo", "bar", "baz"],
    "offset": 5,
    "limit": 3,
    "total": 10,

mypy is good, but it's not magical. It has no way to keep track of the fact that strings["data"] is a list of strings, but strings["offset"] is a int. Instead, it decides the type of each value is the superclass of the types it sees in the initializer (in this case, object, which provides almost no type checking).

There are two traditional solutions: an object, and a NamedTuple (the typing-enhanced version of collections.namedtuple). An object is tedious:

class Strings:
    def __init__(
        self, data: List[str], offset: int, limit: int, total: int
    ) -> None:
        self.data = data
        self.offset = offset
        self.limit = limit
        self.total = total

This provides perfect typing, but who wants to write all that. A NamedTuple is a little better:

Strings = NamedTuple(
    [("data", List[str]), ("offset", int), ("limit", int), ("total", int)],

but still kind of tedious and has other quirks, such as the fact that your object can now be used as a tuple, which can introduce some surprising bugs.

Enter dataclasses, which are new in Python 3.7 (although inspired by attrs, which have been around for some time). The equivalent is:

class Strings:
    data: List[str]
    offset: int
    limit: int
    total: int

So much nicer, and the same correct typing. And unlike NamedTuple, dataclasses support default values, expansion via inheritance, and are full classes so you can attach short methods and do other neat tricks. You can also optionally mark them as frozen, which provides the NamedTuple behavior of making them immutable after creation.

A Detailed Example

Using dataclasses for those random accumulations of data is already great, but today I found a way to use them for a trickier typing problem.

I work on a medium-sized (about 75 routes) Tornado web UI using Jinja2 and WTForms for templating. Returning a page to the user's browser involves lots of code that looks something like this:

self.render("template.html", service=service, owner=owner)

Under the hood, this loads that template, builds a dictionary of template variables, and tells Jinja2 to render the template with those variables. The problem is the typing: the render method has no idea what sort of data you want to pass to a given template, so it uses the dreaded **kargs: Any, so you can pass anything you want. And mypy can't look inside Jinja2 template code.

Forget to pass in owner? Exception or silent failure during template rendering depending on your Jinja2 options. Pass in the name of a service when the template was expecting a rich object? Exception or silent failure. Typo in the name of the parameter? Exception or silent failure. Better hope your test suite is thorough.

What I did today was wrap each template in a dataclass:

class Template(BaseTemplate):
    service: str
    owner: str
    template: InitVar[str] = "template.html"

Now, the code to render it looks like:

template = Template(service, owner)

and now I have type-checking of all of the template arguments and only need to ensure the dataclass definition matches the needs of the template implementation.

The magic happens in the base template class:

class BaseTemplate:
    def render(self, handler: RequestHandler) -> str:
        template_name = getattr(self, "template")
        template = handler.environment.get_template(template_name)
        namespace = handler.get_template_namespace()
        for field in fields(self):
            namespace[field.name] = getattr(self, field.name)
        return template.render(namespace)

(My actual code is a bit different and more complicated since I move some other template setup to the render() method.)

There's some magic here to work around dataclass limitations that warrants some explanation.

I pass the Tornado handler class into the template render() method so that I have access to the template environment and the (overridden) Tornado get_template_namespace() call to get default variable settings. Passing them into the dataclass constructor would make the code less clean and is harder to implement, mostly due to limitations on attributes with default values, mentioned below.

The name of the template file should be a property of the template definition rather than something the caller needs to know, but that means it has to be given last since dataclasses require that all attributes without default values come before ones with default values. That in turn also means that the template attribute cannot be defined in BaseTemplate, even without a default value, because if a child class sets a default value, @dataclass then objects. Hence the getattr to hide from mypy the fact that I'm breaking type rules and assuming all child classes are well-behaved.

template in the child classes is marked as InitVar so that it won't be included in the fields of the dataclass and thus won't be passed down to Jinja2.

Finally, it would be nice to be able to use dataclasses.asdict() to turn the object into a dictionary for passing into Jinja2, but unfortunately asdict tries to do a deep copy of all template attributes, which causes all sorts of problems. I want to pass functions and WTForms form objects into Jinja2, which resulted in asdict throwing all sorts of obscure exceptions. Hence the two lines that walk through the fields and add a shallow copy of each field to the template namespace.

I've only converted four templates so far (this code base is littered with half-finished transitions to better ways of doing things that I try to make forward progress on when I can), but I'm already so much happier. All sorts of obscure template problems will now be caught at mypy even before needing to run the test suite.

2019-11-04: Review: Cold Steel

Review: Cold Steel, by Kate Elliott

Series Spiritwalker #3
Publisher Orbit
Copyright June 2013
ISBN 0-316-21515-5
Format Kindle
Pages 597

Cold Steel is the third book in the closely-linked Spiritwalker trilogy. This is one long, sprawling story rather than separable books. If you want to read it, start with Cold Magic, and beware of spoilers for Cold Fire in even cursory descriptions of Cold Steel.

Cold Steel opens with Cat in the hot Caribbean, where most of Cold Fire was set, but does not stay there. This is a proper end to the trilogy and wraps up all the loose ends: the peril Andevai was left in after the semi-cliffhanger ending of Cold Fire, the conflict over governance in Europe, the power of the cold mage Houses, the politics of the radicals, the conflict between cold and fire magic, Bee's entanglements in the Caribbean, Camjiata's war of conquest, and of course the Wild Hunt. There's a lot of plot to finish.

Somewhat unfortunately, what Cold Steel also contains in large quantity is Andevai being an idiot, Cat and Andevai failing to communicate for new (but dumb) reasons, and one of the characters developing into a full-blown arch-villain.

This is a series with a fascinating premise (a very complex alternate history with dragons, magic, humanoid dinosaurs, and a very odd sort of elven court) that I wanted to be a political drama but that the author wrote primarily as a romance. This is not entirely the author's fault; it's a serviceable romance if you enjoy two vastly stubborn people butting heads, falling in love, discovering that being in love doesn't prevent them from butting heads, and finally reaching some hard-fought compromises. But I thought the romance was the least interesting (and least original) thing that was happening in this series and wanted to get it over with so that the series could move on to the more interesting bits, and that opinion was not shared by the author.

The part that caught my attention, as noted in my review of Cold Fire, is the political cause of radicalism and egalitarianism. The history varies wildly from ours, but the politics are clearly late French Revolution writ large (without the church, which is interesting): princes and cold mages colluding to maintain an aristocratic, almost feudal order, resented by laborers and political radicals who are trying to form new governments of equality and to undermine historical privilege and systems of indenture. Into that mix, Elliott drops Camjiata, a brilliantly ambiguous figure who is this world's Napoleon. He uses the radicals, speaks on their terms, and claims to fight for equality, but he personally wants to conquer Europe and bring back the glories of the Roman Empire. He's a strategic genius, one of the smartest people in the book (that's a difficult characterization to write well and Elliott does a good job), but his agenda isn't that of the protagonists or, really, anyone else. Elliott gives the impression of Camjiata as something that happens to Europe more than a force that comes out of the politics everyone else cares about, which I found intriguing throughout the series.

Unfortunately, despite radicalism's central role in the plot and despite Cat's sister Bee finding an unexpected but believable place for herself as a leader, the politics don't go anywhere satisfying. There is little resolution for Europe here, just some personal impact for Andevai and Cat and a parallel mythical plot line with the Wild Hunt that I thought was a bit too obvious (although the resolution of it is satisfying). A series that wrestled with the political complexities of defining radicalism as a constructive form of government instead of an opposition resistance force (the shoal that, to simplify greatly, the real French Revolution ran aground on) would have been messy and challenging to write, but that was the book I wanted to read. Cold Steel alas turns somewhat away from that to personalize the problems and solve them primarily in the realms of magic and romance.

The other difficulty with this final book is that it's structurally a mess. There are too many loose ends to weave together, which results in some odd pacing and story structure. The book starts as a continuation of Cold Fire, involving Caribbean politics and magic, and builds up to a climax with the fairy world a third of the way into the book. Then comes a quiet interlude with Cat and Andevai that stumbles into a pacing gap where Cat is unhappy but not communicating, Andevai is ignoring her, no forward progress is made on the major plots of the novel, and there is a lot of frustrating talking. Pacing for the rest of the book comes in fits and starts, building up to a weirdly unsatisfying war and turning the broader politics of the series into a too-simplistic arc of personal animus, mustache-twirling evil, and magical penis-measuring. Even the ending, although eventually satisfying, provides another excuse for Cat to not talk to people about things that are important.

I'm probably making this sound worse than it is. I am glad I read the whole series. It's hugely ambitious and succeeds in being something different than the typical fantasy trilogy. The world background is one of the most creative I've seen, and if I found the politics unsatisfying in the end, that's only because Elliott attempted something far more ambitious and radical than most fantasy will go near. I think the first book was the strongest and the series sputtered to a conclusion, but it is a proper and satisfying conclusion and both Cat and Andevai, by the end of the series, are characters I enjoy spending time with.

Cold Steel by itself is a bit of a mess, but I still recommend the series as a whole, as long as you're in the mood for something long and complicated and not completely successful.

Rating: 6 out of 10

2019-10-31: Review: Sweep of the Blade

Review: Sweep of the Blade, by Ilona Andrews

Series Innkeeper Chronicles #4
Publisher NYLA
Copyright 2019
ISBN 1-64197-104-5
Format Kindle
Pages 310

Sweep of the Blade is the fourth book of the Innkeeper Chronicles and the first with a main character other than Dina. The identity of that main character and the nature of the plot are spoilers for parts of One Fell Sweep, the previous book in the series, so don't read further in this review if you would prefer to avoid those.

Maud was the wife of a vampire (which, as in previous books in this series, are more like religious Klingons than the typical fantasy creature). She learned their forms of combat, their languages, and their customs. Her behavior and willingness to fit in were unquestionable. For her trouble, she and her daughter were treated like curiosities and trophies, pawns in her husband's ambition. His schemes left her exiled and fighting for her and her daughter's life on a lawless desert planet. By the time she made her escape (in One Fell Sweep), she was utterly done with vampires. But then she met Arland.

Arland, being the Arland that readers of this series have come to know, is determined to win her heart and her hand. Her heart may be a lost cause. Her hand is another matter. Maud had promised herself to never return to the Holy Anocracy to be rejected a second time, or to return Helen to a life of being bullied as a mongrel child. Arland nonetheless convinces her to meet his formidable family, determined to convince her to marry him.

Maud's requirement is that they take her and Helen on their own terms, or not at all.

This is not the sort of book you read for the plot twists and surprise endings. If you've read much at all, you're going to know the ending within the first few chapters, and are likely to be able to predict most of the story beats. If that's not what you're in the mood for, if you want something less expected and more uncertain, save this for another time. But if you're in the mood for a kick-ass heroine facing down assholes and winning the respect of all the people who matter while protecting her adorably feral child (and it takes a lot for me to like child characters), this is great.

The reader knows, going into this book, that Maud is worthy of Arland, and that Arland can probably manage to be worthy of Maud. The authors know that the reader knows and don't call that into question. The only question is exactly how they're going to demonstrate that, how long it will take the other vampires to figure it out, and what Maud will do to her enemies along the way. The setting is a complex inter-species negotiation mixed with vampire politics and a somewhat strained conspiracy, which gives Maud plenty of opportunity to use her innkeeper background and the vampires lots of opportunities to utterly fail to understand people who are not vampires. The fun is in Maud letting people underestimate her until just the right moment, and then proving she's the smartest and most capable person in the book.

This series as a whole is great fun, but I think this is my favorite book to date. That's not because of complex plots or detailed world-building (the setting is fun but composed of not horribly original components), but because it's the kind of series that unabashedly delivers on a guaranteed reading experience. I wanted to read a book where I knew the good guys were going to win and all the important people were going to learn to like the protagonist as much as I did, where I could just relax into the story and enjoy myself and grin at Maud cutting down (metaphorically or literally) people who thoroughly deserved it.

Also, Helen is an absolute delight. She's fearless, sincere, and childish in turn, and in just the right amount. Her relationship with Maud has just the right mix of protectiveness and trust. A whole book of the two of them together is a wonderful treat.

If you too are in the mood for that sort of uncomplicated story, this (and the whole series) delivers exactly what it promises on the tin. Highly recommended.

This entry doesn't advance the over-arching series plot all that much, but the teaser at the end of the story implies the next book will. As of this writing, it doesn't yet have a title.

Rating: 8 out of 10

2019-10-26: remctl 3.16

remctl is a simple RPC mechanism (generally based on GSS-API) with rich ACLs and native support in multiple languages.

The primary change in this release is support for Python 3. This has only been tested with Python 2.7 and Python 3.7, but should work with any version of Python 3 later than Python 3.1. This release also cleans up some obsolete syntax in the Python code and deprecates passing in a command as anything other than an iterable of str or bytes.

This release also adds a -t flag to the remctl command-line tool to specify the network timeout, fixes a NULL pointer dereference after memory allocation failure in the client library, adds GCC attributes to the client library functions, and fixes a few issues with the build system.

You can get the latest release from the remctl distribution page.

2019-09-30: Review: This Is How You Lose the Time War

Review: This Is How You Lose the Time War, by Amal El-Mohtar & Max Gladstone

Publisher Saga
Copyright 2019
ISBN 1-5344-3101-2
Format Kindle
Pages 200

Red is the most effective operative of the Agency. She darts through time's threads, finds threats to the future, eliminates them, and delights in the work. She rarely encounters the operatives of her enemy directly; they prefer painstaking work in the shadows. But there is one opponent who has a different style. Audacious. Risky.

In the midst of a dead battlefield, Red finds a letter.

Blue is Garden's operative, moving from mission to mission, exerting exactly the right pressure or force at a critical moment to shift the strands of the future. She decided to leave a letter taunting her adversary, but also expressing gratitude at the challenge, the requirement that she give the war her full attention, the relief from boredom. She wasn't sure whether to expect a reply, but she received one.

This Is How You Lose the Time War is an epistolary novel, told in short action sequences by Red or Blue followed by the inevitable discovered letter. At first, they taunt each other and delight in their victories while expressing admiration of their opponent. Blue has the smoother and more comfortable writing style. Red has to research the form of letters and writes like a conversation, sharp and informal. Both threaten and tease the other with the consequences if their superiors discover this exchange.

In word play, cultural references, sincerely-shared preferences, open curiosity, and audacious puns, the letters turn into something more than a taunting game.

The time war is a long-standing SF trope. This one reminds me the most of Fritz Leiber's The Big Time: a two-sided war between far-future civilizations, neither of which are clearly superior in either capabilities or morality. Unlike Leiber's Spiders and Snakes, though, El-Mohtar and Gladstone's Agency and Garden have some solid world-building behind them. Red's Agency is technological, cybernetic, and run by what feels like machine intelligence. Blue's Garden is the biological flip-side, a timeline of crafted life culminating in stars with eyes and a living universe, focus on growth and poison, absorbing and reshaping. To the reader, they alternate between incomprehensible and awful, although Red and Blue are comfortable with their sides at the start. Don't expect detailed or believable descriptions of the technology of either side; this is well into "indistinguishable from magic" territory throughout.

Despite its nature as a time travel story, the plot structure of this story is straightforward and somewhat predictable. You're unlikely to be surprised by the outcome; the enjoyment is in how the story gets there. The relationship didn't quite ring true to me, mostly because it develops so quickly, although some of that has to be forgiven for the format. (I have some experience with epistolary relationships; they're much more rambling and involve far, far more words than this one does.) But the letters themselves are playful, delightful, and occasionally moving, and the resolution, although expected, delivers on the emotional hooks the story was setting up.

I wasn't blown away by this, partly I think because it's too tight, focused, and stylized. Red and Blue are the only true characters in the story and the only people who feel real, which undermines the world-building and means the story can't sprawl into its surroundings or let the reader imagine other ways of living in this world. At 200 pages, it's more of a novella than a novel, and it's structured with the single-minded thrust of short fiction. The dynamic between the two characters is well-done, but there is a limit to how much characterization one can do with only a single other character to interact with. Since Red and Blue can define themselves only in relation to each other, they felt two-dimensional and I was unable to fully embrace either of them as a character.

That said, I read the whole story in an afternoon and did not regret it. I have a weakness for epistolary stories that this satisfied nicely. It hit, at least for me, the sweet spot of recognizing most of the cultural references while being surprised I recognized them, which was oddly satisfying. And the whole book is worth it for the growing tendency they both have for seeing and writing about each other's colors in everything.

I think this is more of an afternoon's entertainment than something you'll remember for a long time, but if you like time travel stories or characters writing letters to each other, recommended.

Rating: 7 out of 10

2019-09-29: Haul post

It's been quite a while since I made one of these, and I... may have been supporting a lot of authors financially despite my huge to-read pile.

Louisa Alcott — Little Women (mainstream)
Louisa Alcott — Good Wives (mainstream)
Louisa Alcott — Little Men (mainstream)
Louisa Alcott — Jo's Boys (mainstream)
Ilona Andrews — Sweep of the Blade (sff)
Rachel Elise Barkow — Prisoners of Politics (nonfiction)
Becky Chambers — To Be Taught, If Fortunte (sff)
James Clear — Atomic Habits (nonfiction)
Michael Collins — Carrying the Fire (nonfiction)
Aliette de Bodard — In the Vanisher's Palace (sff)
Paul Dolan — Happy Ever After (nonfiction)
Benjamin Dreyer — Dreyer's English (nonfiction)
Amal El-Mohtar & Max Gladstone — This is How You Lose the Time War (sff)
Max Gladstone — Empress of Forever (sff)
Emily Guendelsberger — On the Clock (nonfiction)
Alix E. Harrow — The Ten Thousand Doors of January (sff)
Linda Hirshman — Reckoning (nonfiction)
Mike Isaac — Super Pumped (nonfiction)
E.K. Johnston — The Afterward (sff)
Jodi Kantor — She Said (nonfiction)
Guy Gavriel Kay — A Brightness Long Ago (sff)
Sarah Kendzior — The View from Flyover Country (nonfiction)
T. Kingfisher — Minor Mage (sff)
Karoliina Korhonen — Finnish Nightmares 2 (graphic novel)
Karoliina Korhonen — Matti in the Wallet (graphic novel)
Mary Robinette Kowal — The Fated Sky (sff)
Yoon Ha Lee — Hexarchate Stories (sff)
Mark Manson — The Subtle Art of Not Giving a F*ck (nonfiction)
Laurie J. Marks — Air Logic (sff)
Randall Munroe — How To (graphic novel)
Terry Pratchett — Lords and Ladies (sff)
Karl Schroeder — Stealing Worlds (sff)
Ryk E. Spoor — Challenges of the Deeps (sff)
J. Michael Straczynski — Becoming Superman (nonfiction)
P.L. Travers — Mary Poppins (children's)
P.L. Travers — Mary Poppins Comes Back (children's)
P.L. Travers — Mary Poppins Opens the Door (children's)
P.L. Travers — Mary Poppins in the Park (children's)
Jo Walton — Lent (sff)

Phew. I'm coming up on a vacation during which I'll have tons of time to read, but I still am buying books rather faster than reading them. Oh well, money into the pockets of authors, which is always a good thing.

There's a whole mess of non-fiction in there, since I've been in a mood of queuing up a lot of interesting-looking non-fiction to read. (I've resisted grabbing even more.) You might be able to tell that I've never made the transition to getting samples and only buying the book if the sample looks good. Or, for that matter, stopping reading a book if I'm not liking it.

There are also several new releases in there, which will probably be vacation reading, and a couple of books that I've already read but haven't written reviews of yet.

2019-09-01: rra-c-util 8.0

This is a roll-up of a lot of changes to my utility package for C (and increasingly for Perl). It's been more than a year since the last release, so it's long-overdue.

Most of the changes in this release are to the Perl test libraries and accompanying tests. Test::RRA now must be imported before Test::More so that it can handle the absence of Test::More (such as on Red Hat systems with perl but not perl-core installed). The is_file_contents function in Test::RRA now handles Windows and other systems without a diff program. And there are more minor improvements to the various tests written in Perl.

The Autoconf probe RRA_LIB_KRB5_OPTIONAL now correctly handles the case where Kerberos libraries are not available but libcom_err is, rather than incorrectly believing that Kerberos libraries were present.

As of this release, rra-c-util now tests the Perl test programs that it includes, which requires it to build and test a dummy Perl module. This means the build system now requires Perl 5.6.2 and the Module::Build module.

You can get the latest version from the rra-c-util distribution page.

2019-08-31: C TAP Harness 4.5

Peter Paris requested that C TAP Harness support being built as C++ code. I've not been a big fan of doing this with pure C code since I find some of the requirements of C++ mildly irritating, but Peter's initial patch also fixed one type error in a malloc uncovered because of one of C++'s rules requiring the return of malloc be cast. It turned out to be a mostly harmless error since the code was allocating a larger struct than it needed to, but it's still evidence that there's some potential here for catching bugs.

That said, adding an explicit cast to every malloc isn't likely to catch bugs. That's just having to repeat oneself in every allocation, and you're nearly as likely to repeat yourself incorrectly.

However, if one is willing to use a macro instead of malloc directly, this is fixable, and I'm willing to do that since I was already using a macro for allocation to do error handling. So I've modified the code to pass in the type of object to allocate instead of the size, and then used a macro to add the return cast. This makes for somewhat cleaner code and also makes it possible to build the code as pure C++. I also added some functions to the TAP generator library, bcalloc_type and breallocarray_type, that take the same approach. (I didn't remove the old functions, to maintain backward compatibility.)

I'm reasonably happy with the results, although it's a bit of a hassle and I'm not sure if I'm going to go to the trouble in all of my other C packages. But I'm at least considering it. (Of course, I'm also considering rewriting them all in Rust, and considering my profound lack of time to do either of these things.)

You can get the latest release from the C TAP Harness distribution page.

2019-08-26: Review: Space Opera

Review: Space Opera, by Catherynne M. Valente

Publisher Saga
Copyright 2018
ISBN 1-4814-9751-0
Format Kindle
Pages 304

Life is not, as humans had come to think, rare. The universe is packed with it, bursting at the seams. The answer to the Fermi paradox is not that life on Earth is a flukish chance. It's that, until recently, everyone else was distracted by total galactic war.

Thankfully by the time the other intelligent inhabitants of the galaxy stumble across Earth the Sentience Wars are over. They have found a workable solution to the everlasting problem of who counts as people and who counts as meat, who is sufficiently sentient and self-aware to be allowed to join the galactic community and who needs to be quietly annihilated and never spoken of again. That solution is the Metagalactic Grand Prix, a musical extravaganza that is also the highest-rated entertainment in the galaxy. All the newly-discovered species has to do is not finish dead last.

An overwhelmingly adorable giant space flamingo appears simultaneously to every person on Earth to explain this, and also to reassure everyone that they don't need to agonize over which musical act to send to save their species. As their sponsors and the last new species to survive the Grand Prix, the Esca have made a list of Earth bands they think would be suitable. Sadly though, due to some misunderstandings about the tragically short lifespans of humans, every entry on the list is dead but one: Decibel Jones and the Absolute Zeroes. Or their surviving two members, at least.

Space Opera is unapologetically and explicitly The Hitchhiker's Guide to the Galaxy meets Eurovision. Decibel Jones and his bandmate Oort are the Arthur Dent of this story, whisked away in an impossible spaceship to an alien music festival where they're expected to sing for the survival of their planet, minus one band member and well past their prime. When they were at the height of their career, they were the sort of sequin-covered glam rock act that would fit right in to a Eurovision contest. Decibel Jones still wants to be that person; Oort, on the other hand, has a wife and kids and has cashed in the glitterpunk life for stability. Neither of them have any idea what to sing, assuming they even survive to the final round; sabotage is allowed in the rules (it's great for ratings).

I love the idea of Eurovision, one that it shares with the Olympics but delivers with less seriousness and therefore possibly more effectiveness. One way to avoid war is to build shared cultural ties through friendly competition, to laugh with each other and applaud each other, and to make a glorious show out of it. It's a great hook for a book. But this book has serious problems.

The first is that emulating The Hitchhiker's Guide to the Galaxy rarely ends well. Many people have tried, and I don't know of anyone who has succeeded. It sits near the top of many people's lists of the best humorous SF not because it's a foundational model for other people's work, but because Douglas Adams had a singular voice that is almost impossible to reproduce.

To be fair, Valente doesn't try that hard. She goes a different direction: she tries to stuff into the text of the book the written equivalent of the over-the-top, glitter-covered, hilariously excessive stage shows of unapologetic pop rock spectacle. The result... well, it's like an overstuffed couch upholstered in fuchsia and spangles, onto which have plopped the four members of a vaguely-remembered boy band attired in the most eye-wrenching shade of violet satin and sulking petulantly because you have failed to provide usable cans of silly string due to the unfortunate antics of your pet cat, Eunice (it's a long story involving an ex and a book collection), in an ocean-reef aquarium that was a birthday gift from your sister, thus provoking a frustrated glare across an Escher knot of brilliant yellow and now-empty hollow-sounding cans of propellant, when Joe, the cute blonde one who was always your favorite, asks you why your couch and its impossibly green rug is sitting in the middle of Grand Central Station, and you have to admit that you do not remember because the beginning of the sentence slipped into a simile singularity so long ago.

Valente always loves her descriptions and metaphors, but in Space Opera she takes this to a new level, one covered in garish, cheap plastic. Also, if you can get through the Esca's explanation of what's going on without wanting to strangle their entire civilization, you have a higher tolerance for weaponized cutesy condescension than I do.

That leads me back to Hitchhiker's Guide and the difficulties of humor based on bizarre aliens and ludicrous technology: it's not funny or effective unless someone is taking it seriously.

Valente includes, in an early chapter, the rules of the Metagalactic Grand Prix. Here's the first one:

The Grand Prix shall occur once per Standard Alumizar Year, which is hereby defined by how long it takes Aluno Secundus to drag its business around its morbidly obese star, get tired, have a nap, wake up cranky, yell at everyone for existing, turn around, go back around the other way, get lost, start crying, feel sorry for itself and give up on the whole business, and finally try to finish the rest of its orbit all in one go the night before it's due, which is to say, far longer than a year by almost anyone else's annoyed wristwatch.

This is, in isolation, perhaps moderately amusing, but it's the formal text of the rules of the foundational event of galactic politics. Eurovision does not take itself that seriously, but it does have rules, which you can read, and they don't sound like that, because this isn't how bureaucracies work. Even bureaucracies that put on ridiculous stage shows. This shouldn't have been the actual rules. It should have been the Hitchhiker's Guide entry for the rules, but this book doesn't seem to know the difference.

One of the things that makes Hitchhiker's Guide work is that much of what happens is impossible for Arthur Dent or the reader to take seriously, but to everyone else in the book it's just normal. The humor lies in the contrast.

In Space Opera, no one takes anything seriously, even when they should. The rules are a joke, the Esca think the whole thing is a lark, the representatives of galactic powers are annoying contestants on a cut-rate reality show, and the relentless drumbeat of more outrageous descriptions never stops. Even the angst is covered in glitter. Without that contrast, without the pause for Arthur to suddenly realize what it means for the planet to be destroyed, without Ford Prefect dryly explaining things in a way that almost makes sense, the attempted humor just piles on itself until it collapses under its own confusing weight. Valente has no characters capable of creating enough emotional space to breathe. Decibel Jones only does introspection by moping, Oort is single-note grumbling, and each alien species is more wildly fantastic than the last.

This book works best when Valente puts the plot aside and tells the stories of the previous Grands Prix. By that point in the book, I was somewhat acclimated to the over-enthusiastic descriptions and was able to read past them to appreciate some entertainingly creative alien designs. Those sections of the book felt like a group of friends read a dozen books on designing alien species, dropped acid, and then tried to write a Traveler supplement. A book with those sections and some better characters and less strained writing could have been a lot of fun.

Unfortunately, there is a plot, if a paper-thin one, and it involves tedious and unlikable characters. There were three people I truly liked in this book: Decibel's Nani (I'm going to remember Mr. Elmer of the Fudd) who appears only in quotes, Oort's cat, and Mira. Valente, beneath the overblown writing, does some lovely characterization of the band as a trio, but Mira is the anchor and the only character of the three who is interesting in her own right. If this book had been about her... well, there are still a lot of problems, but I would have enjoyed it more. Sadly, she appears mostly around the edges of other people's manic despair.

That brings me to a final complaint. The core of this book is musical performance, which means that Valente has set herself the challenging task of describing music and performance sufficiently well to give the reader some vague hint of what's good, what isn't, and why. This does not work even a little bit. Most of the alien music is described in terms of hyperspecific genres that the characters are assumed to have heard of and haven't, which was a nice bit of parody of musical writing but which doesn't do much to create a mental soundtrack. The rest is nonspecific superlatives. Even when a performance is successful, I had no idea why, or what would make the audience like one performance and not another. This would have been the one useful purpose of all that overwrought description.

Clearly some people liked this book well enough to nominate it for awards. Humor is unpredictable; I'm sure there are readers who thought Space Opera was hilarious. But I wanted to salvage about 10% of this book, three of the supporting characters, and a couple of the alien ideas, and transport them into a better book far away from the tedious deluge of words.

I am now inspired to re-read The Hitchhiker's Guide to the Galaxy, though, so there is that.

Rating: 3 out of 10

2019-08-25: Review: A Memory Called Empire

Review: A Memory Called Empire, by Arkady Martine

Series Teixcalaan #1
Publisher Tor
Copyright March 2019
ISBN 1-250-18645-5
Format Kindle
Pages 462

Mahit Dzmare grew up dreaming of Teixcalaan. She learned its language, read its stories, and even ventured some of her own poetry, in love with the partial and censored glimpses of its culture that were visible outside of the empire. From her home in Lsel Station, an independent mining station, Teixcalaan was a vast, lurking weight of history, drama, and military force. She dreamed of going there in person. She did not expect to be rushed to Teixcalaan as the new ambassador from Lsel Station, bearing a woefully out-of-date imago that she's barely begun to integrate, with no word from the previous ambassador and no indication of why Teixcalaan has suddenly demanded a replacement.

Lsel is small, precarious, and tightly managed, a station without a planet and with only the resources that it can maintain and mine for itself, but it does have a valuable secret. It cannot afford to lose vital skills to accident or age, and therefore has mastered the technology of recording people's personalities, memories, and skills using a device called an imago. The imago can then be implanted in the brain of another, giving them at first a companion in the back of their mind and, with time, a unification that grants them inherited skills and memory. Valuable expertise in piloting, mining, and every other field of importance need not be lost to death, but can be preserved through carefully tended imago lines and passed on to others who test as compatible.

Mahit has the imago of the previous ambassador to Teixcalaan, but it's a copy from five years after his appointment, and he was the first of his line. Yskandr Aghavn served another fifteen years before the loss of contact and Teixcalaan's emergency summons, never returning home to deposit another copy. Worse, the implantation had to be rushed due to Teixcalaan's demand. Rather than the normal six months of careful integration under active psychiatric supervision, Mahit has had only a month with her new imago, spent on a Teixcalaan ship without any Lsel support.

With only that assistance from home, Mahit's job is to navigate the complex bureaucracy and rich culture of an all-consuming interstellar empire to prevent the ruthlessly expansionist Teixcalaanli from deciding to absorb Lsel Station like they have so many other stations, planets, and cultures before them. Oh, and determine what happened to her predecessor, while keeping the imagos secret.

I love when my on-line circles light up with delight about a new novel, and it turns out to be just as good as everyone said it was.

A Memory Called Empire is a fascinating, twisty, complex political drama set primarily in the City at the heart of an empire, a city filled with people, computer-controlled services, factions, manuevering, frighteningly unified city guards, automated defense mechanisms, unexpected allies, and untrustworthy offers. Martine weaves a culture that feels down to its bones like an empire at the height of its powers and confidence: glorious, sophisticated, deeply aware of its history, rich in poetry and convention, inward-looking, and alternately bemused by and contemptuous of anyone from outside what Teixcalaan defines as civilization, when Teixcalaan thinks of them at all.

But as good as the setting is (and it's superb, with a deep, lived-in feel), the strength of this book is its characters. Mahit was expecting to be the relatively insignificant ambassador of a small station, tasked with trade negotiations and routine approvals and given time to get her feet under her. But when it quickly becomes clear that Yskandr was involved in some complex machinations at the heart of the Teixcalaan government, she shows admirable skill for thinking on her feet, making fast decisions, and mixing thoughtful reserve and daring leaps of judgment.

Mahit is here alone from Lsel, but she's not without assistance. Teixcalaan has assigned her an asekreta, a cultural liaison who works for the Information Ministry. Her name is Three Seagrass, and she is the best part of this book. Mahit starts wisely suspicious of her, and Three Seagrass starts carefully and thoroughly professional. But as the complexities of Mahit's situation mount, she and Three Seagrass develop a complex and delightful friendship, one that slowly builds on cautious trust and crosses cultural boundaries without ignoring them. Three Seagrass's nearly-unflappable curiosity and guidance is a perfect complement to Mahit's reserve and calculated gambits, and then inverts beautifully later in the book when the politics Mahit uncovers start to shake Three Seagrass's sense of stability. Their friendship is the emotional heart of this story, full of delicate grace notes and never falling into stock patterns.

Martine also does some things with gender and sexuality that are remarkable in how smoothly they lie below the surface. Neither culture in this novel cares much about the gender configurations of sexual partnerships, which means A Memory Called Empire shares with Nicola Griffith novels an unmarked acceptance of same-sex relationships. It's also not eager to pair up characters or put romance at the center of the story, which I greatly appreciated. And I was delighted that the character who navigates hierarchy via emotional connection and tumbling into the beds of the politically influential is, for once, the man.

I am stunned that this is a first novel. Martine has masterful control over both the characters and plot, keeping me engrossed and fully engaged from the first chapter. Mahit's caution towards her possible allies and her discovery of the lay of the political land parallel the reader's discovery of the shape of the plot in a way that lets one absorb Teixcalaanli politics alongside her. Lsel is at the center of the story, but only as part of Teixcalaanli internal maneuvering. It is important to the empire but is not treated as significant or worthy of its own voice, which is a knife-sharp thrust of cultural characterization. And the shadow of Yskandr's prior actions is beautifully handled, leaving both the reader and Mahit wondering whether he was a brilliant strategic genius or in way over his head. Or perhaps both.

This is also a book about empire, colonization, and absorption, about what it's like to delight in the vastness of its culture and history while simultaneously fearful of drowning in it. I've never before read a book that captures the tension of being an ambassador to a larger and more powerful nation: the complex feelings of admiration and fear, and the need to both understand and respect and in some ways crave the culture while still holding oneself apart. Mahit is by turns isolated and accepted, and by turns craves acceptance and inclusion and is wary of it. It's a set of emotions that I rarely see in space opera.

This is one of the best science fiction novels I've read, one that I'll mention in the same breath as Ancillary Justice or Cyteen. It is a thoroughly satisfying story, one that lasted just as long as it should and left me feeling satiated, happy, and eager for the sequel. You will not regret reading this, and I expect to see it on a lot of award lists next year.

Followed by A Desolation Called Peace, which I've already pre-ordered.

Rating: 10 out of 10

Last spun 2019-12-12 from thread modified 2008-08-13