Hardscrabble 🍫

By Maxwell Jacobson

See also: the archives and an RSS feed

2017 in television

January 3, 2018

Here’s my favorite shows that aired in 2017. Just sharing because I spent way too much time this year watching television, so maybe this will help you pick the good stuff, as long as your taste is also my taste, which it’s not. I’ll try to avoid spoilers.

10. Mr. Robot (s3)

This show remains a technical wonder. It has a very sharply drawn aesthetic – you know you’re watching this show by the mannered acting and the off-center camera compositions more than anything else. Nothing I saw on television was as cool as the 4th episode, which is presented as one long take, and aired without commercials. It feels like the rules don’t apply to this show. This season was mostly about pain and regret and what to do with them. Anything productive? Maybe it’s possible.

Rami Malek is and has been great, but this season was as much about the constellation of characters around him. This show knows it’s fun to watch smart people do hard things and so it drew up a bunch of them, made you love or fear them, and set them against each other. I particularly liked any scene with Dom, Grant, Darlene, or Angela.

Highlight: eps3.4_runtime-error.r00

9. Crazy Ex-Girlfriend (part of s2 / part of s3)

I’ve loved Rachel Bloom since I Steal Pets, a truly brilliant, stupid music video she made in 2011. Somehow she’s made a show that has several hysterical songs in each episode, a cast of lovable gentle Californian weirdos, and a very thoughtful depiction of what it’s like to struggle with mental health. It really feels like an auteur work in the sense that it’s hugely personal and no one else could’ve or would’ve made it. She writes and acts and sings. It’s nuts. I giggled like a child all through “I Go To The Zoo” and “The First Penis I Saw” and felt a swell of an uneasy hope during “My Diagnosis”.

Highlight: Josh Is A Liar

8. Catastrophe (s3)

This show is mostly special beacuse it’s very funny, and there are few pleasures comparable to Sharon and Rob making fun of each other so cruelly that you know they must really love each other or how else could they put up with that? It’s also one of the best, most real-feeling stories of alcoholism that I’ve seen. It reminded me, at times, of a crime drama like Dexter where the anti-hero has a secret and we’re for some reason invested in him not being found out as a serial killer, except here he’s sneaking drinks, and you feel it in the pit of your stomach that this is bad. It’s mostly very funny. But it’s also very dedicated to arguing that we’re better when we step up and be there for each other even when it sucks and I find that helpful to think about. The drama is very ordinary but the characters are so lovable that you care. And Carrie Fisher is great.

Highlight: Episode 6

7. The Marvelous Mrs. Maisel (s1)

Just watched this one over Christmas break, with my mom, so there may be some recency bias here, but I really loved it. This show entertains on so many levels. It’s hysterical, with great characters, acting, and dialogue. The scenes between the comedian characters in particular feel funny in a way that funny people are with each other, where they want to skip past the norms of what they’re supposed to say, and then also not laugh, and then also act like that was normal. All the scenes with Midge and Lenny Bruce were golden like that. And just watching a show about a person figuring out they want to do some kind of art and that they might be good at it and then realizing it’s going to be a lot of work and maybe not that fun all the time is hugely fun, because each step feels real and earned and satisfying. That that person is a Jewish woman with two kids in the fifties in Manhattan played by someone as charismatic as Rachel Brosnahan makes it feel very unique and alive. Also her parents are hysterical and Joel sucks in such a real and lived in way that it almost brings pleasure how reliably the dude sucks. And that the cinematography is frequently dazzling, with a camera that floats through clubs and apartments and over tables and around Hora dancers, and the soundtrack crackles with sometimes on-the-nose but very charming contemporary songs makes the whole thing just kinda whizz by.

Highlight: Because You Left

6. The Americans (s5)

Maybe I’m a sucker and a fool for things that are just the slightest bit unusual, but the premier’s exhuming sequence, which has no dialog for something like 15 minutes as we just watch these people do their job ultra competently and feel the weight of it on their backs and the amount they’re stuck with the decisions they’ve made and have been made for them growing and growing … is very good … and is enough to make me sit up straight and hold my breath.

This is the penultimate season and it kinda feels like one. This is the TV show version of a clenched jaw. It’s all heading to hell, for sure. I think of it as a show about marriage and parenting, which isn’t really an original way to think about it. But it really makes me feel the painful feeling that maybe for all our/their hard work, the next generation won’t really be better off. On that theme, the new characters of Tuan and Pasha were fascinating and painful to watch.

Highlight: Amber Waves

5. The Leftovers (s3)

The final season! This show was based on a book, but they pretty much covered it in the (pretty good) season one. Seasons two and three veered off and did their own thing and explored grief, mental health, love, religion, and family using some of the most bizarre scenarios with the most committed cast. It’s a really stunning show just to look at even if you don’t have any idea what’s going on, which you mostly don’t. Justin Theroux and Carrie Coon are incredible.

I don’t really need things to stick the landing perfectly; Lost’s ending wasn’t perfect but it was fine IMO. The Leftover’s is another Lindelof joint, and it does feel like Lost is hanging over it a little. He did a better job of managing expectations this time, because I don’t think anyone watching expected the show to start making sense right at the end. Nevertheless, the ending had me spellbound.

Highlight: It’s a Matt, Matt, Matt, Matt World

(for the submarine sequence if nothing else, but also the rest)

4. Better Call Saul (s4)

I did like Breaking Bad a lot and I didn’t really know what to make of this when it started. It’s a little hard to talk about. It’s really its own show, distinct from Breaking Bad. When I say that the whole show was building to this season, that doesn’t mean what you think it means if all you know is it’s the Breaking Bad prequel show. More than that it’s a family drama about a handful of lawyers, all proud, all brilliant, each kind of broken in their own way. Like Breaking Bad, it’s about how people’s consciences rot and fall away, or don’t, and the effects on other people. It’s pretty heavy, but in a way that feels real and awful. It’s also, frequently, hysterical.

Highlight: Lantern

3. The Carmichael Show (s3)

Really sharp writing and amazing chemistry from the cast. Nothing else made me laugh as often. I loved the sneaky emotional ones, too, which is almost all of them. Each character gets to take their turn being the asshole taking the overly harsh position on whatever the argument of the week is, and actually gets a chance to speak their mind, and then they keep digging until they find some understanding. It’s a very winning formula. I’m sad this one ended.

Highlight: Cynthia’s Birthday

2. Nathan For You (s3)

Nathan For You is audacious and radical and sweet and kind of cruel and depressing. I hope he does more. It’s so unpredictable, but in unpredictable ways. Most of the funniest moments come from Nathan violating some social norm, but the surprising thing is how polite he is as he does it. I often feel terrible for his subjects, except that it’s very hard to find fault in Nathan’s behavior. He’s never really ridiculing anyone. He’s more enabling people to succeed at whatever their dreams are, if only temporarily. That their results are uniformly terrible makes their dreams feel small and unimportant and them seem foolish for even having them. But maybe it’s better to live your dream than to not?

Highlight: Finding Frances

1. Halt and Catch Fire (s4)

This was the fourth and final season and it was just about perfect. The episode called “Goodwill” is one of the most beautiful things I’ve ever seen on television. What this show did better than any other is make you really love its characters and care about them and feel for them. That it was about the early days of the internet and felt real and lovely is only extra credit. That it told the story of Cam and Donna struggling and having success as women in tech, also extra credit, and I think not talked about enough, IMO.

Highlight: Goodwill

Honorable mentions (alphabetical ordered)

  • Black Mirror (s4)
  • Bold Type, The (s1)
  • Brooklyn Nine-Nine (s5)
  • Fargo (s3)
  • Fresh Off the Boat (s4)
  • Girls (s6)
  • Good Place, The (s2)
  • Master of None (s2)
  • Mindy Project, The (s6)
  • Scandal (s7)
  • Search Party (s2)
  • Sweet/Vicious (s1)

Also enjoyed (alphabetical ordered)

  • Broad City (s4)
  • Curb Your Enthusiasm (s9)
  • Game of Thrones (s7)
  • Glow (s1)
  • Handmaid’s Tale, The (s1)
  • Legion (s1)
  • New Girl (s6)
  • Rick and Morty (s3)
  • Stranger Things (s2)
  • Unbreakable Kimmy Schmidt (s3)
  • Veep (s6)
  • You’re the Worst (s4)

the hardware and software I use (2017)

December 21, 2017

I’m an inveterate reader of Uses This style articles and I’ve always wanted someone to ask me to participate in one and no one has, and I have a perfectly fine blog, so here goes nothing (actually something quite self-indulgent and in need of editing and unlikely to be an annual tradition now that I know how weird it feels to write this all out).

I’m borrowing the questions from that website (thanks).

Table of contents, because this got long, but I guess that’s because I really like using hardware and software, which is true, and that’s a good point when you think about it

Who are you, and what do you do?

Yes hello I am Max Jacobson, I’m a software engineer who makes web apps, currently at Code Climate. Occasionally, and not professionally, I write, speak in public, and podcast.

What hardware do you use?

I have too much and want to have fewer but I’m not sure what to not use. I try to make my hardware last as long as I can and avoid indulging the guilty thrill of buying new things, with mixed success. By frequency of use:

I use my iPhone 7 constantly. It’s great. I like the funny vibrating home button and don’t really miss the headphone jack. I got the AirPods for my birthday and they’re great, although they’ve turned me into someone who gets nervous stepping over grates in the sidewalk. I use it in a Smart Battery Case which is fine, although I feel very warped by it because I start feeling a little stressed out as soon as my phone’s battery dips below 100%.

At work, since March 2017, I use a Lenovo ThinkPad P50 which is enormous and heavy and has a numpad built in that I never use and that little red nub for mousing that I never use and a hinge that curiously opens to a full 180 degrees which I never do and a second set of mouse buttons above the track pad which I never use and a fingerprint scanner that I never use. I didn’t exactly pick it out: when someone left the company I took it over and turned in a MacBook Pro so I could try out switching to Linux (more on that later). I do like it: it’s super fast; the keyboard is good; the screen is sharp; it has all the ports you could want; the actual thing feels pretty good; and once your eyes adjust, its stark black and red aesthetic starts to look kind of slick. I’ll probably pick a smaller ThinkPad for my next laptop.

I use it with a WASD V2 87-Key Custom Mechanical Keyboard, which is awesome and a $5 AmazonBasics USB Mouse which is perfectly fine. I’m not in the camp of people who detest wires on a desk. If anything, I’m in the camp of people who detest having to charge things.

Next up is the 2016 9.7” iPad Pro. I mainly use it to do things like watch videos in bed or read Twitter on the couch. Occasionally I’ll take it out to a cafe with the Apple Magic Keyboard so I can do more productive stuff like write emails or research some project, where I appreciate being able to command+tab to switch between apps or command+space to quick-launch apps. I recently bought a Canopy keyboard case to kinda encourage me to do that more, and hopefully I’ll like that. (Edit: I did)

I like having it. It lets me give my phone a break. It’s so light. It has such a nice screen. Its speakers are surprisingly loud and nice-sounding. It’s fun to use and to look at (I have the pink one with the mint green smart cover).

For a personal, non-iPad computer, I used to have a 15-inch, Early 2011 MacBook Pro, but it died in early 2017. My grandma bought it for me after college when I was trying to become a filmmaker. It ended up being the computer where I learned to code instead. I put that machine through hell trying to keep it going, and I struggled for a while figuring out what to replace it with. I probably would’ve gotten a spec’d out MacBook pro except that all signs suggested they were going to release a big new update soon. They ended up doing so, but not until November, and it was kind of a controversial new design which I’m glad I didn’t wait for.

I ended up getting a 2015 Dell XPS 13 Developer Edition in November 2015, actually awhile before the MacBook Pro formally stopped booting. I was looking for a light laptop that I could travel with (by this point the MacBook Pro felt like a brick in my bag and had a few loose screws clattering around in it which left me preferring to keep it at home), and also to experiment with using Linux. It cost $1,370. I like this laptop fine. It’s light and fast and runs Linux and has a decent battery and a nice screen. I think it’s weird to have the webcam positioned below the display instead of above. The keyboard is only fine. It feels so sturdy and compact that it doesn’t feel fragile and I toss it in my bag without a case. This is the laptop I brought to India and studied Rust, and later brought to New Orleans to present at RubyConf. It’s reliable and straight-forward.

But when my MacBook Pro died, I did kind of feel like I needed a Mac. I had salvaged the harddrive from the laptop and bought a drive enclosure. I also had them backed up to another hard drive and to the cloud, via backblaze. All those backups cried out to be restored, somewhere.

I ended up getting a Late 2014 Mac Mini spec’d out with 16GB of RAM, an SSD, and whatever the fastest processor was. It cost $1,399. That’s a weird computer to choose in early 2017. Even in 2014, it was really poorly received because it didn’t offer all the options that the previous generation did and wasn’t as upgradable after purchase. I was kind of thinking it’d be a stop-gap until the new MacBook Pros came out, and then I’d sell it and get one of those, but then those came out and were not very appealing to me. But to be honest, I kind of love it. In my experience, it’s plenty fast and reliable. I almost bought a Mac Pro and it turned out the Mac Mini was enough for me. Lol. I don’t use it a ton: I’m on a computer enough at work and don’t thrill at the idea of spending much more time on one. But I still like to have a Mac somewhere in my life, to serve as a hub for things like my photo library, my music library, and all my old college essays and writing projects. And as much as I’ve come to like Linux, there remain a few things that I need a Mac for (more on this later).

I use it with another AmazonBasics mouse and a Spacesaver M White Buckling Spring keyboard from Unicomp. It’s hilarious and thunderous and retro and great. I love it.

For sound, I use Altec Lansing BXR1220 computer speakers I bought for $15 five and a half years ago which are perfectly fine and look pretty cool IMO.

For a display, I use a Dell U2713HM 27-Inch Screen LED-lit Monitor that The Wirecutter recommended when I bought it in 2014. It’s been really great.

For recording audio, I use a MXL Tempo USB Desktop Cardioid Condenser Microphone. I was going through a phase where I enjoyed red things. It wasn’t a huge investment, is simple to use, and sounds OK.

For Wi-Fi, I switched to Eero in 2016 to get better coverage of my apartment and it worked out great.

I watch TV and Movies using an Apple TV (which is fine) on a TV that my friend Russ handed down to me when his aunt handed hers down to him (which is fine).

When I write longhand, it’s usually on some cheap Gregg-ruled steno pad I picked up at a pharmacy (I like the spiral being at the top so it stays away from my wrists; I like for the margin to be right down the middle so I don’t feel weird writing right up to the edge of the page, and it also gives me a sense of how far across the line I’ve gotten at any given point? Maybe I don’t need the middle margin actually) using a Muji 0.5MM Gel-Ink black pen which my sister recommended to me once, years ago.

For reading books, I use the library.

I have way too much hardware, but at least I don’t have an Apple Watch.

And what software?

I try not to use too much software, because the more things I use the more keyboard shortcuts I have to remember, and the less room in my heart there is for poetry. I try to use built-in software when possible unless it’s really bad.

For programming a computer, I primarily use Ruby or, for simple things, shell scripts. I also like to use Rust.

For writing code, I prefer to use terminal-based tools, primarily: vim for editing text, tmux for terminal multiplexing (creating separate workspaces for separate projects, each consisting of a few related shells, controlling how they’re laid out and which to focus on), zsh for a shell, and git for tracking changes to source code. I like them because they’re free and open source, blazing fast, and have user interfaces that feel like they’re carved from stone. There’s a long tail of unix tools that assist in the process of writing and testing code, but I’m going to consider them out of the scope of this post. Thankfully, those all work pretty much the same on both macOS and Linux.

My dotfiles are available on GitHub. They use thoughtbot’s lovely rcm tool to ease installation and syncing across multiple machines.

On macOS, I use the built-in Terminal terminal emulator and on Linux I use rxvt-unicode.

For an operating system on my two laptops, I initially tried Arch Linux at the recommendation of a few co-workers and I’ve come to quite love it. It has a deserved reputation for being ultra minimalist, which means you have to do more legwork to get it up-and-running, but then you can customize it exactly to your taste. It’s way more bare bones than I could ever have imagined. If you want it to behave in any way at all, you have to tell it to – even for super basic things like locking the screen after a few minutes of inactivity – but it has all the seams in place for you to do just that. I initially set it up using this fabulous tutorial from LearnLinux.tv and have iterated on it via a lot of guidance from my patient co-workers and by copious browsing of the elaborate ArchWiki.

For managing windows on my laptops, I’m using xmonad, a tiling window manager. In the past I’ve tried macOS apps that add keyboard shortcuts for managing windows in a tiling fashion (Spectacle, I think, and others) and never found them to be particularly compelling. I thought things like: I’ve always arranged my windows using my mouse and it’s been fine; I don’t want to learn a bunch of new keyboard shortcuts; etc. But depite those earlier fears and protestations, xmonad is absolutely wonderful. I think I like it more because it’s not a layer on top of a dynamic window manager, it’s the whole system. I set it up so all of my windows get chunky, hot pink outlines. My screen is always 100% filled. Windows get automatically resized to fit as I open and close things. I can re-arrange and navigate the windows without ever using the mouse. It’s cool as hell. It’s also fast as hell.

For browsing the web, I’m a stubborn Firefox apologist and have been for a while. It feels like the internet to me. I even use it on iOS, so my history and bookmarks will sync there. Unfortunately, I end up in Safari for iOS all the time, since that’s the default browser for everyone.

For email, contacts, and calendar I use FastMail. It’s rock solid, has super fast and pleasant web interfaces, and has no ads. I pay $70 per two years for it. I love it.

For email, I use the built-in Mail app on iOS, which works great with FastMail. I’ve tried a handful of alternatives and didn’t really like any of them, and you can’t change the default app to handle email links anyway so I just go with the flow. On macOS and Linux, I just the FastMail web interface, which is great.

For interacting with my personal calendar on macOS and iOS, I use Fantastical which is very delightful.

For buying domains and managing DNS, I use NearlyFreeSpeech.NET. It’s one of my favorite websites. It’s extremely plain and extremely polite and extremely clear.

For lightweight checklists, both long-lived and short lived, I use the Apple Notes app. For example, I have a note called “movies out” which is a checklist of movies that are out or coming out soon that I think I might want to see. I refer to that occasionally when I think “hmm what’s out?” or when I pass a poster and think “Oh, that’s out?” I have another note called “pantry” which has a checklist of the staples I like to keep in my kitchen, and I check things off when I buy them, and uncheck them when they’re running low. I have another note called “Christmas gifts” which lists all the people I need to get gifts for, with checklists for each person of the things I’ve gotten them (checked) or am thinking about getting them (not checked). I’ll delete that one after Christmas. Sometimes I’ll make a note that just has a checklist of all the things I’d like to get to in the day, and I can look at it throughout the day, and then later on delete it.

For being upset and inspired and not-bored and informed about the world, I use Twitter. It’s a big part of my day. I can’t really imagine the world without it. I used to use it exclusively via a third-party app called Tweetbot, but I switched to the official iOS app and it’s honestly fine. I do see ads now, and I lose some neat features and design, but more importantly I get all of the modern twitter features, like polls and group DMs, which aren’t available to third-party apps. On Linux and macOS, I just use the web interface.

For subscribing to websites and newsletters, I use Feedbin. I still love RSS in 2017. It’s a big part of my day. Whenever I read anything I like on the web, I look for a feed so I can subscribe and get more. Also, whenever a newsletter seems interesting, I subscribe via feedbin rather than via my email, which helps me prevent my email inbox from getting cluttered. On iOS and macOS, I read via Reeder, which probably comes second only to Firefox as my favorite and most-used app of all time; I was browsing Google Reader via Reeder on an iPod Touch between classes in college. On Linux, I use the Feedbin web UI, which is actually really nice. On iOS, to detect RSS feeds on web pages and subscribe to them in Feedbin, I use Feed Hawk.

For hosting source code, I use GitHub. It’s great.

For making my blog, I use Jekyll to structure the source code and build the site, GitHub Pages to host the static site, Markdown to make it pleasant to write prose that will become HTML, and Clicky for some traffic analytics.

For creating slides for my one talk I gave in public, I used remark, an open source tool that let me use familiar web technologies like HTML, CSS, and JavaScript to customize the slides, and let me use my beloved Markdown for writing the actual content, and let me easily host the finished product on my blog.

For editing my podcast, I use … actually I do it so infrequently that each time I basically forget and try something new, and I currently don’t remember.

For listening to music, I use Spotify although I kind of wish I just paid for music again so I didn’t feel a mounting dread about how much money I’ve sunk into something I don’t get to keep. It has good native apps for all the platforms I use. Sometimes I use the Apple Music app for things that aren’t on Spotify, and other times I listen to musicforprogramming.net.

For listening to podcasts, I use the wonderful Overcast. I love to have a podcast app that does server-side polling and sends push notifications when new episodes are available. I generally use the iOS apps, but I’m glad it has a spartan web app I can use on Linux and macOS.

For tracking personal tasks, I use OmniFocus, which is available on all of the Apple platforms. I use it pretty passively. It’s more a thing I write to than read from. If someone recommends something, I’ll put it in OmniFocus. If I see a tweet with a link I want to check out later, I’ll put it in. If I have a random thought I want to explore further, I’ll put it in. If I feel guilty about something, I’ll put it in. Occasionally I’ll go back and look through it and check things off and delete things and organize them into little projects. It helps me remember what are all the things I want to or am supposed to do, which helps me not feel worried all the time, and when I do feel worried I know where to go to remind myself who I am. I used to use Instapaper for saving articles to read later, and this year I stopped, and it’s a relief. But I did make a single action project in OmniFocus called “articles to read later”, and I do occasionally put articles in it. It’s a little deranged. I dearly wish they had a web version so I could check it on Linux.

For tracking work-related personal tasks, I use Todoist, which is pretty similar to OmniFocus, except it feels less reliable to me, has subscription-based pricing, and has a web version so I can check it at work on my Linux computer. I actually kind of like keeping a divide between work stuff and personal stuff. I just wish its syncing engine felt more rock solid.

For keeping files in sync across my goofy amount of computers, and occasionally for sharing files with other people, I use Dropbox. I’m tempted to become more reliant on it. My photo library is currently in Apple’s Photos app, which I can’t access on Linux. That might be a project for next year.

For managing my personal passwords, I use 1Password. I’m very pleased because they recently introduced a web version, which should let me use it on Linux, although I haven’t tried yet. I think I may need to switch over to subscription pricing to use that, which would totally be worth it for that alone. Currently, whenever I need to look up a password on my personal laptop I just look it up on my phone and peck it in, which suuucks when your passwords are super involved. For my work passwords, I use Rooster, a CLI password manager.

For texting I use some combination of iMessage, WhatsApp, Facebook Messenger, Twitter DM, Instagram DM, and Slack depending on who I’m talking to. It’s a mess. And I’m just realizing three of those are owned by Facebook. In theory, I prefer Twitter and Slack most, because those are available on all of the operating systems I use. In practice, I use iMessage the most.

For making screencasts, I use QuickTime to record my screen and ScreenFlow if I need to do any editing. I have used recordmydesktop to record my screen on Linux and it works great, but I never really do it. And I have no idea how to edit video on Linux, although I’m sure it’s done.

For remembering where I’m at in which TV shows, I use iShows TV on my phone. I love it.

For figuring out where to go and planning trips, I use Foursquare.

For giving and receiving FOMO, and helping me remember later the names of the places I’ve been, I use Swarm on my phone.

For some twitter analytics, I use Birdbrain on my phone.

For reading comics, with cool panel-by-panel transition animations, I use Comixology, mostly on my iPad.

For tracking the shipping status of packages I use Deliveries on macOS and iOS.

What would be your dream setup?

Hmmmmm.

I wish that all iOS apps I liked had at least spartan web interfaces so I could interact with their data from my Linux computers.

I’m looking at you, iMessage and OmniFocus.

I wish Vimscript were replaced with Ruby.

I wish there were less lock-in.

I wish I could use iOS more like a general-purpose computer. I know some people can, but I don’t think I can until I can do things like:

  • change the default browser to Firefox so I can follow links in, for example, my email, and have them open the Firefox app
  • run a terminal emulator that gives me access to the actual file system and run arbitrary software and have access to a package manager such as Homebrew
  • not rely on a separate Mac to add arbitrary mp3s to the Music app
  • do things like invoke 1Password from Firefox without going into a share menu – using “sharing” as the way for apps to communicate feels like the wrong abstraction
  • probably other stuff

I should probably get better speakers.

I have a kind of allergy to using Google products that I should probably get over, because they do make a lot of good stuff.

I wish more people had blogs and fewer people had newsletters.

Normalizing surgical drain output

December 11, 2017

Let’s talk body fluids, and then math.

(Note: this isn’t medical advice. Listen to your doctor.)

During a surgery, it’s sometimes necessary to install a drain to prevent the build-up of fluids under your skin from whatever wound you’ve got. When the patient leaves the hospital, it becomes their responsibility to care for the drain. Here’s what that entails:

The drain is a tube connected to your body via some stitches. It runs along until it empties into a bulb, which you may keep clipped to your undershirt throughout the day. The bulb has most of the air squeezed out of it, to create suction.

Periodically, you must unplug the bulb and let in the air. Pour out whatever fluids have collected into a small measuring cup. Make a note of how much you’ve collected, and what time it is. Then dispose of the fluids, squeeze the air back out of the bulb, and plug it back up. Do this at least twice a day.

When you see your doctors, they’ll want to know the rate of drain output so they can get a sense for:

  1. how the wound is healing
  2. if it’s time to remove the drain

Let’s say you’ve taken these notes:

2017-12-07 00:00 0
2017-12-07 14:09 30
2017-12-07 22:10 10
2017-12-08 10:20 7.5
2017-12-08 10:55 23
2017-12-08 22:00 2.5
2017-12-09 11:45 5
2017-12-09 19:15 8
2017-12-10 11:50 18
2017-12-10 17:40 7
2017-12-11 8:55 10
2017-12-11 22:10 12.5

Some days you’ve taken two measurements, and other days three. Some of the measurements follow six hours after the previous one, and some much more.

Let’s say the doctor wants to know the answer to this question:

How many milliliters of serosanguineous fluids did you drain each day since your surgery?

We can do some eye-ball math and determine:

2017-12-07: 0 + 30 + 10 = 40
2017-12-08: 7.5 + 23 + 2.5 = 33
2017-12-09: 5 + 8 = 13
2017-12-10: 18 + 7 = 25
2017-12-11: 10 + 12.5 = 22.5

And that would probably be good enough. It gives you a sense for the trend:

40
33
13
25
22.5

Slowly going down, except for one weirdly quiet day.

But IMO this feels dissatisfying and wrong.

Let’s look at these two measurements again:

2017-12-09 19:15 8
2017-12-10 11:50 18

Is it really fair to bucket those 18 milliliters of serosanguineous fluids solely on 2017-12-10? You emptied the drain at 19:15 the day prior, so those 18 milliliters were trickling out for about five hours on one day, and about twelve hours the next. If we can assume that it trickled out evenly, we should be able to smear that data across both days and get a more accurate picture of the daily trend.

This bugged me enough that I wrote a quick ruby script to do this for me:

require "time"

Measure = Struct.new(:time, :value)

data = File.read("./data").lines.map { |line|
  date, time, amount = line.split(" ")

  Measure.new(
    DateTime.parse(date + " " + time).to_time,
    amount.to_f,
  )
}

all = [data.shift]

raise "no data!!!!" if all.empty?
raise "must start with zero value!!!" if all.first.value.nonzero?

data.each do |next_data_point|
  last_data_point = all.last

  amount = next_data_point.value
  diff = (next_data_point.time - last_data_point.time).to_i

  amount_per_diff = amount / diff.to_i

  diff.times do |n|
    all.push(Measure.new(
      (last_data_point.time + n),
      amount_per_diff,
    ))
  end
end

result = all.each_with_object({}) do |measure, obj|
  obj[measure.time.to_date] ||= 0
  obj[measure.time.to_date] += measure.value
end

result.sort_by(&:first).each do |(date, total)|
  puts "#{date} - #{total.round(2)}"
end

I hacked this together pretty quickly and I’m a little pleased with it. Here’s the idea:

Instead of having just a few measurements taken at odd intervals, let’s pretend we have many thousands of measurements taken at regular intervals. One per second, in this implementation. And then use code instead of dumb eyeballs to add up all the measurements in each day. I’m calling this smearing the data for want of a proper term.

After smearing the data (assuming an even trickle between measurements), the trend looks like this:

41.13
32.6
17.43
24.0
18.35

In this case, it’s so close to the eyeball math that it may not have been worth doing, but I will rest easier nevertheless.

I just have one urgent question for you my dear reader: is there a name for what I did here? I want to learn more about data. As I learn things, I am often pleased to find out that all of the ideas already exist and have good names. What’s this one’s?

mewithoutYou and me

September 29, 2017

mewithoutYou is a fairly prolific rock and roll band from Philadelphia who I love very much. In this post, I’m going to talk a little bit about why and share some of my favorite songs.

Catch For Us The Foxes (2004)

I first heard mewithoutYou in probably 2004. I was going to see my then (and current) favorite band Bear Vs. Shark play a club in Poughkeepsie called The Loft. There were two openers, Codeseven and mewithoutYou, neither of which I had heard of. I’m sure I probably pirated both of their albums, so I could be prepared. I’m not sure if I ever got around to listening to Codeseven…

I remember sitting on the couch in my parents house with headphones on (did I have an iPod? did I burn a CD for an album I wasn’t even sure I liked?) and being fully knocked on my ass.

The album, Catch For Us The Foxes, is their second. It starts with a song called Torches Together. Immediately… who the hell is this guy? What is he talking about? Why isn’t he singing? This dude is out of control spazzing out while very comfortably weaving a metaphor about, I think, the power that comes from letting go of personal ego and joining a community? Individual lines jump out: “Anyway, aren’t you unbearably sad?” Just like that, as an aside. Later: “And I’m afraid and everyone’s afraid and everyone knows it / But we don’t have to be afraid anymore.”

I recently read a brief profile of chef and cookbook author Meera Sodha. She has this great line:

You know when you realize what you’re eating is just so magnificent, and there’s a sort of rip in the atmosphere?

Torches Together ripped into my 16-year-old atmosphere.

Anyway, Catch For Us The Foxes isn’t my favorite album of theirs. Maybe my least favorite. It has a lot of nice moments, but it’s not very focused. Stylistically, I think it reaches the limits of the spoken (and shouted) word style. It gets a lot of mileage from the contrast between the band (who play very precisely) and Weiss’s unhinged vocals. He sounds, often, distressed, and the music behind him almost feels like it’s trying to keep up with him and support the performance like a movie or broadway score. Imagine, if you were distressed, and you broke down, and as you expressed your feelings, some chunky riffs and a tight rhythtm section buoyed you, and encouraged you to keep going. That might be kind of powerful and you might feel kind of validated.

The style is definitely not for everyone, but I love it, and that’s sort of the lens I see it through. Maybe that’s how all bands work, I don’t know.

[A->B] Life (2002)

After the concert (which was amazing, I can’t believe I got to see Bear Vs. Shark and mewithoutYou in the same night) I went back and listened to their debut album, [A->B] Life.

Honestly, I think this is is a really good album. It’s not really trying to be that profound. It’s mostly trying to rock out. It’s short. It moves fast. Weiss pretty consistently screams all his lyrics. There are a few spacey interludes, but overall it’s very crunchy. Track eight, “We Know Who Our Enemies Are”, has a kind of hilarious moment where it fades out and then back in for some reason. It ends with a secret acoustic track, so you know they have a sensitive side.

There are a few songs that are more on the snotty and abrasive side IMO (“I Never Said That I Was Brave”). But a lot of them are (relative) bangers, like “The Ghost”:

Can you even imagine how fun it is sing lines like “Put music! Put music! Put music to our troubles!” along at a concert?

Look, things aren’t going well, and there are simple pleasures to be had. Consider air-guitarring to the little guitar solo at the end there.

This year, in 2017, they’re doing a 15 year anniversary tour where they’re playing the album in full. I’m obviously going.

Brother, Sister (2006)

So album three comes out and I’m officially a fan. I’m also in college now, and sophisticated. I order it on vinyl and put it on my wall (I do not have a record player).

I think this is their first great album. The corner they turned is that Weiss has started to transition from performing anguish to telling stories. He’s still kind of freaked out by everything, but he doesn’t quite as angry anymore. He’s coming around to being gently amused.

Here’s “The Dryness and the Rain”:

My favorite moment comes near the end:

A fish swims in the sea
While the sea is in a certain sense
Contained within the fish!
Oh, what am I to think
What the writing
Of a thousand lifetimes
Could not explain
If all the forest trees were pens
And all the oceans ink?

What am I to think?

Not for the first time, many of their best lines are cribbed from or inspired by The Qoran and the Sufi Islam teacher(s?) Weiss is obsessed with.

The next song, “Wolf Am I! (and Shadow)” is maybe my favorite of their hard rock songs:

I just think the band sounds so tight and good. But also, Weiss finds room for some spontaneity in a way that I find pleasingly self-deprecating:

Oh there, I go showing off again
Self-impressed by how well I can put myself down!

Later, he corrects himself in a way that almost feels like he’s making all this up as he goes:

Shadow am I!
Like suspicion that’s never confirmed
But it’s never denied
Wolf am I

No, “shadow”, I think is better
As I’m not so much something
More like the absence of something

So shadow am I!

Of course, I hope, he’s not just making it up as he goes. But it does play like an effective performance of confusion and self-loathing and fear.

This might feel a little like more of the last album, but it’s redeemed by two things:

  1. it just sounds so good
  2. it’s in the context of an album that isn’t just that, but seeks and finds some level of grace by the end of it

So let’s jump to that…

The album ends with a song called “In a Sweater Poorly Knit” (the title winks at an earlier song’s title, “In a Market Dimly Lit”). It’s the story of Moses in Egypt, sort of. It features some lovely harp and acoustic guitar. All of the genius annotations for this song insist it’s a big metaphor for a breakup, which I’d never once considered before. Eh. Idk. It’s so pretty. Like most of their songs, I think it’s about his relationship with God and with his own ego. It ends the same way the album opens, with the line “I do not exist”. Mostly it’s just so pretty.

At some point you realize he started actually singing and not speaking or shouting and it kind of suits him. He’ll do more of that soon.

It’s All Crazy! It’s All False! It’s All a Dream! It’s Alright (2009)

OK. Here’s my big opinion: this album is a masterpiece.

Pretty much all traces of personal anguish are gone. Almost every song is a story. Almost all of the characters are animals. When they’re not animals, they’re fruits and vegetables. When they’re not produce, they’re the baby Jesus. It sounds gorgeous.

The opening track, “Every Thought A Thought of You”, is a blissed out, bouncy, slightly strained devotional to God. The closing track, “Allah, Allah, Allah”, is also basically the same, except it has a kind of fun campfire singalong vibe.

I’m not a religious person in any way except that I love this band and find them very persuasive, particularly the way they seem almost absurdly non-denominational. There’s even room in these songs for doubt. One of my favorites is the longest song on the album, “The King Beetle On a Coconut Estate”:

What is this song getting at? Is the King a fool? It seems he’s just a dumb bug who flew into a light and died. That’s not so great. The “great mystery” turned out to be dumb. Hmm.

The details in this song are so lovingly rendered (“The beetle king summoned his men / From the top of the rhododendron stem”; “The lieutenant stepped out from the line; As he lassoed his thorax with twine”) that I can’t help but feel that Weiss empathizes with the plight of the Beetle King.

Why not be utterly changed into fire?

Wuff..

Most of all this album sounds peaceful and gentle and assured and pretty. Here’s “A Stick, A Carrot, A String”, which positively shimmers:

Look, this one is straight up about the baby Jesus, from the perspective of the animals who were around when he was born. Weiss finds time to make you empathize with each one:

At a distance stood a mangy goat
With the crooked teeth and a matted coat
Weary eyes and worn
Chipped and twisted horns

Thinking “maybe I’ll make friends someday
With the cows and the hens and the rambouillet
But for now, I’ll keep away
I’ve got nothing smart to say”

God damn - I love that goat.

There are so many songs I love on this album (all of them). Other highlights:

  • “Goodbye, I!” – the bit where he sings “Let’s stand completely still” and then the drummer pitter patters out a little drum solo makes me catch my breath each time I hear it
  • “The Fox, The Crow and the Cookie” – this is a perfect little story delightfully rendered
  • “Fig With A Bellyache” – First of all, that title, come on. But it’s the most stylistically weird song on the album and features some wonderfully awkward lyrics (“The dog below our waists aroused as arms embraced the pretty gals / It came much more as a surprise / It happening while I hugged the guys”)
  • “Cattail Down” – for the conclusion “You think you’re you? You’re not you. You’re everyone else.” and some sweet trumpet action

One thing I loved about this album when it came out is that it felt like the culmination of everything that came before it. Over time their albums became less angry and more concerned with humility. The arrival of wind and string instruments felt like an answer. The urgent searching of youth seemed resolved. The answers had been found.

So where do they go from there?

Ten Stories (2012)

For one thing, they decided to play their electric guitars on all the songs again. I think I recall reading that the non-Weiss members of the band wanted to play more rock songs again, and Weiss was like, sure. When it came out, I was disappointed that it didn’t feel like a continuation of the aesthetic of the previous album. In some ways, it is, though. It’s also concerned with fables, this time it loosely tracks the story of a collection of circus animals who cause a train wreck and make their escape. I don’t really connect to this story. I dunno. For me, this album is just fine.

There’s one song which sounds like it could’ve been on the previous album, “Cardiff Giant”, and it is my favorite song on the album:

Pale Horses (2015)

When this one was coming out, I felt trepidation. Were mewithoutYou drifting in a direction I didn’t like? Was Weiss being held hostage by the rock and roll instincts of his band mates?

I don’t know, but this album is super good, so who cares.

Here’s something from an interview with Weiss:

Interviewer: What does Pale Horses mean to you?

Weiss: That’s a hard one. That question could take up the whole interview. I wouldn’t know where to begin, but it probably doesn’t differ too much from our other albums in that it’s just an expression of where I’m at and what I’ve been experiencing in the past few years, what my convictions are and what my hopes are. It’s pretty personal. Probably more so than some of our more recent albums, which were a little bit more distant and third person and fabulous – you know, fable-based and character-based. For this one I’ve come back more to the first person to try to share what’s in my heart and what’s on my mind, and hopefully be somewhat uplifting in the process. But covers a wide range of things, so it’d be too hard to pinpoint any one of those.

So here’s my theory: the hard rock vibe doesn’t work well with the fables (see Ten Stories) but it works great for the not stories.

The interview continues:

Interviewer: What do you think brought mewithoutYou back to you, as opposed to those third-person fables? Why did you decide to do that?

Weiss: For a couple of reasons, I guess, but the easiest one to give you is that the guys in my band asked me to, and I was happy to oblige. A little bit more of a personal reason has to do with wanting to keep things fresh. When we put out It’s All Crazy! and Ten Stories, that was me trying to move away from the first person and get the ego out of the way, trying to write in a way that others could relate to just as well, or address content that might have some universal – or quasi-universal – significance, and trying to avoid letting my subjectivity muddy the waters. But more recently, I’ve started to doubt whether I can ever do that, and it felt like maybe those attempts were too ambitious and I was maybe biting off more than I could chew. So I thought if there’s one thing I can write about with some insight, it’s myself. That’s not to say I even have any expertise on myself…

Aha. Here is the real, correct answer to “where do you go” from the 2009 album. You walk it back a little. You didn’t find the answers. Obviously. C’mon. Who are you? All that peace you cultivated? Maybe try living life for a little while and see how that goes.

So: he’s screaming again.

Here’s a lovely music video for “Red Cow” and “Dorothy” which hard-lefts from a screamy rock jam into a graceful, melancholy wind-down:

This feels like rich new ground to root around in.

Here’s another song, Blue Hen, that just sounds great, and contains an all-time great Weiss freak out

And I’ll wrap up your absence
In blankets of reverence
A mastodon shadow
Divided by zero

I have no idea what that means but I totally know what it means and I love it.

My favorite song has got to be the last one, “Rainbow Signs”:

It’s long and builds slowly and contains the apocalypse and I guess is about his father dying. I find it very powerful and kind of awe inspiring.

(????) (2018??)

mewithoutYou seems to be in the studio, per their twitter, and a new album should come out soon enough, and I can’t wait.

Sad Blocks

July 6, 2017

I wish Ruby knew when you wrote a sad block.

What is a sad block?

It’s something I just made up.

Consider this code:

Candle.all.each do |candle|
  puts candle.inspect
end

Let’s say you run it, and you see no output. What do you conclude? Probably that there aren’t any candles.

Well, maybe. Or maybe Candle is implemented like this:

# candle.rb
class DatabaseResult
  def each; end
end

class Candle
  def self.all
    DatabaseResult.new
  end
end

Look, that would be weird, but it’s possible, and Ruby doesn’t do anything to help you out here, and I feel like it should.

What’s happening? You’re calling the instance method #each of the DatabaseResult class. And it’s just not doing anything at all and doesn’t even know you gave it a block. Cool.

Brief digression time.

That method has an “arity” of zero. How do I know that?

$ irb
>> require "./candle"
=> true
>> DatabaseResult.instance_method(:each).arity
=> 0

Also by looking at it.

What does it mean? It means that the method takes zero arguments.

But when we count the arity, we’re not considering blocks, because blocks are a special, weird kind of argument, where you can provide it or not and it’s kind of outside of the method signature. You can have methods that takes a block and uses it, and its arity will still be zero:

# candle2.rb
class DatabaseResult
  def initialize(values)
    @values = values
  end

  def each
    @values.each do |value|
      yield(value)
    end
  end
end

class Candle
  def self.all
    DatabaseResult.new([
      new("Geranium"),
      new("Lavender"),
    ])
  end

  def initialize(scent)
    @scent = scent
  end
end

Candle.all.each do |candle|
  puts candle.inspect
end
$ irb
>> require "./candle2"
=> true
>> DatabaseResult.instance_method(:each).arity
=> 0

Even though we use the block we’re given, the arity is still zero.

What about that other syntax where you explicitly put the block in the method signature, does that make it count toward the arity?

def each(&block)
  @values.each do |value|
    block.call(value)
  end
end

I’ll tell you: it doesn’t. Even though it sort of feels like it should.

These versions of the method really require you to pass them a block, which you just have to know. If you forget to pass a block, you get a nasty error:

# candle4.rb
# ...
def each(&block)
  @values.each do |value|
    block.call(value)
  end
end
# ...

Candle.all.each
candle4.rb:9:in `block in each': undefined method `call' for nil:NilClass (NoMethodError)
        from candle4.rb:8:in `each'
        from candle4.rb:8:in `each'
        from candle4.rb:27:in `<main>'

Or in this version, an even better error:

# candle5.rb
# ...
def each
  @values.each do |value|
    yield value
  end
end
# ...

Candle.all.each
candle5.rb:9:in `block in each': no block given (yield) (LocalJumpError)
        from candle5.rb:8:in `each'
        from candle5.rb:8:in `each'
        from candle5.rb:27:in `<main>'

No block given. Local jump error. Sure. That’s Ruby trying to be helpful and I appreciate that.

Ruby helps you (by raising a helpful error) when you don’t provide a block, but you were supposed to. But it doesn’t help you when you do provide a block, and you weren’t supposed to.

Ruby’s like, yeah, sure, just provide a block wherever you want, this is a free country.

If you wanted to change this behavior in your code, and get helpful errors when your blocks are unexpectedly not invoked, you could do something like this:

class SadBlock
  def initialize(&block)
    @block = block
    @called = false
  end

  def verify
    raise 'hell' unless @called
  end

  def to_proc
    ->(*args) {
      @called = true
      @block.call(*args)
    }
  end
end

sad_block = SadBlock.new do |candle|
  puts candle.inspect
end

Candle.all.each(&sad_block)
sad_block.verify

I don’t think you should do this, but you could, and I kind of wish Ruby just did it automatically.

I know it’s an impractical request, because there are valid use-cases where you might pass a block to a method, and the method just assigns it to an instance variable without calling it, but it promises to call it later. But maybe Ruby could detect that somehow. I’m just thinking out loud here.

I’ve seen tests where assertions lived in blocks, and the blocks were never being called, so they weren’t actually asserting anything.

I’ve seen configuration being done via a DSL in a block, except the block wasn’t being called, so the defaults were being used.

I guess what I’m saying is it’s a little weird to me that blocks aren’t treated like ordinary arguments. If they were, you’d get an ArgumentError if you forgot to provide it or if you provided it and it wasn’t expected.

That’s what I want.

there are no rules in ruby

July 2, 2017

Note: I’ve expanded on these ideas in a conference talk, which you can see here.


I recently learned about a feature of the Ruby programming language that has shaken me to my very core.

Consider this code:

# dog.rb
class Dog
  attr_reader :name

  def initialize(name)
    @name = name or raise ArgumentError
  end
end

def get_dog
  Dog.new("Milo")
end

thing = get_dog
if Dog === thing
  puts thing.name + " is a dog"
end

What happens when you run this code? Feel free to try.

But I’ll tell you.

$ ruby dog.rb
Milo is a dog

This code seems pretty resilient to unexpected runtime errors.

Looking at the code, it seems pretty reasonable to believe:

when we have an instance of Dog, we will be able to send it the message name and get back a String

Up is up. The sky is blue. We’re living in a society.

Well, ok, but we can’t actually assume that the value will be a String, because it doesn’t check that. If we change our definition of get_dog, things blow up:

def get_dog
  Dog.new(["Milo"])
end
$ ruby dog.rb
dog.rb:15:in `<main>': no implicit conversion of String into Array (TypeError)

But, OK, at least that error message is pretty good. This is user error. When we write thing.name + " is a dog", we’re expressing some amount of faith in ourselves that we expect a String, because values of other types don’t necessarily respond to a + method. This is a leap of faith that we’re all willing to make when we use Ruby. Other languages eliminate the need to make that leap of faith by checking types when you compile your code, but Ruby doesn’t do that.

And that’s fine.

So maybe our expectation should be:

when we have an instance of Dog, we will be able to send it the message name and get back a truthy value

And we’ll just remember to provide Strings. Maybe we’ll write a comment indicating the expected type of the parameter.

Well, what if get_dog looked like this:

def get_dog
  dog = Dog.new("Milo")
  def dog.name
    nil
  end
  dog
end

Maybe it just casually redefined the name method for that instance. Then your program crashes like this:

$ ruby dog.rb
dog.rb:19:in `<main>': undefined method `+' for nil:NilClass (NoMethodError)

Which… OK, who’s going to write code like that? Not me and no one I work with, for sure!

But where does that leave our statement of beliefs?

when we have an instance of Dog, we will be able to send it the message name

We can’t even say “and get back a value” because what if the override raises an error?

Perhaps you see where this is going…

Well, what if get_dog looked like this?

def get_dog
  dog = Dog.new("Milo")
  dog.instance_eval('undef :name')
  dog
end
$ ruby dog.rb
dog.rb:17:in `<main>': undefined method `name' for #<Dog:0x007fecc104a870 @name="Milo"> (NoMethodError)

Which, again, lol. You can just remove methods if you want to? Sure. No one is going to write this. I know.

(By the way, hat tip to Agis on Stack Overflow for sharing this trick. I figured it was possible but didn’t know how.)

OK so what can we say for sure?

How about this:

when we have an instance of Dog, it will have an instance variable @name defined

Wow that’s sad! How do we even check that? Maybe like this:

thing = get_dog
if Dog === thing
  puts thing.instance_variable_defined?("@name").inspect
  puts thing.instance_variable_get("@name").inspect
end
$ ruby dog.rb
true
"Milo"

OK great, have we reached the bottom?

No, because there are no rules in Ruby.

We can probably break this in many ways. Here’s one:

def get_dog
  dog = Dog.new("Milo")
  dog.remove_instance_variable("@name")
  dog
end

IMO this one is a bit pedestrian. Yeah, fine, you can just remove instance variables on random objects if you want to. Of course. My spirit is already broken, this isn’t meaningfully worse.

So let’s just try to say something that we don’t have to take back right away:

when we have an instance of Dog, the code in the initialize method must have run

Right? That has to be true. We’re living in a society, remember?

Nope:

def get_dog
  Dog.allocate
end

That results in this output:

$ ruby dog.rb
false
nil

What the hell is this?

This is the thing I mentioned at the beginning that I learned recently. When we create new objects in Ruby, we usually use the new class method. Notably, we don’t call the initialize instance method ourselves, although that’s what we are responsible for defining. Ruby handles calling that method for us. But before Ruby can call an instance method, it needs an instance, and that’s where allocate comes in. It just makes an instance of the class.

And you’re allowed to use it in your Ruby code, if you want to.

(Hat tip to John Crepezzi whose blog post explains this really well)

If you do, you get back a normal instance of your class in every way, except that the initialize method hasn’t run.

You can even call your own initialize method if you want to:

def get_dog
  dog = Dog.allocate
  dog.send(:initialize, "Milo")
  dog
end

We have to use send because initialize is private. Well, unless we change that:

class Dog
  attr_reader :name

  public def initialize(name)
    @name = name or raise ArgumentError
  end
end

def get_dog
  dog = Dog.allocate
  dog.initialize("Milo")
  dog
end

Sooooo where does that leave us?

when we have an instance of Dog, it’s a good dog

Basically: 🤷‍♂️.

That’s the bottom. That’s as far as I know how to go. Maybe there’s more. Please don’t tell me.


I want to emphasize: this is not a criticism of Ruby. I’m only faux-alarmed. Ruby is a springy ball of dough. It’s whatever you want it to be. All of these features are sharp knives you can use or abuse.

As I’ve been learning another language which feels much less pliant, I’ve started to notice things about Ruby that never occurred to me before. When I write Rust, I take some pleasure and comfort from the rigid rules. It’s more possible to use words like “guarantee” and “safety” in Rust-land.

But Ruby keeps you on your toes.

chainable shell functions

February 6, 2017

I learned a neat shell script refactoring strategy yesterday that I’d like to share. First some background:

I used to use a rubygem to help me write this blog. When I wanted to create a new post called “chainable shell functions” I would have run:

bin/poole draft "chainable shell functions"

And it would create a file called _drafts/chainable-shell-functions.md with some metadata in the first few lines.

Yesterday I got the urge to try replacing that rubygem with a custom shell script which does exactly the same thing.

I am an enthusiastic novice shell scripter.

I’m vaguely aware there are different dialects of shell scripting and that I’m probably using the wrong one.

Really I’m not expert in this stuff.

But while writing this one I learned something interesting that I’m going to share with you now.

Here is the first draft (annotated with comments for your convenience):

#!/usr/bin/env bash

# fail fast if any expression fails
set -e

# read all of the arguments into a string
title="$*"

# OK don't worry about this gnarly line, I'm going to break it down
slug=$(
  echo "$title" | sed "s/ /-/g" | tr -dc '[:alnum:]-' | tr '[:upper:]' '[:lower:]'
)

# the file we're going to create
filename="./_drafts/$slug.md"

# create the folder if it doesn't already exist
mkdir -p _drafts

# stop if the file already exists -- I don't want to overwrite an in-progress draft
if [[ -e "$filename" ]]; then
  echo "$filename already exists"
  exit 1
fi

# create the draft by piping a string into a file
echo "---
title: $title
date: $(date '+%Y-%m-%d')
---

Alright, this is where your post goes." > $filename

# Print a successful message
echo "Created $filename"

OK did you read that? Great.

So you saw that line I promised I would break down? The idea with that line is that I want to take the input, which is the title of the post, and figure out what is an appropriate filename for the post. I’m figuring that out by applying a series of transformations to the title:

  • echo "$title"
    • just repeats the title, directing the output into a “pipe”, which the next command will read
  • sed "s/ /-/g"
    • sed is a “stream editor”; it reads in a stream of data and prints out a stream of data
    • here we’re using regular expressions to “s” or substitute all occurences of a space with - (hyphen)
    • we want hyphens because they make for nicer looking URLs than spaces, which get escaped to %20.
    • the g at the end means “global”; without it, we would only subsitute the first space
  • tr -dc '[:alnum:]-'
    • tr is short for “translate”
    • -d means “delete”
    • -c means “complementary”
    • this command means “delete all the characters that complement this set of characters”
    • in other words, “delete all the characters that aren’t alphanumeric or a hyphen”
  • tr '[:upper:]' '[:lower:]'
    • “translate” again!
    • this time we’re translating all of the upper-case letters to lower-case letters
  • Finally, we stop piping the output to the next command, and we’re done, so the result is saved in that local variable.

OK so that’s a lot going on in one line, and because of the compact nature of these commands, it’s not super readable.

In other languages, when I have a lot going on in one function, I want to split out smaller, well-named functions. Can I do the same thing here?

At first I wasn’t sure. I knew it was possible to write functions that received arguments by checking $1, $2, etc in the function, but I wasn’t sure how to make them “return” values…

After a little googling I learned: you can just write a shell function that calls commands that read from a pipe, and pipe things to that function.

Let me show you what I mean.

Here’s the second (and, frankly, final) draft:

#!/usr/bin/env bash

set -e

function dashify() {
  sed "s/ /-/g"
}

function removeSpecialChars() {
  tr -dc '[:alnum:]-'
}

function downcase() {
  tr '[:upper:]' '[:lower:]'
}

title="$*"
slug=$(
  echo "$title" | dashify | removeSpecialChars | downcase
)
filename="./_drafts/$slug.md"
mkdir -p _drafts

if [[ -e "$filename" ]]; then
  echo "$filename already exists"
  exit 1
fi

echo "---
title: $title
date: $(date '+%Y-%m-%d')
---

Alright, this is where your post goes." > $filename

echo "Created $filename"

Look at that!

When to use defined? to memoize in Ruby

February 5, 2017

Here’s a quick Ruby thing.

traditional memoization in Ruby

Let’s say you have an object whose responsibility is to give a haircut to a dog.

(I may have recently been reading about this)

class DogStylist
  def initialize(dog_id)
    @dog_id = dog_id
  end

  def perform
    if dog
      dog.sedate
      dog.groom
      dog.instagram
    end
  end

  private

  def dog
    Dog.find(@dog_id)
  end
end

This is kind of fine, but it has one problem: each time you reference dog, you’re calling the dog method, which queries the database each time it’s called, so you’re querying the database over and over, when you really only need to do so once.

Better to write it like this:

def dog
  @dog ||= Dog.find(@dog_id)
end

Here you’re still calling the dog method over and over, but now it’s “memoizing” the result of the database query.

But what does that mean?

Here’s a more verbose version of the dog method that does the same thing:

def dog
  @dog = @dog || Dog.find(@dog_id)
end

You can see that ||= is a syntactical shorthand similar to +=.

In case you’re unfamiliar with +=, here’s an example. These two statements are equivalent:

count = count + 1
count += 1

Here’s an even more verbose version of the dog method that does the same thing:

def dog
  if @dog
    @dog
  else
    @dog = Dog.find(@dog_id)
  end
end

The goal here is to avoid evaluating the database query more than once. The first time the method is called, the @dog instance variable is not defined. In Ruby, it’s safe to reference an instance variable that isn’t defined. It will return nil. And nil is falsey, so the database query will be evaluated, and its result assigned to the instance variable.

This is where things get interesting.

Ponder this question: does this memoization strategy guarantee that the database query will only be executed once, no matter how many times the dog method is called?

It doesn’t.

Why????

I’ll tell you.

What if there is no dog with that ID? Dog.find(4000) returns either a dog, or nil. And, like we said earlier, nil is falsey. So hypothetically, if our perform method looked like this:

def perform
  dog
  dog
  dog
  dog
  dog
end

… then we would execute the database query five times, even though we made an effort to prevent that.

This is actually totally fine, because our perform method isn’t written like that (again, that was just a hypothetical). Our perform method only calls the dog method more than once if it’s truthy, so there’s no problem here.

memoization using defined?

Let’s consider another example, where things aren’t as hunky-dory. Hold please while I contrive one.

OK, I’ve got it.

Let’s say we only want to groom a dog when he or she is unkempt. When she logs into our web site, we want to pepper some subtle calls to action throughout the page encouraging her to book an appointment. We’ll need a method to check if she is unkempt, and we’re going to call it a few times. It looks like this:

class Dog
  HAIRS_THRESHOLD = 3_000_000

  def unkempt?
    Hair.count_for(self) > HAIRS_THRESHOLD
  end
end

That’s right: we’ve got a table in our database for all of the hairs on all of our dogs.

You can imagine this unkempt? method might be kind of “expensive”, which is to say “slow”.

Let’s try adding some memoization to this method:

def unkempt?
  @unkempt ||= Hair.count_for(self) > HAIRS_THRESHOLD
end

Here our goal is to prevent doing the expensive database query (Hair.count_for(self)) more than once.

Ponder this question: does our memoization strategy accomplish this goal?

Answer: it does not.

What?????

I know. Let me show you.

You can try running this Ruby script yourself:

$count = 0
class Hair
  def self.count_for(dog)
    $count += 1
    puts "called #{$count} times"
    2_000_000
  end
end

class Dog
  HAIRS_THRESHOLD = 3_000_000

  def unkempt?
    @unkempt ||= Hair.count_for(self) > HAIRS_THRESHOLD
  end
end

dog = Dog.new
puts "Is the dog unkempt? #{dog.unkempt?}"
puts "Is the dog unkempt? #{dog.unkempt?}"

It outputs the following:

called 1 times
Is the dog unkempt? false
called 2 times
Is the dog unkempt? false

In this script, I have a fake implementation of the Hair class. It’s meant to demonstrate that the count_for method is being called more than once, even though we specifically tried for it not to.

So what’s going on here?

Well, in a way, everything is working as it’s supposed to. The first time we call the unkempt? method, the @unkempt instance variable is not defined, which means it returns nil, which is falsey. When the instance variable is falsey, we evaluate the expression and assign its result, false, to the instance variable. The second time we call the unkempt? method, the @unkempt instance variable is defined, but its value is now false, which is also falsey (which you have to admit is only fair). So, again, because the instance variable is falsey, we evaluate the expression and assign its result, false, to the instance variable.

Shoot – that kind of makes sense.

So what to do? Here’s another way to write this:

def unkempt?
  if defined?(@unkempt)
    @unkempt
  else
    @unkempt = Hair.count_for(self) > HAIRS_THRESHOLD
  end
end

This approach uses Ruby’s built-in defined? keyword to check whether the instance variable is defined at all, rather than if its value is truthy. This is more resilient to the possibility that your value may be falsey.

I wish there were a more succinct way to write this, because I think it’s generally how you actually want your code to behave when you use ||=.

To be fair, you can avoid defined? and instead write this method like this:

def unkempt?
  @hair_count ||= Hair.count_for(self)
  @hair_count > HAIRS_THRESHOLD
end

It’s really just a matter of taste if you prefer one over the other.

Alright, take care.

Using git to track git

August 21, 2016

I made a screencast to share a fun idea I had while exploring a bit how git works.

You may know that when you use git to track a project, it creates a hidden .git directory with some files in it. But what actually goes on in there? And when do the contents of those files change?

Here’s the idea: I know a tool for tracking the changes to a directory over time, and that tool is git itself!

So in this screencast you can see me try and do that – I initialized a git repository, which created a .git folder, and then I initialized another git repository within that .git directory.

I still don’t have a really great understanding of how git represents the data, although I’ve read Mary Rose Cook’s very good essay about this topic Git From The Inside Out, which does contain those answers (I read it a while ago and forgot the details).

But I feel like I learned a few things thru this little experiment, specifically about when they change.

jfk

August 17, 2016

Note: this is a sort of personal story about a stressful experience. I’m writing it because I want to remember it.

Last week I was on vacation in Berlin, and this week I’m jumping back into my work life. Both things are great! In-between something wasn’t great.

My airbnb in Berlin
My airbnb in Berlin. 9:53

I had a few hours laid over in Copenhagen before my flight into JFK. I spent them doing laps around the terminal looking for something vegetarian to eat. While walking I listened to a few episodes of the Bike Shed podcast, which I’m a few months behind on. Eventually I found and ate some nachos, which I happily ate while reading Squirrel Girl. All that, fine.

The flight was uneventful. Norwegian Air. I got around to watching Carol. I probably need to rewatch it on a bigger screen. Solid. Slept a bit. So far so good.

A husky American in a striped polo shirt sat on my right. A young Swedish woman slept on my left. None of us really talked much, except mild small talk about whether you need to declare spices at customs. (She asked me. I had no idea, but I said eh, you can probably get away with not mentioning it. I think she declared anyway.) She told me she’s a student on her way to Arkansas to study abroad, and she’s just transfering in New York.

When we landed, around 8pm, we couldn’t disembark right away. The pilot cited weather. They played music. People kept watching movies and TV. I think I didn’t disembark until around 9:40pm. I remember joking to the Swedish woman that if it wasn’t for the music, I might be kinda impatient, but with it I’m pretty content. The playlist ran out of songs and looped back on itself. I only noticed because it was mostly upbeat pop songs, but then also Blue Bucket of Gold by Sufjan Stevens, a very slow and beautiful song about feeling lonely and alienated. And I didn’t realize it was Sufjan Stevens (who I really like) until it came on a second time and I laughed at how random that is. Then I fell asleep for a bit.

When we did start to disembark, I put on my big black backpack, but then we were asked to sit down again, and I took it off. A few minutes later, people started moving again and we flowed off the plane. I said “thanks” to the flight attendants, thinking they were probably getting a lot of shit from impatient passengers and strolled down the jet bridge (a term I only learned this week) toward customs. I was kind of tired; I’d just been napping with my forehead against a television. All I was thinking about was how to get home, weighing the choice of springing for a cab or just taking the train.

But first, customs. I got on the end of a long line, putting in my earbuds and selecting Hotline Operator, a song which bristles with an impatient energy. Because people were impatient, they pressed forward, as though becoming dense would make the line move faster. Some others got on the stationary moving sidewalk to move farther ahead in the line. I remember thinking it was like people driving on the shoulder of a highway to pass you. Without putting a lot of effort into hanging back, I was pretty much at the way back of the line. I couldn’t see all the way to the front. I started thinking about taking off my heavy backpack with the expectation that I would be standing for a while. When I expect to be bored for a while I kind of let my body go into autopilot and let my mind wander for a while, and that’s what I was starting to do.

I’m not sure what exactly snapped me back to reality, but the next thing I knew I was running, and so was everyone else. In only a few seconds, hundreds of mostly stationary people facing one way turned and ran the other way, toward me. You don’t really question that, you just go. You don’t really have another option. Not that you’re thinking at all.

The gush of humanity I was swept up in elected to run down a jet bridge. There were several abandoned rolling suitcases blocking the path. I knew people were charging behind me and were probably going to stumble on them, and I grab one by the handle and run with it in my hands. At the entrance to the plane, I set it to the side. This is the one good deed I did all night, I think, and I still left a bunch behind.

The Air Korea flight attendants were confused and kind of angry that these people were rushing onto their plane.

“What is happening??” one asked the streaming crowd.

I told her the crowd has panicked and I didn’t know why, but it seemed like maybe there’s a shooter, but I didn’t know.

That seemed like the only explanation. People in the front must have seen something and bolted, understandably. What else could it be? No one needed to say it – we all knew, right away. And people did start saying it.

I felt incredibly alert. I would notice later my mouth was dry as a bone.

I paused in first class and moved into a seat to get my bearings. Some came panting, others crying onto the plane, everyone flowing into the back as though for a takeoff. I saw a woman calling for her son; they were separated in the chaos. “What does he look like?”, someone asked. “He’s ten years old”, she said. I saw another mother holding her young, sobbing son to her stomach and telling him, “It’s okay. It’s okay.” I wrote a text to my family:

I’m safe. There was a a panic at the airport. Crowd ran. I’m sitting on an airplane right now. Different one than I disembarked from. Followed running crowd. No signal. Not sure if this will send. Writing at 22:07 ET Will update. I am safe

I see now that it went through ten minutes later.

I wasn’t sure how safe I was. Even on the plane, I felt exposed and at risk. I was tempted to move to the right side of the plane to be just a little farther from the terminal. But I needed to send that.

I saw the Swedish woman, shocked, move into the back of the plane, and I joined her in a seat.

“What the fuck is going on?” I asked, and she didn’t reply.

Somehow, for some reason, the flight attendants communicated to us that it was time for us to get off the plane. I don’t recall if we were instructed to go back down the jet bridge. I don’t recall if the plane was becoming full of people. What happened next is that people started opening the emergency exits. Maybe that’s why. They couldn’t manage to open the emergency exit in the rear of the plane. I recall seeing some kind of component hanging by a wire, and a flight attendant poking at it like, “well, this one isn’t opening”. “Stay calm”, an elderly woman urged everyone.

The mid-plane emergency exits were both open, and the one on the left (facing the terminal) had a slide going down to the tarmac. The one on the right had no slide, it was just open twenty something feet above the hard ground. One flight attendaant stood spread-eagle in front of it to make sure people knew not to go that way. Another stood by the slide, metering us out, telling us when to go. The two women shouted to each other in Korean over the din.

Ahead of me, people were sliding down to the tarmac and jogging around a corner of the terminal. I jumped, slid, and hit the ground running after them. I passed a man taking shelter in a corner behind what might have been gas tanks, and I was tempted to join him, but I ran on with the bigger group.

Around the corner, we reached a barbed-wire fence. I assume the idea is to make it harder to access the tarmac, but in the moment many people felt trapped. I saw some people pushing a bright yellow dumpster toward the fence, with the idea that they could use it to jump the fence.

I took several photos from this point on, nearly all of which came out blurry beyond interpretation.

The crowd and dumpster in the corner
The crowd and dumpster in the corner. 22:19

My sister texted me:

Max?

The rest of my family was in London, and sleeping, but Gaby was awake. I told her I was safe and to tell me if there’s anything in the news about JFK. Then I fired off a tweet:

I sent tweets intermittently for the rest of the night, most of which have typos because I wrote them quickly while afraid.

People weren’t sure what to do. Personally, my plan was to stay put and wait for a hero. I think some others probably felt the same way. Opinions varied on where was safest to physically stand. There was a large, open garage (I think that’s what you’d call it, although I don’t recall seeing any vehicles), which some stood within. This seemed like a good idea in the event that attackers came around the corner. Others seemed suspicious that attackers might come from inside the garage, and stood vigilant, eyes darting between the garage and the tarmac.

Around this point a policeman came and told us to follow him around the corner back toward the customs area, where there are more police. When he saw the people pushing the dumpster he shouted after them, something like, “You are NOT going to do that.” A woman asked him what the plan was, said she wouldn’t go unless there was a plan. He made the point that he was going to walk us there, and what, is he going to willfully put himself in danger? This argument, an appeal to a relatable self-preservation, seemed to make sense to the crowd.

We started cautiously walking.

The crowd under an overpass
The crowd inching under an overpass toward customs. 22:28

We were all hugely reluctant to move back in the direction of airplane and customs. As we inched forward I saw silhouettes of figures in the windows of the terminal, some walking, some standing and watching us. I didn’t know what to make of that. A child let out a screaming cry, and everyone immediately turned to run back toward the corner, which had somehow started to feel safe, and not worth leaving.

I saw a woman was running barefoot.

“I’m faster this way”, she said.

Gaby:

Something at terminal 1 and 8

They are bringing in bomb squad

What is your battery %

If you feel safe please save battery

They haven’t found shooter or anyone injured as of now

Me:

100%. I feel safe right now

Battery pack baby

Slowly the policeman and some other airport staffers coaxed us back toward customs, and two doors which went directly from the tarmac into the big open room. People were very reluctant to enter. I had the sense that the room must be safe if I’m being told to enter it, and I went in.

Inside, I was shocked to find that there were already dozens of people queueing in an orderly fashion to go through customs. I couldn’t see if anyone was actually at the desks to process them. Beyond the desks, the airport seemed well-lit and deserted.

I didn’t feel safe getting on the line; I’d seen this crowd turn into a stampede in seconds. I stood near the doors and tweeted this:

And a few minutes later, it happened. Something spooked the crowd and they bolted for the doors, feeling safer on the tarmac. I think they believed an active shooter must have still been in the terminal, even though some semblance of order was beginning to form… Outside I tweeted this (compare the timestamp to the previous one):

I meant “pile”, not pipe. Here’s what I saw:

People struggling to leave customs
People struggling to leave customs, and one tarmac staffer helping a fallen person get up. 22:41

At this point I saw people were starting to move way in the opposite direction of customs, to the far edge of the tarmac. I saw why: there were 2 shuttle buses there, and people were getting on them. I walked under an airplane toward the buses.

View of airplane from below
I made my way under an airplane. 22:46
Buses in the distance
I made my way toward these buses. 22:47

A crowd formed here, hopeful. We’re getting outta here. The two buses became full and left. I have no idea where they went. We waited, hopeful that more would come.

I took the opportunity to update Gaby, tweet a few updates, and search for any news. My twitter feed was full of people talking about the Olympics and or the election. I remember feeling slightly stunned that the whole world wasn’t talking about me. Every minute or two I got notifications of people sharing or replying to my tweet updates. Some folks mentioned or direct messaged me asking questions and providing updates from police scanners and news reports. No one knew anything. One BBC reporter mentioned me asking if I wanted to do an interview when I feel safe; a reporter from a talk radio show asked me if I could confirm that shots were fired.

I felt the need to offer this update:

I didn’t know if there was a shooting or not. I wanted to believe there hadn’t been. I knew there was a lot of confusion both among the passengers on the tarmac and among the people replying to my tweets. I couldn’t clear away the confusion but I wanted to emphasize to anyone who was following the story not to assume the worst.

After more than 20 minutes of standing restlessly and comparing confused notes with each other, three more buses arrived. I got on the last bus. The last person to board the bus before the door closed was a young guy who reminded me of my 20 year old cousin Eric from Georgia. He asked if I knew anything. Said his phone died. Said he got separated from his sister.

“How old is she?” I asked

“Nineteen”, he said.

Nearby a guy in a Panini Express shirt told us he was working when his boss suddenly turned and ran without saying anything. The next thing he saw was a traveler crawling behind the counter for shelter.

We waited on the bus for about ten minutes when an airport staffer boarded in the front and cheerfully, loudly gave us directions.

“Alright, here’s what we’re gonna do”, he projected. “We’re going to exit the front of the bus and follow [muffled muffled]. OK? Do you understand? Who understands?”

He spoke with the cadence of a DJ trying to get the party started. I had missed some of what he said, but I still felt kind of compelled to echo back, “I understand!” This was the clearest instructions I heard all night. Give that guy a medal.

The bus was packed, and there was a second exit in the back. A woman shouted out to the bus driver, “Back door!” The kind of thing you might shout on a city bus on a normal day, when the door hasn’t opened. I couldn’t help but wonder if the front door was somehow safer than the back door, and that was why we were being told explicitly to exit through the front door.

Upon exiting I followed a stream of people back toward customs. The crowd was much bigger now than before, and much calmer. There were more police. At some point a fire truck showed up. People started to relax, breath, sit.

Gathering crowd on tarmac
Gathering crowd on tarmac. 23:28
Crowd including man with torn shirt
Man with torn shirt in crowd. 23:31
Woman in crowd prays
Woman in crowd was praying to herself. 23:39

I noticed my dad has replied to me:

Dad (23:19, 5:19 in London):

Just got up to pee. Any new updates? News at 15 minutes ago says unconfirmed reports of gun shots at terminal 8 & 1 (yours)

I told him where I was and that it was much calmer now.

Dad:

Be patient & be a calming influence. See if anyone is alone & nervous

Me:

Yep

I pictured my mom sleeping and my dad glued to his phone.

The Swedish woman approached me.

“Hey”, she said.

I was shocked and very relieved to see a familar face. I had been traveling alone on my vacation and kind of reveling in the freedom to do whatever I wanted at any time. But during this incident I wasn’t myself, wasn’t independent, I was just alone, an unindividuated animal in a suspicious herd. We shared a water bottle she got from a fireman. I started to feel like a human again.

She missed her transfer to Arkansas and was on the phone with her friend in New York to see if she could stay there.

Many people sat down. I realized I felt safe enough to sit down.

Soon we started queueing toward customs and I felt safe getting in line. A young woman stood with us for a while, seemingly wanting not to be alone.

We parted ways inside, and I went through customs. I think it was quiet. The kiosk was working, and I punched in that I wasn’t planning to declare any food, spices, or anything. I was glad I held onto my backpack as I dug my passport out.

The young southern man from the bus was ahead of me in line.

“I found her”, he said, indicating his nineteen year old sister.

It took me a moment to register who he was and figure out what to say.

“I’m really glad to see that”, I finally said.

The police officer checked my passport and barely glanced at my customs slip.

“You’re okay”, he said, and stamped my passport.

As I exited baggage claim, I was met by a huge group of people waiting for the long-overdue terminal 1 arrivals, and felt momentarily like they were all there just for me, just to tell me I had been brave and I could rest now.

Crowd at arrivals
Crowd at arrivals. 00:27

I felt destabilized by how normal operations were on this side of the terminal, when it had been such fright and chaos on the tarmac.

There was no question I was taking a taxi. I waited for a while and ended up sharing one with three other people. The dispatcher was long gone, so the drivers were free to negotiate whatever prices they wanted. No problem.

On the drive home, we compared notes. A guy my age sat in the front seat, and a mother and teenaged daughter sat with me in the back. The news was saying it was all a false alarm and that there was no evidence of a shooter or a bomb or anything. The daughter said something about how scary the night had been for her, and her mother made the point, “We were never actually in danger at any point.”

When I eventually found my way to my bed (around 2:30) and closed my eyes, I felt waves of cold energy coursing through my limbs.


If you’re curious for another experience of this night, read this article: Scenes From the Terrifying, Already Forgotten JFK Airport Shooting That Wasn’t which captured a lot of what I felt and am feeling and also filled in some details for me.

This passage jumped out like a lightning bolt:

The fact that there had been, actually, nothing to panic about was an enormous relief, of course. But it made things all the more eerie the next morning, when we woke up feeling like survivors of a ghost trauma, a minor local-news story. For several hours, we were in the flood of panic and chaos of an ongoing act of terror. There’s no other way to describe it. That it was an overreaction almost doesn’t matter; in fact, that is how terrorism works.

Reading it made me feel some amount of relieved and want to write something myself.