Categories
Review

Star Trek: Lower Decks

I like the concept of Star Trek a lot — some of my favorite books growing up were optimistic science fiction, and the majority of Star Trek falls into that category. That said, I’ve mostly failed to actually get very much into Star Trek; without the nostalgia of having grown up watching it, I (watching in the ‘golden age of TV’) struggle to get past the date aesthetic of the older series.

All that said, Lower Decks feels like it was specifically targeted at me — I love the “adult animated television series” as a form of media, and it’s nice to have a clear entry point into the greater Star Trek universe.

The concept is pretty simple: instead of following around the bridge crew, what’s life like for the regular folks on a Starfleet ship? It feels more like a space opera: from this perspective you get glances into the crazy sci-fi goings-on, but half the time it’s just background dressing for interpersonal stories. It’s fun watching the characters shrug off a member of the bridge crew returning from the dead because “oh, they always do that. Probably they got Borg’d, or it’s a transporter clone, or something.”

Having it as animation also works quite well. It frees them to do ridiculous scenes without blowing through an entire season’s worth of special effects budget. It’s a lot easier to animate the aforementioned transporter clone scene when you just… draw the person twice and have them record two takes. No compositing shots together, no body-double in a green-screen suit. And, I hope, it will allow the animation to remain much more timeless than the live action shows can manage.

All told, I quite like Lower Decks, and do recommend it. As of this writing, we’re about halfway through the second season, with new episodes coming out every week, so go ahead and check it out. Each episode is around half an hour, and they work well as a palate cleanser between heavier series. Check it out.

Categories
Photography United States

Mt. St. Helens

If you’re looking at that and thinking “hey, that looks like a lot of mountain to climb,” you’re right! It is a lot of mountain to climb, and from that angle, you can’t see the half of it. (Literally.)

It’s an interesting hike, in three sections – the woodsy bit at the bottom, the bouldering bit in the middle, and the ash fields at the top.

This last photo was from the bouldery bit, but you can see a wash of the ash fields extending down in the back there.

But now, dear reader, I must admit to you that I didn’t make it to the summit; I got as far as the ash fields and realized that, if I tried to continue on to the top, I’d wind up replacing my “I went on a cool hike!” story with an “I went on a ride in a Forestry Service rescue helicopter” story.

One day, I’ll be back. And hey, it was a cool hike! Anywhere with signage like this is going to be a cool hike.

Categories
Playlist

Playlist of the Month: August 2021

If my spate of “here’s some photos instead of writing something” posts weren’t a hint, I have had a very busy month. Gotta make time for music, though!

Cologne – Haux on Something to Remember – EP

How It Was – Yoste on A Few Brief Moments – EP

We’ll Be Alright – Yoste on A Few Brief Moments – EP

DON’T TELL THE BOYS – Petey on Checkin’ Up on Buds – EP

Spaces – Jaymes Young on Spaces – Single

Arcade – Duncan Laurence on Arcade – Single

twentyfive – Yoste on twentyfive – Single

Oh Dear, Oh Beaux – beaux on A Love Letter To the Moments Spent Outside

Neon Medusa – The Midnight on Horror Show – EP

Flow – Vide on Flow – Single

Starlite – HOKO on Heathen

DIM – SYML on DIM – EP

BLACK TEETH – SYML on DIM – EP

Good in Red – The Midnight on Horror Show – EP

Coldplay (feat. Vic Mensa) – Mr Hudson on Coldplay (feat. Vic Mensa) – Single

Don’t Go Puttin Wishes in My Head – TORRES on Thirstier

I Only Go South – Yoste on I Only Go South – Single

Phonky Town – Playaphonk on Phonky Town – Single

b!!!rds – Sir Sly on The Rise & Fall of Loverboy

Los Tontos (Live at NPR’s Tiny Desk) – C. Tangana & Kiko Veneno on El Madrileño (Live at NPR’s Tiny Desk) – Single

All Eyes On Me – Bo Burnham on Inside (The Songs)

Distorted Light Beam – Bastille on Distorted Light Beam – Single

BYE – Jaden on BYE – Single

Politicians – Hayden Calnin on Politicians – Single

old cartoons – FJ Law on TAPE 1

family – FJ Law on TAPE 1

Tangerine – Benicio Bryant on Tangerine – Single

Why Is Everyone Sad? – Yoste on Why Is Everyone Sad? – Single

Vampires – The Midnight & Magik*Magik on Vampires – Single

Don’t You Look At Me That Way – flora cash on Chronically Beautiful – EP

The Comeback Kid – The Midnight & Magik*Magik on The Rearview Mirror – EP

Reckless – Turbo on Cocaine & Fireworks

Anxiety In Real Time – The Maine on XOXO: From Love & Anxiety In Real Time

Ghosts – BANNERS on Teen Wolf Season 5

Shockwave – Marshmello on Shockwave

Tied Up – Jon Bryant on Tied Up – Single

Momma Look – Lani Rose on Momma Look – Single

Smells Like Teen Spirit – Malia J on Smells Like Teen Spirit – Single1

Sunset – The Midnight & Magik*Magik on The Rearview Mirror – EP

superpowerful – slenderbodies & Crooked Colours on are we?

INDUSTRY BABY – Lil Nas X & Jack Harlow on INDUSTRY BABY – Single

I Didn’t Change My Number – Billie Eilish on Happier Than Ever

I Think I Love You – The Partridge Family on The Partridge Family Album2

Falling Down – Harrison Storm on Falling Down – Single

Almost Home (feat. Novo Amor, Mindy Jones & Darlingside) [Reprise Version] – Moby on Almost Home (Reprise Version) [feat. Novo Amor, Mindy Jones & Darlingside] – Single3

Give Me The Future – Bastille on Give Me The Future – Single

Sunset Lover – Petit Biscuit on Petit Biscuit – EP

SEAL of DARKNESS (feat. CXXLION) – Playaphonk on SEAL of DARKNESS (feat. CXXLION) – Single

Take My Breath – The Weeknd on Take My Breath – Single

Bedroom Eyes (feat. Studio Killers) – The Knocks on Bedroom Eyes (feat. Studio Killers) – Single

I’m Tired – Amber Run on I’m Tired – Single

Sink – Yoste on Sink – Single4

Better Days – Dermot Kennedy on Better Days – Single

Our Bones Turn To Stone – Aquilo on A Safe Place To Be

Oxytocin – Billie Eilish on Happier Than Ever

Yate – C. Tangana on Yate – Single

NDA – Billie Eilish on Happier Than Ever

Bop – Masked Wolf on Bop – Single

Makes Me Wonder – ItsLee, nowifi & Vide on Makes Me Wonder – Single

What It Means to Be Human – Hayden Calnin on What It Means to Be Human – Single5

Kyoto (Copycat Killer Version) [feat. Rob Moose] – Phoebe Bridgers on Copycat Killer (feat. Rob Moose) – EP

Inner Light – Elderbrook & Bob Moses on Inner Light – Single6

Starlight – Griffin Stoller on Starlight – Single

In This Shirt (Original) – The Irrepressibles on In This Shirt – Single7

@ my worst – blackbear on misery lake – EP

Chronically Beautiful – flora cash on Chronically Beautiful – EP

Higher Power – Coldplay on Music of the Spheres

  1. This was an excellent musical choice for how it was used in Black Widow.
  2. Much like the song, I woke up, sat bolt upright in bed, and started singing this. Incredibly catchy.
  3. Some lovely Novo Amor, but tempered by the other artists, so it doesn’t immediately crush you with The Sadness.
  4. “Don’t you want to see your sister again? Her life’s a wreck, but she was cool back then.” – sent to my sister, thanks to the “share lyrics” thing in Music.
  5. I think Hayden Calnin is the most lyrically interesting artist I’m listening to at the moment. Good stuff!
  6. Probably my single favorite new one this month; I can’t figure out what song it’s making me nostalgic for, but it’s definitely doing it.
  7. Very cinematic in feeling; is there a reverse “what song is in this show?” site so I can look up what shows wind up using this song?
Categories
Photography United States

Tubbs Hill

I was in Coeur d’Alene a while back, and had the chance to go for a hike around Tubbs Hill. If you’re ever in the area, I recommend it – it’s a nice little hike, and there’s some great views along the way.

Categories
Photography United States

Silver Falls

This weekend, I am attending a wedding and then moving; in place of my usual weekly words, please enjoy a couple photos I took at Silver Falls State Park.

Categories
Technology

Async/await in Vapor

Swift 5.5 with the new async/await features is going to be released quite soon, and I’m very excited. And while all the new async APIs in UIKit and SwiftUI are neat, they don’t actually catch my eye all that much. As of now, async/await will only be available on the fall-2021 Apple OS releases, and following the general pattern of “current iOS minus one” that means async/await will be available starting… fall of 2022.

However, that’s not the only place I use Swift! I also dabble in server-side Swift when I can, and over there, it’s ready-ish to use now.1

And hey, would you look at that, Vapor’s already got a PR open for adding async variants of the core APIs. Neat! Let’s see how this is implemented.

And, hey, it’s… pretty simple, really! All it takes is getting into the (pre-release!) _NIOConcurrency module, and suddenly any EventLoopFuture has a new function: public func get() async throws -> T. The implementation of that is also lovely and simple, thanks to Swift’s withUnsafeThrowingContinuation and the way SwiftNIO already works.

Now, where I’m more excited about this than Vapor is in Fluent — a lot of the server-side work I do is in C#, using the excellent Entity Framework Core. Database requests are straightforward: MyModel model = await db.MyModels.FirstOrDefaultAsync(m => m.id == id);

With the non-async version of Vapor and Fluent, this is… a bit more difficult to work with. MyModel.find(id, on: req.db).flatMap { model in ... }

At first glance it doesn’t look too bad, but notice the flatMap — we can’t just continue writing our code, we have to move into a new scope. Hello, pyramid of doom.

But now?2 Now, we can await things. Fluent looks more like Entity Framework all of a sudden: let model = try await MyModel.find(id, on: req.db)

That’s already a much nicer API to work with, and it delivers on the promise of async/await: much simpler flow of code. I’m excited to untangle some of my more complex flatMap pyramids.

What I’m still curious about, and will need to do some testing with, is to see how well it does with concurrency. It’s a known issue in Entity Framework that you can’t run multiple requests against a single DbContext; what I’m wondering is if Fluent has this same limitation, or if I can safely do something like:

async let model1 = try MyModel.find(id1, on: req.db)
async let model2 = try MyModel.find(id2, on: req.db)
let result = MyResult(model1: await model1, model2: await model2)

That, I don’t yet know! I’ll have to do some experimenting and try it — which may be a future blog post. Still, even if I have to immediately await everything, the improvements to my code’s legibility will be worth it.

  1. Disclaimer: Swift 5.5 is, as of this writing, pre-release; similarly, the versions of Swift-NIO and Vapor that provide async APIs are pre-release. Don’t use these in production.
  2. Well, soon: this as another draft PR as of this writing.
Categories
Programming

Islands.swift

Back in undergrad, I did a lot of programming challenges – the sort of thing that shows up as a somewhat-contrived question in an interview. Great way to learn about automated testing, though, as in a school environment it’s basically an automated test suite where you aren’t allowed to see the tests.1

I’ve been wanting to brush up on algorithms a bit, and figured this would be a good way to do it. This time around, though, I’m using Swift; C++ was a fine language to learn, but I have exactly no desire to use it anymore.

So, the first problem I found whilst aimlessly googling was “Islands”. Given a grid of some sort (could be [[Bool]], although in this case I’ve done one as [[Character]], with '1' and '0' being the inputs2), count the number of islands in it.

An island, in this case, is any contiguous collection of '1' is in the grid, while all the the '0's are ocean.

Step One: Ask Questions

First question: what does ‘contiguous’ mean? Can we move diagonally, or only in the four cardinal directions?

Only the four cardinal directions, in this example.

Alright. Next, what happens at the edges? Do we Asteroids-style loop around, or is it just ‘out of bounds == ocean’?

Out of bounds is ocean.

Excellent! Means we don’t have any literal edge cases.

Final question before I start actually digging into the problem, how are we hooking into the test case?

protocol Islands {
  func numIslands(_ grid: [[Character]]) -> Int
}

Alright, simple enough. Let’s get cracking.

Step Two: Brainstorm

My first thought upon seeing a grid is “flood fill.” Now, this isn’t quite a flood fill, because the whole point is that it’s not all interconnected, but that at least gives me a starting point – for a flood fill, you want recursion. And you want to remember where you’ve been, so you can make a base case – otherwise, you’ll just loop forever. O(infinity) isn’t really ideal.

So, what’s the actual algorithm here?

Simple enough: for each point on the grid, check if it’s part of a new island. If it is, add one to our count of islands; if it isn’t, don’t. Move on to the next point on the grid.

Now, how do we check if it’s a new island? Also pretty simple: check if we’ve been here before; if we have, it’s not a new island. Then check if it’s an island; if it isn’t, it’s also not a new island. And, now that we know it’s a new island, we go recursive – mark that we’ve been to this spot, and flood fill our way across the neighboring tiles on the grid until we run out of island.

Step Three: Code

class Solution: Islands {
    private var visited: [[Bool]] = [] // (1)
    private var grid: [[Character]] = [] // (2)
    
    @discardableResult
    private func isNewIsland(x: Int, y: Int) -> Bool { // (3)
        guard x >= 0 else { return false }
        guard y >= 0 else { return false }
        guard x < grid.count else { return false }
        guard y < grid[0].count else { return false }
        if (visited[x][y]) { return false } 
        visited[x][y] = true
        if (grid[x][y] == "1") {
            // visit all neighbors
            isNewIsland(x: x+1, y: y)
            isNewIsland(x: x-1, y: y)
            isNewIsland(x: x, y: y+1)
            isNewIsland(x: x, y: y-1)
            return true
        } else {
            return false
        }
    }
    
    func numIslands(_ grid: [[Character]]) -> Int { // (4) 
        // precondition checks
        guard grid.count > 0 else {
            return 0
        }
        guard grid[0].count > 0 else {
            return 0
        }
        // reset visited state
        let subItem = Array<Bool>(repeating: false, count: grid[0].count)
        self.visited = Array<Array<Bool>>(repeating: subItem, count: grid.count)
        self.grid = grid
        var islandCount = 0
        for i in 0..<grid.count {
            for j in 0..<grid[i].count {
                if isNewIsland(x: i, y: j) {
                    islandCount = islandCount + 1
                }
            }
        }
        return islandCount
    }
}

Let’s go through this bit by bit.

  1. visited is key – we need to know where we’ve been. Unlike the folks making the problem statement, I actually know what my data type is – [[Bool]]
  2. This… is mostly for ease of passing it around. While I could write up my isNewIsland function in a purely-functional sense, with it taking in the coordinates, visited state, and grid, and then outputting the result… well, that’s kinda silly, really.
  3. isNewIsland is the bulk of the work. Let’s go through it.
    • The first four lines are checking that we’ve got valid input. This saves us from needing to check the range every time we call the method; if we try to do something that we can’t, it’ll just return false. Admittedly, I’m not checking that the grid properly exists before using it, but that’s what private is for – this is an implementation detail.
    • Next, check if we’ve visited this spot before. If we have, we’re done – it’s not a new island!
    • Before we go any further, mark this spot as visited! Important, because we may be recursing shortly, and we don’t want to get caught in an infinite loop.
    • Check if this is an island at all. If it isn’t, we’re done – this wasn’t a new island. If it is, though, we recurse. This is where the @discardableResult comes in – since we’ve got an early bail-out for “we’ve already visited this spot”, we know we’ll never be checking the same island twice, so we actually don’t care about the result of checking the neighboring spaces, we just need it to happen… so that they get marked as visited. And after we’re done marking the whole island as visited, we can finally return true, telling the caller (if they’re listening!) that this was a new island.
  4. Finally, our implementation of numIslands. Mostly it’s just checking for valid inputs – Swift can enforce at compile-time that nobody tries to pass us anything completely the wrong type, but it can’t force people to give us a grid with dimensions greater than 0 in either direction, so we need to check that ourselves. After that, we set up our visited as all-false, copy the grid, and loop through it, counting up our islands.

Step Four: Test

This part, I will mostly leave to your imagination; for myself, I just hit ‘run’ and let the automated tests do their thing. In an interview, Step Two should also include coming up with some test cases and running through them on your pseudo code – and remember to include one or two invalid inputs!

After that, if your tests pass, you could talk about changes you might make. Given my numerous remarks about [[Character]] instead of [[Boolean]], my thought would be to make it generic – have a grid of [[T]] and take a (T) -> Bool closure that tells you whether or not a grid point is an island. I’d also want to comment up the code a bit more, which I’ve neglected to do in this case as I’m writing a blog post around it instead.

Now, having already spoiled the answer, I’ll go ahead and mention that I tried this out at LeetCode; while this one may not be the most fun for you in the immediate wake of my explainer, they’ve got plenty of other solutions you could take a crack at, and a variety of languages to use. Give it a go, it’s kinda fun!

  1. Basically, the inverse of an “opaque box” analysis, if you think about it.
  2. And by “I’ve done one” I mean “the site that gave the example,” because I… wouldn’t use [[Character]] to represent a [[Bool]].
Categories
Playlist

Playlist of the Month: July 2021

This month has been very long, and terrifyingly hot. Nobody tell me that it’s August now, I don’t want to think about that yet.

Cologne – Haux on Something to Remember – EP

How It Was – Yoste on A Few Brief Moments – EP

We’ll Be Alright – Yoste on A Few Brief Moments – EP

DON’T TELL THE BOYS – Petey on Checkin’ Up on Buds – EP

Spaces – Jaymes Young on Spaces – Single

Arcade – Duncan Laurence on Arcade – Single

twentyfive – Yoste on twentyfive – Single

Oh Dear, Oh Beaux – beaux on A Love Letter To the Moments Spent Outside

Neon Medusa – The Midnight on Horror Show – EP

Flow – Vide on Flow – Single

Starlite – HOKO on Heathen

DIM – SYML on DIM – EP

BLACK TEETH – SYML on DIM – EP

Magic – Phillip LaRue on Night Swimming – Single

Rät – Penelope Scott on Public Void

Good in Red – The Midnight on Horror Show – EP

Coldplay (feat. Vic Mensa) – Mr Hudson on Coldplay (feat. Vic Mensa) – Single

Save Me – Majik on It’s Alright / Save Me – Single

Expectations – Joel Ansett on Expectations – Single

Magazines – Anson Seabra on Magazines – Single

Paranoid – Lani Rose on To: Keep You From: Hurting Me

Imported – Jessie Reyez & 6LACK on Imported – Single

Belong – slenderbodies on Komorebi

Never Surrender (feat. H. Kenneth) – Mike Emilio & Hypanda on Never Surrender (feat. H. Kenneth) – Single

Don’t Go Puttin Wishes in My Head – TORRES on Thirstier

Todo Estaba Bien – Carlos Sadness & Manuel Medrano on Tropical Jesus

Baci Dalla Tunisia – Mahmood on Ghettolimpo

Talata – Mahmood on Ghettolimpo

T’Amo – Mahmood on Ghettolimpo

I Only Go South – Yoste on I Only Go South – Single

Phonky Town – Playaphonk on Phonky Town – Single

b!!!rds – Sir Sly on The Rise & Fall of Loverboy

Gone Are The Days (feat. James Gillespie) – Kygo on Gone Are The Days (feat. James Gillespie) – Single

Los Tontos (Live at NPR’s Tiny Desk) – C. Tangana & Kiko Veneno on El Madrileño (Live at NPR’s Tiny Desk) – Single

All Eyes On Me – Bo Burnham on Inside (The Songs)1

Cherry – Chromatics on Cherry (Deluxe)

Bezos I – Bo Burnham on Inside (The Songs)

August – Flipturn on Citrona

Distorted Light Beam – Bastille on Distorted Light Beam – Single

Red – bennytheghost on Supersonic – EP

BYE – Jaden on BYE – Single

Sister – ferdinant. on Airplanes – EP

Your Power – Billie Eilish on Happier Than Ever

Politicians – Hayden Calnin on Politicians – Single2

Every Time – Landon Austin on Every Time – Single

Lost Cause – Billie Eilish on Happier Than Ever

who put blood in my drink? – SYML & sagun on who put blood in my drink? – Single

Follow You – Imagine Dragons on Follow You / Cutthroat – Single

Lean Into Life – Petey on Lean Into Life

Play Dead – Armen Paul on Play Dead – Single

barcelona – Winnetka Bowling League & Sasha Alex Sloan on barcelona – Single

The Only One – Chord Overstreet on Stone Man – EP

Skeletons – HARLOR on Letters To an X – EP

old cartoons – FJ Law on TAPE 13

Fingers Crossed – Trevor Daniel & Julia Michaels on Fingers Crossed – Single

Mi Gente – J Balvin & Willy William on Vibras

If I Go (Embers Edition) – Blakey on If I Go – Single

family – FJ Law on TAPE 14

Hurricane – Thirty Seconds to Mars on This Is War

War – Chance Peña on War – Single

Tangerine – Benicio Bryant on Tangerine – Single

Cruel Love – Teflon Sega on Cruel Love – Single

ME PASE (feat. Farruko) – Enrique Iglesias on ME PASE (feat. Farruko) – Single

Why Is Everyone Sad? – Yoste on Why Is Everyone Sad? – Single

Vampires – The Midnight & Magik*Magik on Vampires – Single5

Good Girls – CHVRCHES on Screen Violence

Motley Crew – Post Malone on Motley Crew – Single

Don’t You Look At Me That Way – flora cash on Chronically Beautiful – EP

  1. But seriously, if Burnham doesn’t win an Emmy for Inside, I… don’t know what I’ll do, really. Disregard the entire concept of the Emmys for the future, I suppose.
  2. I’ve yet to tune in to the entirety of the lyrics here, but the bits I do listen to feel about right for the title, which I like.
  3. “… where the normal people are. But don’t worry: I’m going to help you hide the fact that you’re high as shit.”
  4. My taste in music, at the moment, is definitely Very Autotune on the vocals.
  5. Definitely my favorite new addition this month; kinda reminds me of the Westworld theme.
Categories
Tools

The Self-Deprecation Jar

In the last few months, I’ve made surprisingly few changes to my home screen. One of them, though, was reorganizing what appears in the Shortcuts widget. I combined all my ‘playlist’ stuff into one Shortcut that checks what time it is and tries to guess what playlist I want, and then gives me a list of the options, in case it guesses wrong.1 With that additional space, though, I needed a new Shortcut to occupy the fourth slot. Can’t have an unbalanced home screen, after all!

What I eventually arrived at was the Self-Deprecation Jar. I can’t claim the idea as my own: I first came across it entitled “the self-depreciation jar,” but renamed it to reference self-deprecating humor.

The core idea is something roughly in the area of cognitive behavioral therapy, though I am emphatically not a mental health-care professional, so take what I say with a grain of salt.

The point, though, is that every time you say (or write, or think too loudly—pick a delineation that works well for you!) something negative about yourself, you put a dollar in the jar. (Or a quarter, or a penny—again, it’s meant as a very flexible system!)

I’ve found it a helpful thought technology: it draws attention, and imposes a negative penalty, on the sort of negative self-talk that’s far too easy to fall into. I suspect the former effect there is more meaningful than the latter, in the same way that people tend to have success toward their diet goals regardless of the diet, simply by paying more attention to what they’re eating.

Now, as a millennial, I own a great many mason jars… but I don’t actually own any cash. Instead, I put together a digital version of this in Shortcuts. In my implementation, I wanted to have the ‘cost’ be $1 put into my investment account—I’m still incurring an in-the-moment penalty, but the end result is a positive for Future Grey. Alternately, pull up a deserving charity and throw some money their way!2

Now, I do that investing via Acorns (shameless affiliate link!), which has a minimum of $5 for a transfer, so the Shortcut had to be a bit more complex than just an “Open App” action. Happily, I have Data Jar installed, which makes it nice and easy to store data across runs of a Shortcut. A little modular math later, and we’ve got a Shortcut that, every time you tap it, increments a counter; and every time that counter is a multiple of 5, it opens Acorns and throws me a notification to say “hey, that’s five, put in $5.”

After a bit of tinkering with the Shortcuts editor, I’ve arrived at this version, which will prompt you to customize as necessary—tweak the frequency, the message, and what app to open, and you’re off to the races.

And, hey. Be kind to yourself. Remember: You’re a lovely human being, and the world is a better place for having you in it.

  1. It’s an educated guess, really, considering the way I structure my playlists and my day.
  2. I’ve shared this idea here and there, and a fairly large Discord I’m a member of now has a custom :jar: emoji that we bring out when someone is being self-deprecating. I will take approximately no credit for that, though, as it’s my friend Madi who took it upon herself to be the spirit of kindness in the server and remind people to take care of themselves.
Categories
Review

“Fine Structure”

Sam Hughes

I actually read this almost immediately after “Ed”, and it makes for a heck of contrast. Where “Ed” is mostly pretty light and quick, this is all kinds of convoluted in terms of what’s going on with the plot. But it also does that thing that Hughes does really well—it spans a massive amount of time and space, and covers a staggering amount of ground.

The basic concept is truly excellent, though: what if, every time you tried some Cool Science Fiction Thing, it worked—once. And then never again.

It’s a really interesting constraint for a work of science fiction, as well: how can you do enough Cool Science Fiction Things for a full book, when the core concept is that those concepts are consumable? It makes for, as I said, a sprawling world that must be built out—larger than “Ed” or “Ra” had to be, and bringing in some definite “higher-dimensional beings would look a lot like Cthulhu, wouldn’t they?” energy.

The end result is, I can’t recommend this as readily as I did “Ed”, because it’s boggling at times. But it’s a great payoff, and ties together things you wouldn’t at all expect, much better than you’d expect. If that sounds good to you, give it a read.

Categories
Review

“Ed”

Sam Hughes

I’m reading Hughes’ work all out of order, but happily, there’s really no shared universe from story to story, so it’s not an issue. And, having now read all four of his books, I can say that “Ed” would be my recommended starting place, if you want to give it a go.1 It’s the lightest – a very silly beginning, and told by a narrator who’s further outside the story than any of his other works. While it’s not quite to that space opera feel, with the big events happening in the background and the story following regular-sized people just trying to make it through, it’s more human in scale.

It’s also very episodic most of the way through, so it’s easier to pick up and put back down for a while, if that’s your reading style, though there’s enough callbacks that you’ll be rewarded for going right through. (And, because this is a web-first piece of fiction, if you’re reading it online, a great deal of those callbacks are hyperlinks to the correct chapter – an easy way to catch yourself back up without devolving into “as you know, Bob” territory.)

All in all, “Ed” is a fairly short read, and a fun one. It’s got that characteristic “sprawling across space and time” feel that’s characteristic of Sam Hughes, but at no point do you feel like you need to stop and take notes to try to follow what’s going on. I quite enjoyed it. Give it a read, and if it really captures your interest, buy a copy!

  1. I’ve previous reviewed “Ra”, and will at some point write up my thoughts on “Fine Structure”, but my stance on “There Is No Antimemetics Division” is the simple “if you like SCP, you’ll like this; if you don’t know what that means, this isn’t a good starting point.”
Categories
Programming

Dev Blogs

I recently had someone ask for recommendations for dev blogs to fill out their RSS reader. After going through what I have in my RSS reader, I realized that this would make a good little post—these have all been helpful resources to me, so it seems likely they could be helpful to someone else. (I have also been informed by a friend of mine that I should include myself in this list, but I figure you’re already here. Go ahead and throw this site URL into your RSS reader, the feed is nice and discoverable.)

So, in no particular order, developer blogs:

  • Use Your Loaf posts fun little things, usually regarding iOS development.
  • The Always Right Institute does terrible, terrible things with Swift, and they’re consistently a delight to read.
  • SwiftLee writes very solid articles explaining various Swift language features, iOS API things, and general career tips.
  • Swift with Majid does weekly deep dives into SwiftUI… and may or may not be the inspiration for my recent spate of programming posts.
  • Swift by Sundell is one of the definitive Swift sites; not only does John Sundell write up plenty of great articles (see ‘Swift Fundamentals’ as a great starting point!), he also has a couple podcasts, if listening is more your style.
  • Reda Lemeden does some fun explorations of Swift, as well as more general things with his “This Week I Learned” posts.
  • Gui Rambo does some really neat explorations of what’s possible on iOS when he’s not cracking open the developer betas to see what features Apple forgot to feature-flag out of the build.
  • Povilas Staškus hasn’t posted in a while, but his posts are worth a back-read; the Jekyll-to-Publish migration was a fun one. And, hey, putting an infrequent poster in RSS is what RSS is for!
  • NSHipster is a venerable name in the iOS dev world; not as actively updated as it once was, but the back catalog is extremely solid, and will quite often be one of the first results when Googling various things.
  • Kristaps Grinbergs writes in a similar vein to Swift with Majid, with explanations of various parts of the SwiftUI API.
  • Julia Evans is a break from my very iOS-heavy list so far; she does comics and zines explaining general programming concepts, command line tools, and all sorts of stuff. Seriously good explanations, I recommend checking them out.
  • Brent Simmons blogs about NetNewsWire, life as a developer, and neat bits of history in the Apple world.
  • Steven Troughton-Smith may well be the expert on Catalyst apps; his recent post of various sample apps is a wonderful resource if you’re looking into bringing an iPad app to macOS.
  • Erica Sadun’s blog has recently been a lot of neat macOS automation stuff; she’s also a frequent contributor to Swift itself.
  • On Hacking With Swift, Paul Hudson provides excellent WWDC coverage and an amazing array of tutorials and documentation.1 If you’re just getting started, go through his free 100 Days of Swift and 100 Days of SwiftUI courses.

And, in addition to all those blogs, I also funnel email newsletters into RSS whenever possible. My top picks from that list are:

  • iOS Goodies provides a little commentary with a list of articles.
  • iOS Dev Weekly comes with much more commentary; I’m also a fan of the “and finally…” section, it’s a nice ending every week.
  1. Seriously, it’s so robust that one of my top bangs is !hws
Categories
Programming

Network Caching With Combine

I’m going to lead in to this post by saying that this isn’t a Definitively Correct way to do this; it works for my use case, but has some definitive issues. Still, in the interest of transparency—and not running out of content for this blog—I’m going to share it anyways.

Last week, I shared a fun little enum-based way of representing API endpoints in a type-safe manner. It’s also pretty easy to expand on to use as the key for an in-memory cache of endpoint results.

(This, by the by, is Caveat 1: it’s a purely in-memory cache, with nothing being persisted to disk. User quits out of your app? Cache is gone. For what I’m working on, though, this is the behavior we want, so it works well.)

Let’s get started with the actual API surface we want:

class ApiClient {
	func get<T: Decodable>(endpoint: Endpoint, reset: Bool = false) -> T? {
		...
	}
}

Now, the funky thing about how I’ve got this implemented is that this get method is idempotent – if you call it 10,000 times for the same Endpoint, it’s only going to spit out one network request, and will just continue returning nil until something is actually present. (Well, unless you’re setting reset to true – that’s really in there for debug purposes more than anything else. Give the API I’m using on this project, it’s almost never necessary.)

And, for use with SwiftUI, we want it to be called again when the network request finishes, so we’ll make it an ObservableObject.

Next, let’s work out how to store things to achieve that. The main thing we need is somewhere to store the network requests, and somewhere to store the results – the aforementioned in-memory cache. And, to get that “call the method again when the cache changes” behavior, we can make it @Published:

class ApiClient: ObservableObject {
	private var loaders: [Endpoint:AnyCancellable] = [:]
	@Published private var cache: [Endpoint:Decodable] = [:]
}

Now, to make Endpoint we need it to be viable dictionary key, which is pretty easy to accomplish:

extension Endpoint: Hashable { }

Finally, we need to implement the actual method. Without further ado:

class ApiClient: ObservableObject {
	private var loaders: [Endpoint:AnyCancellable] = [:]
	@Published private var cache: [Endpoint:Decodable] = [:]
	
	func get<T: Decodable>(endpoint: Endpoint, reset: Bool = false) -> T? {
		if reset {
			cache[endpoint] = nil
			loaders[endpoint] = nil // which implicitly calls loaders[endpoint]?.cancel(), via the deinit
		}
		if let _item = cache[endpoint], let item = _item as? T {
			print("\(endpoint): cache hit")
			return item
		}
		if loaders[endpoint] == nil {
			print("\(endpoint): cache miss; beginning load")
			loaders[endpoint] = URLSession.shared.dataTaskPublisher(for: endpoint.url) // 1
				.map(\.data)
				.decode(type: T.self, decoder: JSONDecoder()) // 2
				.receive(on: DispatchQueue.main)
				.sink(receiveCompletion: { (completion) in 
					print(completion)
				}, receiveValue: { (item) in 
					self.cache[endpoint] = item
				})
		}
		print("\(endpoint): cache miss; loading already in progress")
		return nil
	}
}

Two notes in there:

  1. You may want to swap out the use of URLSession.shared here for a parameter into the class’ constructor – it’ll make your testing a bit easier.
  2. Even more so than (1), you probably want to swap out JSONDecoder() here for something stored on the class – that decoder isn’t free to initialize!

Now, as I mentioned, this has some limitations. The first one I already went over – it’s a purely in-memory cache. The second is at the call site – since this is generic across Decodable, you have to annotate at the call site what the expected return type is, which isn’t the most ergonomic.

My actual thought on fixing that was very TypeScript-inspired – creating overloads that specify T based on the given endpoint. In Swift, this isn’t too difficult, just adding something like:

extension ApiClient {
	func getCategory(_ id: Category.ID, reset: Bool = false) -> Category? {
		get<Category>(endpoint: .category(id), reset: reset)
	}
}

Which, of course, can get a bit repetitive depending on the number of endpoints you have. But hey, it works for my use case, and it may be helpful to someone else, so: blog post!

As a post-credits scene of sort, the TypeScript version is a bit silly, and takes advantage of the absolutely bonkers way method overloads work in TypeScript. I’ll throw out some pseudocode:

class ApiClient{
	func get(endpoint: Endpoint.Category, reset: bool): Category?
	func get<T>(endpoint: Endpoint, reset: bool: T? {
		...
	}
}

Yes, method overloads like this are just declaring the method multiple times before the actual body. And yes, you can specify an individual enum case as a type.

Categories
Playlist

Playlist of the Month: June 2021

This month, Dolby Atmos suddenly became a thing in the streaming music world, and I’ve been having fun with it! If you can, give it a try, it’s neat. (Admittedly, most of the discernible difference is just a matter of “this is a nice remaster,” but still.)

Cologne – Haux on Something to Remember – EP

Slowly – ODIE on Slowly – Single

How It Was – Yoste on A Few Brief Moments – EP

Come On – Will Young on Echoes

We’ll Be Alright – Yoste on A Few Brief Moments – EP

DON’T TELL THE BOYS – Petey on Checkin’ Up on Buds – EP

Spaces – Jaymes Young on Spaces – Single

Arcade – Duncan Laurence on Arcade – Single

twentyfive – Yoste on twentyfive – Single

Lightning – Hayden Calnin on Lightning – Single

Oh Dear, Oh Beaux – beaux on A Love Letter To the Moments Spent Outside

I Can’t Lose You – Isak Danielson on Tomorrow Never Came

MONTERO (Call Me By Your Name) – Lil Nas X on MONTERO (Call Me By Your Name) – Single

On My Own (feat. Kid Cudi) – Jaden on ERYS

Neon Medusa – The Midnight on Horror Show – EP

Flow – Vide on Flow – Single

Into Your Arms – Vide on Into Your Arms – Single

Starlite – HOKO on Heathen

DIM – SYML on DIM – EP

BLACK TEETH – SYML on DIM – EP

Magic – Phillip LaRue on Night Swimming – Single

I Took a Pill in Ibiza, Youth (Acoustic Mashup) [feat. Chasing Taylor] – Landon Austin on Covers: Six

Astronaut In The Ocean – Masked Wolf on Astronaut In The Ocean – Single

Rät – Penelope Scott on Public Void

Good in Red – The Midnight on Horror Show – EP

Somewhere We Can Be Alone – Gunnar Gehl on Somewhere We Can Be Alone – Single

Symphony – Forester on Symphony – Single

The Stranger – The Midnight on Horror Show – EP

Ghost in Your Stereo – The Midnight on Horror Show – EP

Coldplay (feat. Vic Mensa) – Mr Hudson on Coldplay (feat. Vic Mensa) – Single

Save Me – Majik on It’s Alright / Save Me – Single1

Tiempo – Ozuna on Tiempo – Single

Attached – Shawn James on Attached – Single

Hold Me – Lani Rose on To: Keep You From: Hurting Me

Hell On Earth – Turbo on Hell On Earth – Single

Can’t Work You Out – Nathan Ball on Can’t Work You Out – Single

All We Have Is Now (feat. Trixie) – Blewbird on All We Have Is Now (feat. Trixie) – Single

False Confidence – Noah Kahan on Busyhead

Expectations – Joel Ansett on Expectations – Single

Magazines – Anson Seabra on Magazines – Single

Blinding Lights – The Weeknd on After Hours

In Your Eyes – The Weeknd on After Hours

Paranoid – Lani Rose on To: Keep You From: Hurting Me

Imported – Jessie Reyez & 6LACK on Imported – Single2

Belong – slenderbodies on Komorebi

u love u – blackbear & Tate McRae on u love u – Single

Revolver – bülow on Revolver – Single

Goosebumps – HVME on Goosebumps – Single

There Is A Light That Never Goes Out – The Smiths on The Queen Is Dead3

Never Surrender (feat. H. Kenneth) – Mike Emilio & Hypanda on Never Surrender (feat. H. Kenneth) – Single

Sunflower (Spider-Man: Into the Spider-Verse) – Post Malone & Swae Lee on Hollywood’s Bleeding

Late At Night – Roddy Ricch on Late At Night – Single

Like That – JP Saxe on Dangerous Levels of Introspection

Don’t Go Puttin Wishes in My Head – TORRES on Thirstier4

Todo Estaba Bien – Carlos Sadness & Manuel Medrano on Tropical Jesus

My Rajneesh – Sufjan Stevens on America – EP

Baci Dalla Tunisia – Mahmood on Ghettolimpo

Talata – Mahmood on Ghettolimpo5

T’Amo – Mahmood on Ghettolimpo

Karma – Mahmood & Woodkid on Ghettolimpo

Klan (Spanish Version) – Mahmood & DRD on Ghettolimpo

Kobra – Mahmood on Ghettolimpo

Out in LA – Aquilo on Out in LA – Single

I Only Go South – Yoste on I Only Go South – Single

GROWING PAINS – AJ Mitchell on GROWING PAINS – Single

Dinero – Trinidad Cardona on Dinero – Single

In the End – Linkin Park on Hybrid Theory6

Player 2 – Rory Webley on Player 2 – Single

Under the Covers (feat. DT James) – Blewbird on Under the Covers (feat. DT James) – Single

Phonky Town – Playaphonk on Phonky Town – Single7

b!!!rds – Sir Sly on The Rise & Fall of Loverboy

Gone Are The Days (feat. James Gillespie) – Kygo on Gone Are The Days (feat. James Gillespie) – Single

Los Tontos (Live at NPR’s Tiny Desk) – C. Tangana & Kiko Veneno on El Madrileño (Live at NPR’s Tiny Desk) – Single8

Ride – Amber Run on Ride – Single

Instant Crush (feat. Julian Casablancas) – Daft Punk on Random Access Memories

bad guy – Billie Eilish on WHEN WE ALL FALL ASLEEP, WHERE DO WE GO?

All Eyes On Me – Bo Burnham on Inside (The Songs)9

Cherry – Chromatics on Cherry (Deluxe)

Bezos I – Bo Burnham on Inside (The Songs)

August – Flipturn on Citrona

Welcome to the Internet – Bo Burnham on Inside (The Songs)

Distorted Light Beam – Bastille on Distorted Light Beam – Single

  1. if I listen to Majik enough, will they come back? I can dream
  2. “Some people call me six-lack”
  3. It’s really neat living in the future, because this song popped into my head the other day, and I just announced to the air that I wanted to hear it, and then, suddenly, there it was.
  4. This feels like my childhood, but in a way that doesn’t evoke nostalgia, somehow? It’s a very strange feeling, but I quite like it.
  5. I really enjoyed this whole album; I think Talata is my favorite, just because it’s the weirdest.
  6. I think this is the best Atmos remaster I’ve listened to so far.
  7. “This sounds like demons trying to tell you something.” – my sister
  8. This sounds like it shouldn’t be possible, what with the pandemic still ongoing, but it’s a delightful piece of music.
  9. If you haven’t yet watched Inside on Netflix, I heartily recommend it — it’s an incredible work of art. The fact that I got this song out of it, which is one of my favorites this month, is a nice plus.
Categories
Programming

Safe API Calls with Enums

I really hate runtime errors. I’m a firm believer in the concept that as many errors as conceivably possible should be caught at compile-time. It makes sense, after all: a compile time error for me means a few minutes of my time spent fixing it; a runtime error for my users means a few seconds of their time staring at an error… multiplied across however many users there may be. And, really, how often are you working on an app where you outnumber the users?

In this vein, I’ve been tinkering with how to handle network requests. The easy first thing to do was to swap out stringly-typed URLs for an enum, and Swift’s associated values make this a breeze:

enum Endpoint {
	case globalConfiguration
	case category(Category.ID)
	case product(Product.ID)
	...
}

Your definitions may vary – this is, of course, very specific to the actual use case. But look at that – not only do we have clear representations of the various endpoints available, we can even link the variable components of those URLs to being the proper type for the model we expect to get back. How’s that for self-documenting code?

Next, we need a way to convert from this lovely enum to the actual URL we’re going to use to make our requests. I’ve split this up a little bit:

extension Endpoint {
	var pathComponents: String {
		switch self {
			case .globalConfiguration:
				return "/config.json"
			case .category(let ID):
				return "/category/\(ID).json"
			case .product(let ID):
				return "/product?id=\(ID)"
			...
		}
	}

	var url: URL {
		#if DEBUG
		return URL(string: "https://dev.my.app")!.appendingPathComponent(pathComponents)
		#else
		return URL(string: "https://api.my.app")!.appendingPathComponent(pathComponents)
		#endif
	}
}

Et voila, converting to our known URLs, and a nice little “automatically use the dev server in debug mode” check, while we’re at it.

Now, depending on how you’re organizing your networking code, we can tweak the API surface here a little bit to make things clearer. Add those extensions in the same file as the code that makes the network requests, and declare them fileprivate – and now, you’ve got an automatic reminder from the compiler if you start writing some URL-based networking code outside of the network component. No more leaky abstraction!