department of hack
1249 stories

Timo Tijhof


Who are you, and what do you do?

I'm Timo Tijhof. I work as a principal engineer for Wikimedia Foundation, the non-profit behind Wikipedia and its sister projects. I help build, design, and maintain the MediaWiki software, currently focusing on web performance.

What hardware do you use?

My computer is a 15-inch MacBook Pro (2015 model) with 16 GB of RAM and a 500 GB drive. My work desk - I work at home - is an IKEA BEKANT, where I place the laptop on an old wooden tea chest. There, the laptop connects with a Logitech MX Master 2S mouse, and a Matias Ergo Pro keyboard.

I use Bose QuietComfort 25 headphones for meetings, and for when I need a quiet zone. They provide great sound quality, and cancel noise well even without batteries. This might be obvious, but it took me a while to get out of the habit of always powering them on - instead coming to see the headphones themselves as my zone.

My phone is an iPhone SE I've had since 2016, and yes, I suppose that portrays me as one of those stubborn folk who refuse to let go of the iPhone 5 form factor. In truth I'm not that bound to it. It works as well as it did then, and I've got no complaints. The newer models neither attract nor repulse me. I replaced its battery last year for a few bucks, and expect to use it for at least two more years.

Beyond the above; I have an AirPods case nearby for podcast listening, and a paper notebook to negotiate with my past and future self. I use WD Elements Portable drives for backups and offline storage.

And what software?

I code in Atom or Sublime Text. For projects that I work on most, I have everything configured just right in Atom. For everything else, including commit messages and ad-hoc projects, Sublime is right there. I started using Atom since it caught up to Sublime's feature set, is open-source, and enjoys a larger ecosystem. But, as it's still a slow-starting Electron app, Sublime remains my go-to editor to instantly open any file or directory from the command-line.

I rely on NetNewsWire and Feedly to keep up with blogs, news sites, and other feeds. For organic discovery of articles outside my circles I rely on social media, which I interact with through the Pinafore web app.

I take notes with Simplenote. I particularly like its version history and syncing. Most of my long-term projects are in here, with new data prepended as I go. I have a pinned note that serves as my inbox for low-friction entering of impromptu ideas wherever I go. These are later triaged into my todo system, a mixture of OmniFocus and Apple Reminders. I use KeeWeb for offline sensitive data.

I take regular breaks with the help of Time Out. This app has been the single-most impactful addition to my workflow. I found it distracting at first but, after some tuning, it's become a trusted companion. I use micro breaks to check my posture, and to check if I should continue what I'm doing if I've been at it for a while. The macro breaks are for exercise, nutrition, and remembering what else I set out to do today.

On desktop I currently browse with Firefox. But, I do make a point of changing desktop browsers once every year.

On mobile I use Firefox Focus as my main browser. For the handful of sites where I want to be logged-in, I have a folder of Safari shortcuts. My homescreen is minimal, featuring only Focus, NetNewsWire, Simplenote, Podcasts, and Telegram.

What would be your dream setup?

I've tuned my setup a lot and am quite happy with it.

In the past, I tried a large monitor, but, didn't use it much. I found I generally prefer apps in narrower windows, and no more than two or three apps on-screen. I'm a big fan of Spaces and the retention of vision it allows for. (Although you have to turn off the "Automatically rearrange" system preference.)

I prefer keeping the spatial composition of windows in my mind, rather than on-screen. I use ctrl-arrow to pop over to the virtual desktop where I know a given window resides. Before I switch, I know exactly where I'm going and which single or double keystroke gets me there. Previously, switches became a moment. I didn't like that. Instead of Overview-Find-Go, with Spaces you just "Go".

I do see the appeal of a monitor when working with photos or videos, allowing for more content and controls on-screen. I studied to be a graphic designer and sometimes miss the iMac I worked with then, but, I can't justify it now.

The one part of my setup I'm dissatisfied with is the keyboard. I've had the Matias Ergo Pro for a year, and still get unwanted or missed key presses at times. I'd like to try out the Logitech Ergo keyboard.

Read the whole story
3 days ago
Boulder, CO
14 days ago
Share this story

Unifying the Technical Interview


Previously: Rewriting the Technical Interview.

Aisha’s hands rattle you. They float gently in front of her shoulders, wrists cocked back. One sways cheerfully as she banters with the hiring manager—her lacquered nails a cyan mosaic over ochre palms. They flit, then hover momentarily as the two women arrange lunch. When the door closes, Aisha slaps her fingertips eagerly on the pine-veneer tabletop. Where have you seen them before?

But she is giggling and glad to finally meet you, and her hair bounces in loose ringlets around the shoulders of her yellow sundress, and you like her, this thirty-something engineer who has worked here three years (even if you don’t understand what it is, exactly, that she does for Mineral Analytics, Limited), who heard you were on the market, and just had to interview you personally. She tells you about the yogurt bar, and the yoga studio, and how important work-life balance is to the company. Then she asks you to balance a binary tree.

Even after all these centuries, you still have trouble grafting limbs. “I’m sorry, you’ll have to give me a moment,” you stall. “I can never keep the transformation rules straight.”

Plead with the demigoddess Andréka to intercede on your behalf.

vidrun@bamse ~> swipl

Aisha smirks. You suspect she knows something.

“Remind me, Aisha—we need to make sure that both branches have the same height? Or differ by just one?”

“At every level.” She folds her arms and offers you a break. “Would you like me to get us started?”

Release the cryptographic wards on two of your xterms, and slide the machine over.

eval_prim(Exp, _, Exp) :- Exp = nil ; Exp = [] ; number(Exp).

“We’ll start with terminals. Nils, empty lists, and numbers—that should be a good start, don’t you think?”

This seems reasonable enough.

“And you know we’re gonna need equality constraints. Addition too, for tree heights.”

:- use_module(library(reif)). eval_prim([eq, A, B], Env, R) :- eval_exp(A, Env, ARes), eval_exp(B, Env, BRes), if_((ARes = BRes), R = true, R = nil). eval_prim([plus, A, B], Env, R) :- eval_exp(A, Env, AR), eval_exp(B, Env, BR), R is AR + BR. eval_exp(Exp, Env, R) :- eval_prim(Exp, Env, R), !.

Clock! She pops her tongue at the cut, startling you. Consider asking about Env, but decide against it: she clearly knows where she’s going.

Aisha gives you a very knowing look over the top of her glasses, then summons a list from the void.

eval_prim([first, List], Env, R) :- eval_exp(List, Env, ListR), if_(([] = ListR), R = nil, [R|_] = ListR). eval_prim([rest, List], Env, R) :- eval_exp(List, Env, ListR), ((ListR = [], R = nil) ; (ListR = [_|R])). eval_prim([cons, Head, Tail], Env, R) :- eval_exp(Head, Env, HeadR), eval_exp(Tail, Env, TailR), if_((nil = TailR), TailL = [], TailL = TailR), R = [HeadR | TailL].

Without moving anything else, raise a single eyebrow two centimeters.

“I’ve read your work! You couldn’t walk into a bodega without conjuring a shopping list from thin air.”

She has read you, hasn’t she?

?- eval_exp([cons, 2, [cons, 1, nil]], {}, R). R = [2, 1].

You’re ready to take it from here, but Aisha continues, nails clacking against the keycaps.

eval_each([], _, []). eval_each([X | More], Env, [XR | MoreR]) :- eval_exp(X, Env, XR), eval_each(More, Env, MoreR).

“Aisha, I–”

Her hands flash and flourish, slapping out a polyrhythm you remember.

type(X, R) :- (atom(X), R = atom) ; (number(X), R = number) ; ([] = X , R = list) ; (functor(X, '[|]', _), R = list), ! ; (compound_name_arity(X, λ, _), R = function), ! ; (compound_name_arity(X, , _), R = macro), ! ; (compound_name_arity(X, Type, _), R = Type). eval_prim([type, X], Env, R) :- eval_exp(X, Env, XR), type(XR, R).

Fy Faen! You see it now!

Snap your fingers, recalling your laptop to your hands. The instrument flies from her grasp, and you are already on your feet, anchoring yourself to the ethernet, Aisha standing in one fluid swooping motion as you cross-step back for space, one hand outstretched to seize—

—thin air.

Your laptop hovers in the air between you, screen blushing rose to iris. Aisha, yawning, checks her nails. Faint bangles of non-printable control characters encircle her wrist in glowing rings, impossible to see directly.

“Oh honey.” Her voice acquires a familiar, husky resonance.

You know her you know her you know–

She sighs in exasperation. “You’re old, not senile!”

Remember 1921: the salt-wind scoring your forehead as the bow of the Kovno cut through the cold Atlantic night. The icy pit of the Gardnos Stone, wrapped in burlap, weighing your bag as if seeking to drown the whole ship. It refused flight, warmth, comprehension. The coven had sent you in search of answers: to Antwerp, to Victorieplaats—all masonry and copper, gold figures gleaming atop the mercantile houses of the bustling diamond-hive.

You’d sought a specialist. You found Aligaz.

A eager scion of the Semien stone-mages, cast from rocky fingertips at a time of cosmopolitan awakenings, he had set up shop in an alley just south of the plaza. Géomancien, Aligaz had explained, giggling at the word, and gestured to the glittering shelves: crystals and milky stones in every color, charged by the steady glow of gaslight. All swish and charm, he had whisked the Stone from your bag and informed you, in lilting French, that you needed to lighten up. “Ces suédois!” You’d bristled at the insult… but he had, in the end, been right.

You had traded spells and languages, huddled under the rain-roaring copper roof of Aligaz’s attic. Worked together to uncover the mystery of Gardnos, and one another. Aligaz, endlessly inventive, had outmatched you at every turn: constructing puzzles within puzzles, locks which everted, became their own keys. He had wondered at your tales of the fjords, and you, in turn, had planned to travel south with him, to see the Ethiopian landscapes he so lovingly recalled—but the coven recalled you to Skjækra. That he could stand before you now, after so many years…

“… Aisha?” You ask, tentative.

“After my aunt.” She smiles fondly.

“What a beautiful name!” It suits her well. “When did—”

“Harlem. After the war.” And here she trails off; lost, perhaps, in her own private recollection.

You are happy for Aisha, of course. To claim one’s true self is the right of all people, and the most powerful of all magics. But you have so many questions!

“What became of the Stone? How did you know? What are you doing here, of all places?”

“Vidrun, this is a geological surveying company. Of course I’m here. The question is why are you applying?”

You pause for a moment.

Why are you here?

“You summoned me!”

“I didn’t know for sure,” she admits. “But rumors spread, and your style is distinctive. How could I pass over the chance to challenge you once more?”

Now the meaning of her spell becomes clear: a trap laid not to ensnare, but to evince. She has offered you a gift. Return the laptop to her, and accept the invitation with grace.

bind(K, V, Env, Env2) :- atom(K), Env2 = Env.put(K, V).

“I bind you, Vidrun,” she intones. Her voice now carries the weight of the Semien, of the Alps, of the Sierra Nevada. A century has grounded her.

“I bind you head and tail.”

bind([], _, Env, Env). bind([K|Ks], Vs, Env, Env3) :- (K = &(K2), bind(K2, Vs, Env, Env3), !) ; ([] = Vs, bind(K, nil, Env, Env2), bind(Ks, [], Env2, Env3)); ([V1|V1s] = Vs, bind(K, V1, Env, Env2), bind(Ks, V1s, Env2, Env3)).

Your breath catches in your throat.

“You shall see only that which is shown,” she continues.

eval_var(Var, Env, R) :- atom(Var), get_dict(Var, Env, R).

“You may speak without speaking,”

eval_prim([quote, R], _, R).

“And within these bounds, you may choose your own path.”

eval_prim([cond], _Env, nil). eval_prim([cond, Default], Env, R) :- eval_exp(Default, Env, R). eval_prim([cond, Test, Branch | More], Env, R) :- eval_exp(Test, Env, TestR), if_((nil = TestR), eval_prim([cond | More], Env, R), eval_exp(Branch, Env, R)).

A Lisp, then. Murmuring softly, you offer a prayer to McCarthy. Hopefully Aisha does not expect you to reinvent lambdas from scratch.

“What you let, will become.”

eval_prim([let, [], Body], Env, R) :- eval_exp(Body, Env, R). eval_prim([let, [K, V | More], Body], Env, R) :- eval_exp(V, Env, VR), bind(K, VR, Env, Env2), eval_prim([let, More, Body], Env2, R).

Well, that’s something, at least. Serial assignment, too—a luxury. But Aisha is not finished. She clacks her heels firmly onto the industrially-sealed concrete, and opens her arms wide. Her palms are ruddy laterite, her upturned eyes glinting magnetite under halogen stars.

evoke(Vars, Args, Body, Env, R) :- bind(Vars, Args, Env, Env2), eval_exp(Body, Env2, R). eval_application([Fun | Args], Env, R) :- eval_exp(Fun, Env, FunR), FunR = λ(Vars, Body, Env2), eval_each(Args, Env, ArgsR), evoke(Vars, ArgsR, Body, Env2, R).

The Church. The lambda calculus. The gay agenda. It has known a thousand names, a thousand forms. Aisha slams her palms together, and with the crash of mountains, summons the thousandth-and-first.

eval_prim([lambda, Args, Body], Env, R) :- R = λ(Args, Body, Env.put(recur, R)).

The conference room twists, folds somehow. Outside, the rows of desks and office furniture rush away to the horizon, replaced by a featureless expanse of evenly-lit pine. On the tabletop, a miniature cube of glass and concrete inflates from nothing to a tiny, modernist dollhouse. You lean over to look inside, notice the light outside the conference room abruptly dim, and think better of it.

eval_exp(Exp, Env, R) :- eval_var(Exp, Env, R), ! ; eval_prim(Exp, Env, R), ! ; eval_application(Exp, Env, R), !.

Aisha takes a deep breath and regains her composure. You sense her spell is near complete.

Whisper softly, so as not to disturb her work. “Macros?”

She glares sidelong, eyes wide. “Really?”

“It’ll be fun.”

eval_prim([gensym, Prefix], _Env, R) :- atom_concat(Prefix, '__auto__', Sym), gensym(Sym, R). eval_prim([macro, Args, Body], Env, R) :- R = (Args, Body, Env). eval_application([Fun | Args], Env, R) :- eval_exp(Fun, Env, FunR), ((FunR = λ(Vars, Body, Env2), eval_each(Args, Env, ArgsR), evoke(Vars, ArgsR, Body, Env2, R)) ; (FunR = (Vars, Body, Env2), evoke(Vars, Args, Body, Env2, Code), eval_exp(Code, Env, R))).

“One more thing,” you request. “Some kind of product type.”

“Ma'am, this is a Wendy’s.”

“Do you really want to watch me reinvent a dynamic type system on top of bare lists?”

Aisha considers the possibility—somewhat sadistically, you think—but this is not that kind of interview.

“Fine. But that’s ALL you’re getting.”

eval_prim([struct, Type | Fields], Env, R) :- eval_each(Fields, Env, FieldsR), compound_name_arguments(R, Type, FieldsR). eval_prim([destruct, Type, Struct], Env, R) :- eval_exp(Struct, Env, StructR), compound_name_arguments(StructR, Type, R).

Your hair charges with static as Aisha seals the spell. Her nails flare in the light, leaving cyan trails in the air above the keyboard. As she executes the final command, you feel your connection with Andréka severed. You are locked in Aisha’s calculus now.

“I knew exactly where you were going with this, Vidrun. You couldn’t remember how to balance a binary tree, so you thought you’d define the transformational invariants and have Prolog solve them for you.”

“So you’re trapping me in a Lisp instead.”

“Would you have preferred an Algolem?” Aisha smirks. You would not.

Very well. Sprinkle salt, as is tradition, in the protective form of two parentheses. No—you wipe the salt clear—square brackets. Test the forms of this new plane.

?- eval_exp([let, [list, [lambda, [&(args)], args]], [list, 1, 2, 3]], e{}, R). R = [1, 2, 3].

It’s not much, but it’s a start. Aisha watches you expectantly; you need to do something. Stall for time by summoning an algebra.

and, [macro, [a, b], [list, [quote, cond], a, b]], or, [macro, [a, b], [let, [a_, [gensym, a]], [list, [quote, let], [list, a_, a], [cond, a_, a_, b]]]], not, [lambda, [x], [cond, x, nil, [quote, true]]],

Aisha makes a show of yawning. Bore her with type predicates.

is_null, [lambda, [x], [or, [eq, x, []], [eq, x, nil]]], is_list, [lambda, [x], [eq, [type, x], [quote, list]]], is_pair, [lambda, [x], [and, [not, [is_null, x]], [is_list, x]]], is_fn, [lambda, [x], [eq, [type, x], [quote, function]]],

You think you’re getting the hang of this—though you haven’t used your comma key this much in years. It’s all so excitingly verbose.

second, [lambda, [coll], [first, [rest, coll]]], map, [lambda, [f, coll], [cond, [is_null, coll], [], [cons, [f, [first, coll]], [recur, f, [rest, coll]]]]], find, [lambda, [f, coll], [cond, [is_pair, coll], [let, [x, [first, coll]], [cond, [f, x], x, [recur, f, [rest, coll]]]]]], fold, [lambda, [f, init, coll], [cond, [is_null, coll], init, [recur, f, [f, init, [first, coll]], [rest, coll]]]], rev, [lambda, [coll], [fold, [lambda, [list, elem], [cons, elem, list]], [], coll]], [rev, [list, 1,2,3]] R = [3, 2, 1].

Smile, wistfully. That had been a good interview.

syntax_quote_fn, [lambda, [expr], [cond, [is_null, expr], expr, [is_list, expr], [let, [f, [first, expr]], [cond, [eq, f, [quote, unquote]], [second, expr], [cons, [quote, list], [map, recur, expr]]]], [list, [quote, quote], expr]]], syntax_quote, [macro, [expr], [syntax_quote_fn, expr]],

“Honestly!” Aisha mutters. “I don’t know what you wanted macros for in the first place. Is this really necessary for balancing a tree?”

Keep your expression as enigmatic as possible. She may have cut you off from the Goddess Andréka, but you can will your own deities into existence.

lvar, [lambda, [num], [struct, lvar, num]], is_lvar, [lambda, [x], [eq, [quote, lvar], [type, x]]], lvar_num, [lambda, [x], [first, [destruct, foo, x]]],

Aisha’s jaw drops as she grasps your scheme. “You have got to be kidding.”

“Kiselyov, Friedman, Byrd”, you incant. “I call upon you, triune, though you have never dwelt within these forms. May you walk this plane as well!”

walk, [lambda, [u, subs], [let, [pr, [and, [is_lvar, u], [find, [lambda, [v], [eq, u, [first, v]]], subs]]], [cond, pr, [recur, [rest, pr], subs], u]]],

“May all merge within your reasoned embrace!”

ext_s, [lambda, [x, v, subs], [cons, [cons, x, v], subs]], unify, [lambda, [u, v, subs], [let, [u, [walk, u, subs], v, [walk, v, subs]], [cond, [eq, u, v], subs, [is_lvar, u], [ext_s, u, v, subs], [is_lvar, v], [ext_s, v, u, subs], [and, [is_pair, u], [is_pair, v]], [let, [subs, [recur, [first, u], [first, v], subs]], [and, subs, [recur, [rest, u], [rest, v], subs]]]]]],

Aisha locks eyes with you. Inside each of you, something shifts, merges, becomes bound.

mzero, [],

“M'Zero,” she quips, and you share a giggle.

“Absolute unit,” you respond.

unit, [lambda, [state], [cons, state, mzero]],

Demand equality. Some would call this a constraint, but you know it to be foundational.

eql, [lambda, [u, v], [lambda, [st], [let, [subs, [unify, u, v, [first, st]]], [cond, subs, [unit, [cons, subs, [rest, st]]], mzero]]]],

“May this space be open to newcomers,” you declare.

call_fresh, [lambda, [f], [lambda, [st], [let, [c, [rest, st]], [[f, [lvar, c]], [cons, [first, st], [plus, 1, c]]]]]],

“And may their futures weave a beautiful tapestry.” You forsee threads of execution racing ahead, taking turns, alternating and combining.

mplus, [lambda, [a, b], [let, [mplus, recur], [cond, [is_null, a], b, [is_fn, a], [lambda, [], [mplus, b, [a]]], [cons, [first, a], [mplus, [rest, a], b]]]]], bind, [lambda, [stream, goal], [let, [bind, recur], [cond, [is_null, stream], mzero, [is_fn, stream], [lambda, [], [bind, [stream], goal]], [mplus, [goal, [first, stream]], [bind, [rest, stream], goal]]]]],

Imbue the triune with its own algebra.

disj, [lambda, [g1, g2], [lambda, [state], [mplus, [g1, state], [g2, state]]]], conj, [lambda, [g1, g2], [lambda, [state], [bind, [g1, state], g2]]]

Moving carefully, you follow a thread of that tapestry to its first few knots.

pull, [lambda, [stream], [cond, [is_fn, stream], [recur, [stream]], stream]], take, [lambda, [n, stream], [cond, [eq, 0, n], [], [let, [stream, [pull, stream], state, [first, stream]], [cond, state, [cons, state, [recur, [plus, n, -1], [rest, stream]]], []]]]], run_raw, [lambda, [n, goal], [take, n, [goal, [cons, [], 0]]]],

The work complete, you let go a sigh. Your godhead takes its first tottering steps.

?- eval([run_raw, 3, [call_fresh, [lambda, [x], [disj, [eql, x, [quote, aisha]], [eql, x, [quote, vidrun]]]]]], R = [[[[lvar(0)|aisha]]|1], [[[lvar(0)|vidrun]]|1]].

“You absolute madwoman,” Aisha chortles. “You can’t remember how to balance a binary tree, but you memorized µKanren?”

“It comes in handy!” You stammer. “And honestly, it’s shorter.” Offer her a reifier by way of apology.

“Fine. Fine!” Aisha throws up her hands in mock exasperation. “I’ve let you get away with it this far!”

walkr, [lambda, [v, subs], [let, [v, [walk, v, subs], walkr, recur, w, [lambda, [v2], [walkr, v2, subs]]], [cond, [is_lvar, v], v, [is_pair, v], [cons, [w, [first, v]], [w, [rest, v]]], v]]], reify, [lambda, [states], [map, [lambda, [state], [walkr, [lvar, 0], [first, state]]], states]]

“Only with respect to the first variable,” you explain. “But it still makes things easier to read.”

eval([reify, [run_raw, 3, [call_fresh, [lambda, [x], [disj, [eql, x, [quote, aisha]], [eql, x, [quote, vidrun]]]]]]], R). R = [aisha, vidrun].

Aisha has the look of a woman who has realized, six hours into her shift, that she has left the oven on. “That’s why you wanted macros. You wanted syntax for this rickety-ass interpreter.”

Reward her insight by entering the metatrance of creation, and let the forms spill from your fingertips.

zzz, [macro, [g], [let, [state_, [gensym, state]], [syntax_quote, [lambda, [[unquote, state_]], [lambda, [], [[unquote, g], [unquote, state_]]]]]]], conjall, [macro, [&(gs)], [let, [[g, &(gs)], [rev, gs]], [fold, [lambda, [form, goal], [syntax_quote, [conj, [zzz, [unquote, goal]], [unquote, form]]]], [list, [quote, zzz], g], gs]]], disjall, [macro, [&(gs)], [let, [[g, &(gs)], [rev, gs]], [fold, [lambda, [form, goal], [syntax_quote, [disj, [zzz, [unquote, goal]], [unquote, form]]]], g, gs]]], conde, [macro, [&(clauses)], [cons, [quote, disjall], [map, [lambda, [goals], [cons, [quote, conjall], goals]], clauses]]], fresh, [macro, [vars, &(goals)], [fold, [lambda, [form, var], [syntax_quote, [call_fresh, [lambda, [[unquote, var]], [unquote, form]]]]], [cons, [quote, conjall], goals], [rev, vars]]], run, [macro, [n, vars, &(goals)], [syntax_quote, [reify, [run_raw, [unquote, n], [unquote, [cons, [quote, fresh], [cons, vars, goals]]]]]]]

Crack your knuckles, and enjoy the series of pops which fizzle from somewhere under your keyboard. “Would you like to do the honors?”

Aisha, half-amused, half exasperated, draws a shining hair from her scalp. Fingers deft, she ties a knot in it for zero.

succ, [lambda, [n], [cons, [quote, 'S'], n]], n0, [list, 0], succo, [lambda, [n, next], [eql, [succ, n], next]],

You draw one of your own, and align them, side by side.

lesso, [lambda, [lesser, greater], [let, [lesso, recur], [fresh, [g], [succo, g, greater], [conde, [[eql, lesser, g]], [[lesso, lesser, g]]]]]], maxo, [lambda, [a, b, max], [conde, [[eql, a, b], [eql, max, a]], [[lesso, a, b], [eql, max, b]], [[lesso, b, a], [eql, max, a]]]], approxo, [lambda, [a, b], [conde, [[eql, a, b]], [[succo, a, b]], [[succo, b, a]]]],

Aisha reaches into her purse, and withdraws a single gleaming seed. This she sets gently upon the table, and waits for you to draw it to fruition.

leaf, [lambda, [x], [list, [quote, leaf], x]], branch, [lambda, [left, right], [list, [quote, branch], left, right]],

A maple tree in miniature, no more than 4 inches high, rises from the tabletop. Outside, in the vast expanse of tabletop, its twin—writ large—swells to full size.

Aisha recognizes the form you have chosen, and raises a spell of translation around it.

tree, [lambda, [t], [cond, [is_list, t], [let, [[left, right], t], [branch, [recur, left], [recur, right]]], [leaf, t]]], untree, [lambda, [[type, a, b]], [cond, [eq, type, [quote, branch]], [list, [recur, a], [recur, b]], [eq, type, [quote, leaf]], a]],

With a flick of your hands, the tree shimmers, reforms.

[tree, [quote, [1, [2, 3]]]] R = [branch, [leaf, 1], [branch, [leaf, 2], [leaf, 3]]].

You measure along each leaf, each branch, and find the height thereof.

heighto, [lambda, [t, h], [let, [heighto, recur], [conde, [[fresh, [x], [eql, t, [leaf, x]], [eql, h, n0]]], [[fresh, [left, right, left_height, right_height, max_height], [eql, t, [branch, left, right]], [heighto, left, left_height], [heighto, right, right_height], [maxo, left_height, right_height, max_height], [succo, max_height, h]]]]]],

Pause a moment, and look to Aisha. A balanced tree: each branch of roughly the same height. Each branch, in turn, a balanced tree itself. Speaking slowly, then faster, in unison, recite:

balancedo, [lambda, [t, h], [let, [balancedo, recur], [conde, [[fresh, [x], [eql, t, [leaf, x]]]], [[fresh, [left, right, left_height, right_height], [eql, t, [branch, left, right]], [balancedo, left], [balancedo, right], [heighto, left, left_height], [heighto, right, right_height], [approxo, left_height, right_height]]]]]],

With all the delicacy you can muster, you peel boughs from trunks, rearrange, then press the limbs gently together. Aisha leans in and blows; the warmth of her breath speeding the healing process.

rot_lefto, [lambda, [t1, t2], [fresh, [a, b, c], [eql, t1, [branch, a, [branch, b, c]]], [eql, t2, [branch, [branch, a, b], c]]]],

Aisha folds her hands together, whispers an incantation, and unfolds them. The maple peels apart into mirrored copies.

rot_botho, [lambda, [t1, t2], [conde, [[rot_lefto, t1, t2]], [[rot_lefto, t2, t1]]]],

Following her lead, you thrust your hands forth and split your fingers in twos, then fours. The tree flickers, expanding into a small forest of copies–each different in exactly one way.

roto, [lambda, [t1, t2], [let, [roto, recur], [conde, [[rot_botho, t1, t2]], [[fresh, [t1l, t1r, t2l, t2r], [eql, t1, [branch, t1l, t1r]], [eql, t2, [branch, t2l, t2r]], [conde, [[eql, t1r, t2r], [roto, t1l, t2l]], [[eql, t1l, t2l], [roto, t1r, t2r]]]]]]]],

Aisha snaps, and with a thwap, unfurls a ludicrously oversized hand-fan from thin air. You ask her where she summoned it from, and how did she ever get Turing to sign it—but she simply bats her eyes and fans herself, looking incredibly smug.

rotso, [lambda, [t1, t2], [let, [rotso, recur], [conde, [[eql, t1, t2]], [[fresh, [t], [roto, t1, t], [rotso, t, t2]]]]]],

The tabletop is filled with trees; outside the conference room, an infinite forest of golden leaves shimmers. Aisha’s fan waves, and a thousand branches whisper in wind-dappled light.

When she refurls the fan, the forest folds with it. Only a solitary tree remains.

It is an act of extraordinary beauty, and for a moment, you are back in the Victorieplaats attic: sunset cast through time-warbled windows, each amber shaft holding sparkling motes of dust which blaze into light, drift briefly, and then go dim again: not lost, merely out of view, and Aisha is whispering an incantation to you, her words unfolding a map within a map within your mind, and you wonder, had you not been recalled, what more you might have become…

“Vidrun. Almost there.”

You take her offered hand in yours, and voices evenly matched, complete the spell.

balanceo, [lambda, [tree, balanced], [conde, [[rotso, tree, balanced], [balancedo, balanced]]]]

Measuring carefully and whispering to herself, Aisha encodes the tree in front of you, and asks for its balanced form:

[map, untree, [run, 1, [t], [balanceo, [tree, [quote, [[0,1],[[2,[3,4]],5]]]], t]]] R = [[[[0, 1], 2], [[3, 4], 5]]].

The tree shifts, blurs, and for an instant you perceive a forest of its balanced siblings, each one possible, but this one yours, and there it is: golden leaves arranged exactly as they were before, but supported by new, more even branches. As her spell unwinds, and the conference room re-attaches to reality, Aisha beams.

“I knew you couldn’t do anything the easy way.”

“Never have,” you admit. “Thank you, Aisha.”

“Any time,” she smiles, and from the tabletop, into your outstretched palms, delivers you a gleaming maple seed.

With sincerest thanks to Taber Bain, Zyle Cook, Brad Greenlee, Coda Hale, Duretti Hirpa, Moishe Lettvin, Dan Mess, Kit Patella, and Emily St, who contributed research, story suggestions, and feedback on initial drafts.

This work was inspired by Andréka and Németi’s 1978 The Generalized Completeness of Horn Predicate Logic as a Programming Language, which shows that logic programs are Turing-complete, as well as McCarthy’s 1955 Lisp paper which defines a Lisp evaluator (including recursive lambdas!) in terms of a handful of basic functions: car, cdr, cons, cond, and so on. It also draws on Steele Jr & Sussman’s The Art of the Interpreter. I designed several Prolog metacircular interpreters based on both of these papers, but ultimately wrote the (much more complex) interpreter here in search of concision and execution speed. The µKanren implementation is directly taken from µKanren: A Minimal Functional Core for Relational Programming, by Hemann & Friedman, though Vidrun invokes Friedman, Byrd, and Kiselyov for their work on The Reasoned Schemer. An earlier version of this work followed Byrd, Holk, and Friedman in using miniKanren to generate quines: Vidrun was to escape a trap by constructing interpreters which interpreted themselves. This approach seemed too derivative, and balancing a binary tree was an interview question which I personally bombed in 2010; it felt nice to revisit the problem with Aisha & Vidrun at the helm.

You can find a fully commented and expanded version of Aisha & Vidrun’s program here. This version includes (do ...) expressions, side effects, stacktraces, additional literals, eval, and a partial dynamic type system. In addition to balancing binary trees, it includes a tiny lambda-calculus interpreter: a sort of lisp-in-minikanren-in-lisp-in-prolog.

Read the whole story
14 days ago
Boulder, CO
Share this story

On Digital Ocean's Hacktoberfest

1 Share

Digital Ocean, the cloud services company, runs a marketing campaign every October where they give out T-shirts to people who have opened a certain small number of pull requests on GitHub for any open source projects. It seems that in previous years this has been fairly successful: many projects have gotten at least somewhat useful contributions, and some even new long-term contributors, and DO has gotten people to wear their marketing material.

This year, a lot of people are opening entirely useless PRs, some just adding space characters, in order to get the T-shirts. This has upset some of the open source project maintainers, who feel their time is being wasted: it takes time to reject a PR. I'm inclined to agree with them.

On various platforms that thrive on driving "engagement" (read: controversy and click-bait) there's a lot of discussion about this now, some of it clearly uncivil.

I don't think this is a situation where DO is clearly in the wrong, and it would be good if people didn't claim that. A more nuanced view of this seems to be in order. Here's my attempt at that.

Disclaimer: I'm not directly affected by any of this, not being a user of DO or GitHub myself.

While Hacktoberfest is clearly a marketing campaign by DO, it's also clear the they do like to help open source projects. I think that's nice of them, even if they way they're doing it has has unforeseen consequences.

There are incentives to open many PRs on GitHub, even without HF. Some employers look at job applicants' GitHub profiles, and a profile with many PRs opened seems more impressive than one with fewer. HF is merely giving more incentive for this. However, there has been much less of a problem with pointless PRs on GitHub previously. Why is HF having such an effect, and why now?

I think it's because HF simplifies the equation. The recruiting process is much more complicated than just counting PRs that have been opened, so making a thousand PRs that each only add an empty line would in fact be very unlikely to land you a job, but it might give you 250 T-shirts. While a T-shirt is not all that big an incentive to most people, it's a fine prize for very little effort. The cost/benefit equation for HF is such that it makes sense to spend a few minutes to open four PRs if you get a T-shirt for that. It may even be worthwhile to automate opening of PRs if you get many T-shirts.

This is of course the classic spam problem: if it the cost of doing something is negligible, but the likelihood of getting some benefit is high, a spammer will do the thing as many times as they can. If it costs the same to send a thousand emails as one, and the likelihood of getting a sale goes up with the number of emails sent, why not send a million or even more? The cost to those carrying or receiving the emails is not included in the equation.

With email the spam problem is currently pervasive and there's an arms race to filter spam away, and it doesn't seem like the filters are winning. It would be nice if Hacktoberfest could avoid the arms race.

It seems that this year the HF spam PR problem was triggered by a popular Youtube channel covering HF and showing how easy it is to make a PR. That trigger is, I think, not the real problem. This has been a bomb waiting to explode.

For Hacktoberfest, the equation is largely controlled by Digital Ocean. There are several things that DO could do, all seemingly simple:

  • only count PRs for projects that have opted into Hacktoberfest
    • perhaps the project could define a tag for Hacktoberfest PRs and only PRs tagged with that would be counted?
    • or have projects register to a list maintained by DO
  • require PRs to be accepted, not just opened
    • maybe follow PRs until the next Hacktoberfest starts, send T-shirts over the next year, so that it's not necessary for the PR to be merged in October
  • instead of just giving out T-shirts, also donate to one or more of the many open source organizations and foundations
    • this doesn't benefit the projects affected, but would at least mitigate the negative reactions

I'm interested in seeing what happens.

Read the whole story
22 days ago
Boulder, CO
Share this story

Git squash is not nice history

1 Share

Today I read a brilliant article about effective use of git bisect, but I disagreed with a small nuance of one of its conclusions (and, by internet law, was honor bound to write a blog post about it):

Had this happened in a code base with a ‘nice history’ (as the squash proponents like to present it), that small commit would have been bundled with various other commits. The problem wouldn’t have jumped at me if buried in dozens of other changes.

It’s true that git merge --squash obscures history; whether or not this makes a nice history is entirely dependent on the situation.

First we need to agree on background and introduce terms.

Lossless merges

Let’s say you have a git history that looks like this:

      C - D         feature/magic
A - B               main

A standard git merge feature/magic issued on the main branch results in this history:

      C - D         feature/magic
A - B - C - D       main

This is a fast-forward merge. Since the main ref is at B and B is the parent of C when we merge feature/magic into main, main’s ref is updated to point at the commit at D.

There is no loss of fidelity from the point of development. Every development commit is kept and the relationships between commits maintained.

Lossy merges

Using --squash instead of the default merge strategy is lossy: the fidelity of git history is lost. Squash, in our example, results in a new commit being added to main’s history that is an amalgam of the commits on the feature/magic branch:

      C - D        feature/magic
A - B - E - CD'    main

You can no longer see that C and D were two separate commits.

Helpful Loss

There are reasons to choose a lossy merge over a lossless merge.

There are blogs that advocate heavily for a squash workflow. Which strategy to choose is dependent on the content of the commits you are merging. The strategy chosen should maintain the principal that a commit in a mainline branch’s history should make sense on its own.

The content of our feature branch is obscured in our example above. A new example might be a feature/lossless branch that contains a refactor and a new feature that depends on that refactor:

| * (feature/lossless) feature: method is dynamic
| * refactor: method instead of global
* (main) Initial Commit

This is an example where the desired outcome is lossless: both of these commits are meaningful on their own and can be vectors for bugs. After the merge, in an ideal case, the main branch should look like:

* (main, feature/lossless) feature: method is dynamic
* refactor: method instead of global
* Initial Commit

Now, imagine a different branch – a bugfix, with only a single commit that is up for review.

| * (bugfix/lossy) bugfix: validate user input
* (main) Initial Commit

During review, there’s a typo in a comment that needs fixing. Now the branch graph looks like:

| * (bugfix/lossy) fix comment typo
| * bugfix: validate user input
* (main) Initial Commit

I would argue that the typo commit doesn’t make sense on its own. There’s no need to persist that commit into the main branch: it’s noise. It’s not a vector for meaningful error, it’s a development detail that shouldn’t leak back into the main branch. In short, a more functional history for a merge would be to use a lossy strategy:

* (main, bugfix/lossy) bugfix: validate user input
* Initial Commit

This is all an oversimplification

Of course, the example above completely ignores merge commits, repository merge strategies, and any shared agreements about the state of feature or mainline branches, code review, testing strategies, deployment pipelines, and so so (so!) much more!

Many of the blog posts on git I read make broad generalizations about the Right™ way to use some particularly controversial features of git (pull, merge, rebase, branching, commit messages …wait. 🤔 Is every feature controversial?), but the reality is that there is a lot of nuance in the world and the only right answer depends on your situation.

Read the whole story
23 days ago
Boulder, CO
Share this story

Congress Gets Ready to Smash Big Tech Monopolies

2 Comments and 3 Shares


Welcome to BIG, a newsletter about the politics of monopoly and finance. If you’d like to sign up, you can do so here. Or just read on…

Today I’m going to write about the remarkable and historic report from the House Antitrust Subcommittee, the culmination of a sixteen-month investigation into Apple, Google, Facebook, and Amazon.

First, some house-keeping. My organization is hosting a Zoom event today at noon, “Protecting Restaurants & Communities: The Case Against Dominant Food Delivery Apps.” You can RSVP here. Also, I wrote a paper a year ago titled “Ad Tech and the News” on how invasive models of surveillance mesh with monopoly power to undermine the free press. There’s a history of Google and Facebook in there, as well as the rise of surveillance based advertising inventory traded on stock market-like exchanges.

Finally, Axios reported on the enveloping scandal around cheerleading, “Bain Capital’s Cheerleading Investment Gets Ugly,” citing reporting from this newsletter. It’s increasingly difficult to be a monopolist who engages in abusive and predatory activity.

And now…

The Cicilline Report Lands

Yesterday, the House Antitrust Subcommittee chaired by Congressman David Cicilline came out with its report on large technology platforms, the culmination of a 16 month investigation into Google, Facebook, Amazon, and Apple. Like Pujo hearings of (1912-1913), the Pecora Commission (1933-1934), the Temporary National Economic Committee (1938-1941), and Emanuel Celler’s Anti-Monopoly Subcommittee hearings in the 1950s, this investigation and report is a prelude to major policy action reorganizing the economy and corporate America.

The report itself is over 400 page long; I’ve read a bunch of these reports from enforcers all over the world, this one is by far the clearest and most aggressive. The subcommittee staff went through 1.287 million documents and significant quantities of enforcement agency records, did hundreds of hours of interviews with “more than 240 market participants, former employees of the investigated platforms, and other individuals totaling thousands of hours,” and had seven hearings, including questioning the richest man in the world, Jeff Bezos.

It’s an extraordinary document, and it’s worth noting that the investigation was set up to succeed. Cicilline fought tooth and nail to make sure the investigations were serious, and that the CEOs of the four companies at hand testified. The key staffer on the subcommittee, Lina Khan, is an experienced journalist, as well as the preeminent scholar of modern antitrust thinking; before coming to the investigation, she had published a legendary law review article, Amazon’s Antitrust Paradox, which single-handedly undermined the intellectual structure of the current way lawyers and judges handle corporate power.

I spent much of yesterday reading the report. I’m going to explain what’s in it, why it matters, and why the cynics about the possibility of progress are wrong.

Monopolies, Lies and Fear

The basic thesis of this report isn’t a surprise, and consists of two basic elements. The subcommittee found that Apple, Google, Facebook, and Amazon are abusive monopolies. The report also noted that Obama and Trump era enforcers failed to uphold anti-monopoly laws, which allowed these corporations to amass their dominance.

What makes these platforms unusually dangerous is that they are gatekeepers with surveillance power, and they can thus wield “near-perfect market intelligence” to copy or undermine would-be rivals. For Apple the dominant facility is the App store, for Google it’s the search engine, Maps, adtech, etc, for Facebook it’s social media, and for Amazon it’s the marketplace, AWS, Alexa, Fulfillment, and so forth. They exploit their gatekeeping and surveillance power to extract revenue, fortify their competitive barriers, and subsidize entry into new markets.

Over and over, the report just lays into the Federal Trade Commission and Antitrust Division for refusing to enforce monopolization laws and failing to stop mergers, even when they had evidence that such mergers were anti-competitive. The four companies bought more than 500 companies since 1998. However, "for most, if not all, of the acquisitions discussed in this Report,” it says, “the FTC had advance notice of the deals, but did not attempt to block any of them." What were the priorities of the agencies? "Both agencies have targeted their enforcement efforts on relatively small players—including ice skating teachers and organists—raising questions about their enforcement priorities." Ouch.

But the subcommittee report is also a deeply political document, explicitly so. Cicilline attacks the way that these corporations finance think tanks and academics. “Through a combination of direct lobbying and funding think tanks and academics,” it wrote, “the dominant platforms have expanded their sphere of influence, further shaping how they are governed and regulated.” I got fired from my think tank after criticizing Google in 2017, so that section rings true to me. The platforms also engaged in routine attempts to deceive investigators, and the report is merciless about such attempts at deception. For instance, the committee asked Amazon for a list of its top ten competitors. The report authors noted that “Amazon identified 1,700 companies, including Eero (a company Amazon owns), a discount surgical supply distributor, and a beef jerky company." The report has multiple examples of such dissembling, from each company.

The report also notes the coercion they have imposed on commerce. It’s one of the first things Cicilline articulated last year when starting the investigation as he started poking around, how scared businesspeople were to talk to the subcommittee. Investigators found “a prevalence of fear among market participants that depend on the dominant platforms, many of whom expressed unease that the success of their business and their economic livelihood depend on what they viewed as the platforms’ unaccountable and arbitrary power.”

The report is peppered with footnotes of interviews from anonymous customers and merchants who use the platforms. Said one source, “It would be commercial suicide to be in Amazon’s crosshairs . . . If Amazon saw us criticizing, I have no doubt they would remove our access and destroy our business.” One attorney representing app developers said they “fear retaliation by Apple” and are “worried that their private communications are being monitored, so they won’t speak out against abusive and discriminatory behavior.”

It’s a sophisticated document, with sections analyzing industry dynamics among voice assistants and cloud computing, as well as presenting better data on the platforms themselves. Facebook, for instance, has over 200 million users in the U.S. just on its Facebook app, and is on 74% of mobile phones, which is new information. Amazon has in all likelihood over 50% of online sales, not 40% as eMarketer puts out. And I learned some new areas of anti-competitive activity, like Google requiring users of Maps APIs to have a Google Cloud Platform account, or credible allegations that Amazon Web Services engaged in “cross-business data sharing.”

But the key finding was that “courts and enforcers have found the dominant platforms to engage in recidivism, repeatedly violating laws and court orders,” which “raises questions about whether these firms view themselves as above the law, or whether they simply treat lawbreaking as a cost of business.”

I put up a twitter thread yesterday with all the various nuggets I found interesting, but the bottom line is that the House Antitrust Subcommittee found, with lots of evidence, that these are aggressive and deceptive predatory monopolies.

Report Recommendations: Break ‘Em Up

In terms of substance, what Chair David Cicilline was able to accomplish was to put out the most aggressive and important legislative document on corporate power and monopolies in decades, outpacing what enforcers all over the world have done (even my favorite, Rod Sims in Australia).

It will immediately empower regulators all over the world who have been waiting for the U.S. to legitimize real action against large technology platforms. The report also lays the foundation of a Biden administration’s anti-monopoly framework, should Biden be elected. Most importantly, this report re-asserts Congress’s role as the central policymaking body in America, seizing control from judges who have re-written case law in ridiculous ways, as well as slothful enforcers.

What does this report recommend, in terms of policy specifics?

Basically, Cicilline wants to fix the problem we have with big tech, make sure it doesn’t recur by changing the laws that led to it, and make enforcement better by pressuring public officials and empowering ordinary citizens themselves to enforce anti-monopoly laws. So recommendations fall into four buckets: (1) a legislative break-up and restructuring of big tech platforms to restore competition online (2) a strengthening of laws against monopolies and mergers, (3) institutional reforms to fix and fund the Federal Trade Commission and DOJ Antitrust Division, and (4) restoring the ability of ordinary citizens to take monopolists to court on their own.

The premise of this report is that the tech sector is simply far too concentrated, and so Congress will have to affirmatively take steps to de-centralize power there. Cicilline recommends passing laws that would break up tech platforms, as well as imposing rules mandating that dominant platforms offer equal access to their facilities for rivals. The report notes that there are ample historical precedents, like the financial syndication rules that I wrote about last July that sustained Hollywood’s creativity, or the Commodities Clause of the Hepburn Act for railroads, which blocked railroads from owning coal mines.

The report also suggests reinvigorating antitrust laws by reasserting Congress’s role as the central policymaker when it comes to authoring laws, which the courts have gradually encroached on. Cicilline suggests writing statutes to overrule a host of Supreme Court precedents that have unreasonably crippled antitrust laws on things like pricing below cost or abusing one’s dominant position as a platform, as well as making very clear rules on when mergers can go through and when they can’t.

(WARNING - THIS PARAGRAPH IS WONKY - Areas of law to change include monopoly leveraging, predatory pricing, essential facilities doctrine, predatory design, ending special antitrust exemption for platforms (Amex), shifting the burden of proof on mergers for dominant platforms, imposing bright line market share rules for mergers, imposing a presumptive ban on future acquisitions by large platforms, imposing a presumption that vertical mergers by large platforms are unlawful, and ending arbitration clauses and re-empowering class action lawsuits)

The report also suggests more money and skepticism towards the FTC and DOJ, as well as reinvigorating the right of ordinary citizens to sue monopolists by changing a variety of legal standards and invalidating contracts that eliminate the right to sue.

Taken together, it’s a big deal, and would reorient the American political economy so that small companies could once again compete and monopolization would once again be illegal.

So That’s the Report. Does It Really Matter?

I’ve been overflowing with effusive rhetoric so far, but it’s important to note that in terms of impact, the Cicilline Report is just a document with no intrinsic legal force on its own. It will require follow-on enforcement action at the agencies, or Congress to pass new laws. And cynics would argue, with what seems like a mountain of evidence, that the U.S. government is unable to do anything to constrain the powerful. In particular, Congress itself is nothing but a dysfunctional and corrupt mess. So in that sense, who cares? Wake me when Congress actually passes a law or does something beyond putting out a report, so goes this viewpoint.

I think this attitude of nihilism, while tempting, doesn’t stand up to scrutiny. A little over a year ago, I wrote a piece on the Congressional investigation of the large technology firms titled “Big Tech Meets Its Pecora Commission: Why Google's Toughest Opponent Is Now Congress.” I predicted two outcomes. The first was an antitrust case against Google, the second is a report from Congress calling for legislation to split up big tech platforms.

I can’t tell you how many people have rolled their eyes at my optimistic outlook in this area, but they were, frankly speaking, wrong. They would scoff at the notion that Donald Trump’s administration would bring a case against one of the most powerful corporation in America. And yet, a Google case is likely to be announced this week or next. My rationale here for making predictions was the same one as when I noted how the Obama administration would fail to constrain financial power, or in noting that the CARES Act would shift wealth and power upward. It’s what policymakers, and perhaps voters, wanted.

Why do policymakers and voters want to break up monopolies now, when they didn’t before? It’s perhaps too simple to even be accepted as a reason, but it’s actually quite simple. We the people changed our minds. To put it a different way, the norms have shifted, radically.

This happens sometimes in American history. Just twenty years ago, the idea of gay marriage was politically unthinkable, and Howard Dean was considered a lunatic for proposing civil unions. Today gay marriage is broadly accepted, and young people don’t give it a second thought. As another example, in 1995, a majority of Americans didn’t approve of interracial marriage, today that number is at 8%. Not only have our minds and attitudes changed, but people today who opposed either interracial or gay marriage don’t admit or maybe don’t even remember doing so. It just seems crazy to think that we might have thought that way. And policy changed, in line with attitude.

In some ways, that is how norms are shifting around monopoly and corporate power. Today, the anti-monopoly position increasingly seems to be the default. It isn’t that everyone agrees, but that even opponents see anti-monopoly sentiment as the conventional wisdom.

But from 1980-2016, the idea of taking on monopolies was considered fringe. For instance, in the early 1980s, The Economist wrote a profile of Ronald Reagan’s antitrust chief Bill Baxter, whose great accomplishment was to begin the shutting down of antitrust in America. Baxter fought the Supreme Court’s ‘wacko’ decisions maintaining monopolization standards, refused to enforce anti-merger laws, and dropped the major antitrust case against IBM, signaling to Wall Street and corporate America that it was time for a historic merger wave. “John D. Rockefeller would have liked a trust-buster like Baxter,” wrote the magazine.

What’s astonishing about what Baxter did is not just what he did, but that he did it with virtually no opposition. When The Economist noted how Rockefeller would appreciate Baxter’s way of doing antitrust, it wasn’t a criticism, it was a compliment. And this pro-concentration philosophy continued, unchallenged, for decades.

Even into much of the Obama Presidency, virtually no one gave a second thought to monopoly power. It’s almost impossible to remember the excitement of Facebook helping to launch Barack Obama in 2007, both progressive symbols of hope, change, and a better future, or the odes of tech platforms as they offered themselves as beacons of freedom during the Arab Spring in 2011. Many Obama alums went to work at Facebook or Google which, in their view, was continuing their good progressive work they had done at the White House. And it was pretty much taken for granted that Hillary Clinton would be appointing Facebook chief operating officer Sheryl Sandberg as Treasury Secretary. This pro-monopoly conventional wisdom is impossible to remember because it’s so embarrassing.

And this was not some elite plot among Democratic or Republican leaders, either, appreciation for monopoly power was widely held. During the Occupy Wall Street protests in 2011, for instance, hundreds of protesters at Zuccotti park offered a moment of silence to honor, upon his death, the iconic billionaire Steve Jobs (and yes, Jobs shows up as a monopolist in the Cicilline Report). There were an endless number of tweets and comments lauding the Apple co-founder, with lefty anarchist types explaining that Steve Jobs might be of the 1%, but he wasn’t like the Wall Street types they disdained. Here’s a sample tweet promoting Apple’s branding campaign.

I don’t say this to criticize Occupy Wall Street or anyone who lauded Google or Facebook, but to show that five, ten, fifteen, twenty five years ago, most of us though very differently than we do today.

To pick a few data points from this month at random, Donald Trump, no great brainiac, attacked Bloomberg’s monopoly publicly, and just tweeted that we should repeal Section 230 of the Communications Decency Act. Last week, hedge fund consultant Austin Goolsbee, a former top Obama advisor, wrote a New York Times piece with the title, Big Companies Are Starting to Swallow the World. Oh, and The Economist that lauded Baxter for killing antitrust? Today that magazine is attacking Joe Biden for being insufficiently aggressive on market power. That’s today’s conventional wisdom. In other words, it’s hard to conceptualize just how radically our politics has changed from the slothful system and intellectual environment in which we have inhabited for forty years. The norms have shifted.

One consequence of a norm-shift is that old attitudes seem almost unreal. So we forget, and pretend we always knew better. In fact, many like Goolsbee, as well as Obama era enforcers, are protecting their own reputations. Last week, for instance, Bill Baer, who was the head of the Antitrust Division towards the end of the Obama administration and constantly bragged about how well he had done as an enforcer, was forced to admit in a Congressional hearing that he had failed as an enforcer. He explained that he was weak in his enforcement choices, but that’s because “today’s debate hadn’t really begun.” And he’s not wrong about that, but if we recognized norm shifting, Baer would have to acknowledge that his expertise is largely in doing antitrust wrong.

Just as Baer comforts himself by pretending he always knew better even though he demonstrably did not, it’s comfortable for all of us to wallow in powerlessness, to pretend that we can’t act through our democracy, instead of recognizing that we just didn’t realize monopolies were a problem until recently. To do otherwise means acknowledging that our political icons - Obama, Trump, Clinton, Reagan, etc - were actually not what we thought they were. Large numbers of political thinkers find it too painful to recognize that policymakers and voters made bad choices, so instead they comfort themselves with by pretending that they can’t make any choices at all.

Of course it doesn’t help that this stuff, though incredibly fun and interesting, is hard to learn. The language of structuring corporations and banks and markets is a political language, and it has been heavily confused and polluted by the Chicago School. It’s easier to ignore political economy entirely, which is what most of our TV media does.

So recognizing such a shift is embarrassing and requires tossing away our political icons, as well as learning new political discourse. And yet, without doing so, the world makes no sense. A world where we imagine the government can’t act will increasingly diverge from reality as the government does act. It’s not just this report, or the Google antitrust suit. Last week, the Senate Commerce Committee subpoena’d the CEOs of Google, Facebook, and Twitter, with a unanimous vote, and the press barely noticed. Dislike of big tech’s power has become the conventional wisdom, so much so that it’s become boring.

The reality is that we live in a democratic society, where what the people think matters. The reason that we didn’t constrain the powerful with the rule of law is that large swaths of the public and institutional players in business, academia, and politics - including and especially powerful Democratic leaders like Barack Obama - in fact did not believe in doing so. And now, as simple as it sounds, many of these people have changed their minds.

In other words, there has been a massive shift in norms, and one can only understand the massive forces at work if can see that shift clearly.

A Time for Action

So now it’s time for action, and this report is the beginning of real action. While the subcommittee was led by Democrats, in particular Chair David Cicilline, there is Republican support for addressing monopolies. Republican Ken Buck, a conservative from Colorado, released his own additional views to the report, in which he and a bloc of fellow Republicans agreed with Cicilline’s diagnosis of the problem, though he suggested a milder set of remedies. Then there’s the leader of Republicans on the committee, Jim Jordan, who dissented from the report (with a document probably financed and written by antitrust lawyers working for Google, Amazon, and Facebook), but even he called for changes to telecommunications law.

Having multiple competing points of view on a complex problem isn’t unusual; in fact it’s the norm throughout American history. And working through these different points of view is actually how the legislative and political process works. Cicilline has laid out a clear marker, and his report represents the most likely path for legislation and action over the next four years.

But you don’t have to take my word for it. The Cowen Washington Research Group, which is a pretty orthodox investment analysis firm, has a similar view.

Our take: Given bipartisan concerns with Big Tech, we believe passage of a new antitrust statute in 2021 is quite realistic. If Democrats sweep in November, the odds of passage would rise and the specifics become more anti-platform because Democrats could well eliminate the Senate filibuster, which would reduce Republican blocking power. But even in an all-Democratic Washington, legislation to actually break up or structurally separate these American champion companies is likely to be a tougher sell. Finally, regardless of whether any legislation passes, we think the depth of this report – Congress’ first in decades on antitrust – will become the Democrats’ center of gravity on tech platforms if Biden wins. It also could provide cover (or pressure) for DOJ/FTC/state AGs to file tech antitrust lawsuits this year and next even under existing laws.

That’s why in my view, it’s hard to overstate the importance of what David Cicilline and the House Subcommittee just accomplished. This report, and the investigation upon which it sits, represent a radical shift in the American balance of power, moving back who governs from private monopolists to public institutions. It will be explosive abroad, because enforcers in other countries have been held back by American timidity. It’s also a reassertion of Congress as the central policymaking body in America, retrieving that from unaccountable judges and flaccid and bloated executive branch. I suspect that over the next four years, large technology platforms will be broken up, and policymakers in the U.S. are going to restructure our economy.

It’s hard to imagine this happening. I admit I have trouble recognizing the implications of the predictions I’m making. Then again, it was hard for most of us to imagine President Trump, until President Trump happened. The bottom line is that if we refuse to recognize the reality that norms in our society have shifted massively over the past four years, we are leaving out key political evidence that can help us understand the world we are entering.

Thanks for reading. Send me tips, stories I’ve missed, or comment by clicking on the title of this newsletter. And if you liked this issue of BIG, you can sign up here for more issues of BIG, a newsletter on how to restore fair commerce, innovation and democracy. If you really liked it, read my book, Goliath: The 100-Year War Between Monopoly Power and Democracy.


Matt Stoller

P.S. Hey antitrust lawyers, here are some job options in the Department of Justice Antitrust division. Apply!

Trial Attorney (Digital Markets Counsel)

Trial Attorney (Appellate Division)

P.P.S. I got this super-sharp email from a high school student, based on the last issue I wrote where I mentioned the edtech software monopoly Clever.

Good evening,

I'm a high school student in North Carolina, and I always enjoy reading your newsletter. When I saw that you had mentioned Clever, I felt the urge to write to you about it. In my school district, we use both Clever and Google Apps for Education. In general, the educational space seems to be full of monopolies and duopolies. From what I can tell, almost all school districts contract with Microsoft or Google for their email and technology. This locks them into an ecosystem of Chromebooks or Surfaces and dictates most of the rest of their technological choices. That's all well known, but one problem that I experience and don't see discussed is how this facilitates the usage of too much software and disregard for student privacy. In any given week, I often have to use ten or fifteen different applications across my different classes. I sign into most of these through Clever or Google OAuth, and teachers do too. As a result, no one seems to stop and consider how many different companies have our data or what that means for students. I'm not sure whether that fits into anything broader you want to write about educational monopolies, but that's just how this one affects me, so I thought it might be helpful. You're probably aware of the various educational monopolies, but I'll list some because I imagine it can be difficult to find them all from the outside. The obvious ones are the College Board, the Common Application, and Pearson, who owns PowerSchool, a grade management platform, but I also see one in Naviance.

While I'm writing to you, I thought I'd also mention another monopoly I see. I don't know much about this one, but I also see a growing monopoly of government information in Municode, a vendor for municipal codification and code management applications. They don't yet hold a monopoly, but with the no-cost model and consistent growth, I see it coming.

I hope that some of this is of use to you.


Benjamin Knight

The kids are alright.

Read the whole story
20 days ago
Not an unbiased source but interesting
Washington, DC
23 days ago
Long, but worth reading in full.
Boulder, CO
Share this story

Bigger Problem

1 Share
Your point that the world contains multiple problems is a real slam-dunk argument against fixing any of them.
Read the whole story
25 days ago
Boulder, CO
Share this story
Next Page of Stories