Sep 19 9 2019


Em Short, who is arguably the world’s most thoughtful and prolific critic of electronic fictions, recently wrote an interesting note on “Links and Structures from Michael Joyce to Twine. ” This followed a critique of my own “The Fellow Who Caught Fire,” in which Short took me to task for expecting readers to know my arcane and idiosyncratic terminology. “That’s not my terminology,” I complained, “it was coined in a 1997 paper by Michael Joyce! And if you can’t allude to well-known papers in a pamphlet intended to be read by professors of English, when can you?”

And this is Short’s great strength: she promptly took up Joyce’s 1997 paper “Nonce Upon Some Times”, examined it, and looked at ways its concepts played out in the decades that followed. It’s a good discussion; here, I want to gloss one peripheral detail.

At the outset, Short accurately observes that “Joyce, however, is overtly contemptuous of branching narrative.” “What interests him,” she observes, “is not the question of how one might project oneself into the role of a protagonist; not how one might experience agency, constraint, or non-agency through this pattern of links.” These latter questions are central to the Interactive Fiction (IF) movement with which Short is identified.

Why was Joyce not interested in how one might project oneself into the role of protagonist? It’s easy, but wrong, to assume this is just the contempt of an insider for the gender-fluid, punk-inflected Twine world.

First, Joyce was not attacking IF; IF didn’t exist then, not as a literary movement. The “choose-your-own-adventure” stories he dismisses as “dearily branching fiction” are Edward Packard’s children’s books. Here’s the opening from Tenopia Island, shortly after you’ve crash-landed your spaceship’s lifepod on Tenopia:

By the time you crawl through the hatch, your pod is already surrounded by giant half-human creatures with huge forked hands. They blindfold you and lead you along a winding bumpy surface, then down a long ladder that leads deep underground. At last they remove your blindfold. You are in a huge dimly lit cavern that reeks of sulfurous fumes.
The creatures search you. You hold your breath when they find your computer. But they toss it aside as a useless ornament, and you're able to retrieve it a few moments later.
You quickly learn that your captors are crogocides and that you have been condemned to slavery in their krelium mine. Closely watched by crogocide guards wielding long spiked sticks, you are forced to chip and shovel krelium ore for seven hours before you're allowed to rest for the night.

Which half of these creatures are human? How do we know they are “crogocides?” In what sense are their hands “forked” and ours not? Why does the cavern reek of sulfurous fumes? Why is access by ladder, when even 19th-century coal mines had mechanical access? How do they know to how to blindfold a human? Why would they? This isn’t competent SF.

But let that be. I think that Joyce — an MFA-wielding disciple of the Iowa Writer’s Workshop — was reacting against a different and more substantial opponent, the Victorian sentimental novel. “Sentimental” in this context has a precise, technical meaning: it refers to art that tries to teach us how we ought to feel. This was a great project of the 19th century: think Dickens and Trollope and Victor Hugo: the purpose of the plot was to show you how a good person ought to think about the world.

Modernism saw this use of plot as a chump’s game. Authors contrive events to show that the good end well and the bad end otherwise. Virtuous and compliant women were rewarded with industrious husbands and loving sons; women who strayed or rebelled received tuberculosis and poverty. The world we know isn’t like that. You can’t look at an ugly man and know he is wicked; you can’t glance at a poor woman and know her to be unworthy. It’s easy to lie in the sentimental novel, to set up the fictive world so that wealth and power are synonymous with virtue.

In writing, the reaction against the sentimental novel is called Modernism. People devised different ways to avoid dishonest sentiment. You could try to speak to the unconscious: Brecht and Gertrude Stein and sometimes James Joyce. You could strive to speak directly and truly, about things everyone knows and in language everyone uses: Hammett and Hemingway, Faulkner and Lawrence. You could speak to the concerns of people who don’t generally show up in literature and who have no reason to lie: Huck Finn, Carrie Meeber, Proust’s Narrator, or the speakers in Frost’s early dramatic poems like “Death Of The Hired Hand”. You could avoid plot altogether, or turn plot on its head, or subvert your own plots.

It’s this crusade — the crusade against sentimental dishonesty — in which Joyce has enlisted. His opponent is sentimental, slanted, dishonest propaganda, not IF.

Moreover, “Nonce Upon Some Times” was written in 1997, the turning point in a generation-long interrogation of the relationship between text and meaning. Is the meaning of a story contained in the marks on the page? Reflection gradually showed that readers construct meaning from the intersection of what they know and what the text says. Readers matter, and different readers read differently. This ultimately led people to wonder whether there was any inherent meaning in a text, whether there was anything there that some reader might not turn entirely on its head; this got sorted out eventually, but for a few years it was a near-run thing. Links make this tangible: everyone reads differently, but that difference is easy to hide when we're talking about The New Yorker. When the way you read means you see different words on the screen, it’s harder to argue that the reader is merely a passive recipient of information transfer.

Joyce’s disinterest in branching narrative is not an attack on IF; it's a rejection of Horatio Alger and Carolyn Keane.

by Rachel DeLoach Williams

Rachel Williams was a young, ambitious assistant at Vanity Fair when, on a girls’ night out, she met Anna Delvey. Delvey was nice, friendly, and happened to be rich: she was in New York to set up a small art foundation and to buy a building to house its gallery and performance space. Rachel and Anna hit it off; they started working out together, meeting for drinks, meeting for dinner. Anna was generous about picking up the tab, and gracious in letting Rachel occasionally pay for things and handle arrangements. Then Anna took Rachel to Marrakesh along with her personal trainer and her videographer. It was lots of fun.

Something was wrong with Anna’s credit card, and soon something is very wrong with everything and Rachel owes almost $70,000 in hotel charges on her corporate Amex. It turns out that Anna wasn’t an heiress at all. Solving this becomes quite a puzzle.

One thing that’s fascinating here is that it's not quite clear whether Anna was actually running a long con. If so, she doesn't seem to have had a crew, or to have known a lot about the business. Yet she was very good at fooling a lot of people for a long time, and it makes for an enthrallingly good yarn.

by Tara Westover

A fascinating memoir of a girl who was home-schooled in remote rural Idaho. By the time Tara came along, Mom had lost interest in the “school” part of home-schooling, so she lived a sort of improvised dystopian version of Summerhill while working for her manic-depressive, zealous, and very dangerous father in the family junkyard. Her parents don’t hold with public schools, or with medicine, or with the government, and are actively preparing for the end-times by canning fruit and stockpiling ammo. Her father is certain that civilization will collapse from Y2K: when midnight passes and the television fails to go off the air, she’s relieved but vaguely disappointed.

When an older brother beings to turn chronically violent and abusive, Tara flees to Brigham Young University, where she is appalled by the other students’ apparel (whorish) and her own ignorance (profound). Starting college, she had no idea what the holocaust was; reading Les Miserables because it seemed the sort of book a college student should read, she bogs down. “Napoleon felt no more real to me than Jean Valjean. I had never heard of either.”

Joshua Marshall: Epic List of (Historical) Books. Where he and I overlap interests, this is spot on.

Practical Concurrency: Some Rules

When Tinderbox started out, computers had a single processor. When it was time to update agents, Tinderbox interrupted your work for a moment, updated agents, and then allowed you to resume. We tried hard to guess when it would be convenient to do this, but of course that’s not always something your computer can anticipate.

Nowadays, your computer has somewhere between 4 and 24 processors. Starting with Tinderbox 6, agents no longer interrupted you; agents do their work on one processor while you do your work on another. The two tasks run concurrently.

Concurrent operations can be tricky to get right. For example, suppose one operation is writing “The truth will set you free” into a buffer, and another operation is writing “Donald Trump” into the same buffer. You might end up with “The tr Trump”, or “Donald will set you free”, or “Toearl…” or something else. If one processor reads while another is writing, it might see a partial result, and that might be complete nonsense. This means you need to take great care whenever processors share results.

Getting concurrency right by the book is one thing, and getting it right in the trenches is something else entirely. I’ve recently changed my approach to concurrency; here are my newly-revised rules.

  1. You can get away with murder. Going by the book, you’ve got to use extreme caution and you’ve always got to get it right. In practice, Tinderbox Six took all sorts of risks and accepted that it was Doing It Wrong in order to get stuff done. That worked remarkably well for a long time. Naive Concurrency blows up when two operations step on each others’ toes: a lot of the time, they’ll just be lucky and will go for hours (or years) without getting in each others’ way.
  2. You can often get away without concurrency. (This is the Law of Brent Simmons, named for the developer of Vesper who pledged to do everything on the main thread.) Computers are fast: much of the time, just ask one processor to do everything and you’ll be fine. You can’t always do without concurrency: some things like network access do require it. But if you just do everything on the main thread, you’ll often find that everything is fast enough.
  3. The profiler is now good. It wasn’t always. In the Tinderbox 4 era, firing up the Profiler meant recompiling the world, and that took 20 minutes. Then, you'd get slow and inconclusive results, and throw up your hands. Life today is better, recompiling the world only takes a minute or two. For Tinderbox, ruthless refactoring has eliminated lots of classes that had a zillion dependencies, and that means I seldom need to recompile the world anyway.
  4. Placekicks are easy. The placekick concurrency pattern is a way to offload a complex calculation or network access if you don't need the answer right away. In the placekick patterns, the “ball” is a bundle of work that needs to be done; you set it up, you kick it to another processor, and then you forget all about it. For example, Tinderbox notes may need to display miniature thumbnails of their text, and because those thumbnails might have lots of different fonts, styles, and pictures, they can be slow to build. So, when we add the note’s view, we fire up a new operation to build the thumbnail in the background, and leave a note behind to say that there's no thumbnail yet but it's under construction. If we need to draw the note before the thumbnail's ready, we simply skip the thumbnail; when it’s finally ready, we leave the thumbnail the appropriate place and it’ll get drawn the next time we update the screen. Placekicks are hard to get wrong: you kick the ball, and you're done. When the operation has done what it was asked to do, it leaves the result in an agreed-upon place; it doesn't need to coordinate with anything else or ask permission. If you can, use placekicks and only placekicks.
  5. The placekicker shouldn’t tackle. The concurrent operation has one responsibility: kick the ball. It does its task. It may also need to do something to let the system know that it’s finished its work, but that last thing should be trivial. Post a notification, or set a flag, or send one object a single message. Don’t mix responsibilities.
    1. Never put anything but a placekick on a global queue. Placekicks can’t deadlock. You know they can’t deadlock. Any idiot can see they can’t deadlock. If there’s any question, make a private queue instead.
  6. Queues are light and cheap. Operations are light and cheap, too. It takes a few microseconds to make a GCD dispatch queue, and scarcely longer to make an NSOperationQueue. Adding an operation to a queue is just as fast. It’s not necessary to be confident that all your tasks are big enough to demand concurrent processing: if some are, there's not much overhead to simply spinning everything off.
  7. Focused queues are easier to use. If a queue has one clear purpose, it’s easier to be confident it won’t deadlock. Dispatch queues are cheap. Don’t share queues, don’t reuse queues, don’t worry about making queues.
  8. Classes should encapsulate their queues. This is a big change: Tinderbox used to depend heavily on a bunch of queues that were public knowledge. That’s a bad idea, First, we're sharing disposable objects — I had no idea how disposable dispatch queues are, but there’s no reason to conserve them. Second, when lots of classes are sharing a queue, then any badly-behaved class might cause untold trouble for unrelated classes that share the work queue. Placekick concurrency is an implementation detail: no one needs to know that there’s a queue in use, and they certainly don't need the details or identity of the queue.
  9. Test the kick, not the queue. Unit testing concurrent systems is better than it used to be, but clever factoring makes it unnecessary to unit-test placekicks. Instead, make the task operation available as its own method or method object, and let the test system test that. You’ll also want to do some integration testing on the whole concurrent system, but that’s another story.
  10. Classes should clean their queues. Be sure that any objects and resources that your tasks require remain available until the tasks are done. Closing the document and quitting the application requires special care that tasks be completed before we dispose of their tools.
    1. To clean a queue, cancel all pending operations, then wait for the current operation to finish. Do not suspend the queue, but do make sure no new tasks are added! It’s easy for a class to be sure that it doesn't add anything to its own private queue, but hard for a system to be confident that no one is adding tasks to a queue shared with lots of other objects. That’s a big advantage of private queues.
  11. Use queue hierarchies to consolidate bursts of work. When we open a new Tinderbox document, there's a bunch of low-level indexing that needs to be done. It’s not urgent, and typically we need only a few milliseconds per note, but some notes will take more work and there might be 10,000 notes. So, we placekick the indexing. The system sees that we want 10,000 things done! “Gadzooks!” it says to itself, “I’d better roll up my sleeves!” This can make the system s[ion up a bunch of worker threads. But we know better: it looks like a pile of work, but it’s not that much and it’s not urgent. So, we tell the system to route all 10,000 tasks to another queue with limited concurrency: now, the system says “I have 10,000 things to do, but I can only do two of then at a time: piece of cake!”
  12. Read sync, write async. When you read a shared object, you need a result. Read synchronously: that shows your intent and, if the wait is long, you'll see the problem at once as waiting for the read, rather than some mysterious lock or stall. Write asynchronously; it’s the classic placekick and there's no need to wait. The exception here is where we’re writing to say that the old value is dead, defunct, and not to be used; in that case, write needs to block all the readers, and asynchronous reading can get you back some time. Often, the easiest approach remains serial access managed by a single dedicated serial queue that can be used with the read sync/write async rule.