Weeknotes 61
Marginal at best
-
Last week I got a notification congratulating me for spending 14 years on Twitter. That doesn’t seem like something worth celebrating but it did provoke two otherwise unrelated thoughts about Twitter.
-
Twitter thought one: I remember having dinner in San Francisco with Twitter cofounder Evan Williams and internet pundit Keith Dawson in 1999. I was living in Silicon Valley at the time and knew Keith through TBTF, which I’d been helping out with by writing odd bits of Perl and PHP. He was in town for something and invited a small group of friends to join him for a meal.
I don’t have any photos of it (which seems ridiculous now but was of course completely normal for 1999) and I threw out Evan’s business card when I moved house years later, but I do remember Keith introducing us and — very kindly — encouraging Evan to hire me to help with the new product he’d just launched called Blogger.
I don’t think Evan needed to hire anyone at that point and presumably wouldn’t have considered me even if he did, but that’s still probably the moment in my life when I came closest to being involved in the start of something really big. Because, you know, theoretically, if I’d worked for Pyra Labs then I could have ended up working for Google, and then Odeo, and then Obvious, and then Twitter. That’s a thing which could in principle have happened.
Evan smiled politely.
-
Twitter thought two: I use Tweetbot everywhere and when Tweetbot 6 came out in January I assumed I’d need to sign up for a subscription, but I’ve been trying the restricted free version since then and I think it’s actually an improvement.
It’s essentially a read-only Twitter client and, it turns out, that suits me fine. I do occasionally open a tweet in Safari so I can like it or reply to it, but the small amount of extra friction makes it less likely that I’ll retweet something in anger or find myself transformed in my bed into a reply guy.
Maybe they should make all the features available by default and charge a subscription to enable read-only mode! That is my joke for this week.
-
I tried playing Maquette but couldn’t get into it. It has the same conceit as A Fisherman’s Tale which I’ve already played, and the presentation felt more like a parody of an indie walking simulator than a real game, so I didn’t stick with it.
-
I have upgraded my anti-static coffee technique from a wet teaspoon to a little spray bottle as advised in this video. The bottle is indeed slightly better than the spoon because it doesn’t require running water and keeps the moisture on the upper surface of the beans which prevents them from sticking to the inside of the weighing container when I tip them into the grinder. Also I don’t have to get a spoon out.
I realise these improvements sound utterly marginal at best, but this change made me realise that any tiny increase in convenience is magnified by how sleepy and confused and clumsy I am when making coffee in the morning. So I agree it seems silly but very slightly reducing the faff actually does make a noticeable difference to how much I enjoy the first few minutes of the day.
-
Yesterday was the one-year anniversary of my last ever breakfast with friends at the Shepherdess. I expect we’ve all got plenty of “one year since…” dates coming up around now, but this one felt a bit sad, not least because several of the people whose company I enjoyed at those breakfasts don’t even live in London any more.
Will everybody I know eventually have children or move away or both? It’s all just a normal part of life, but I’m interested to see if it changes how I feel about London in The After. Let’s get there safely first I suppose.
-
Over the weekend I was writing some testing code in Ruby and found myself wanting a general way to assert that two different implementations of a boolean function have the same behaviour. The only practical way to decide this for sure is to check that the functions’ outputs agree on all inputs, but that approach becomes unwieldy when the number of parameters is large.
A compromise is to randomly sample a large but manageable number of test inputs from the much larger space of all possible inputs, but Ruby’s
Array#product
eagerly produces an “all possible inputs” array which is way too large to be usable for sampling in this case.So I wrote a sort of lazy Cartesian product implementation which knows how to randomly access any of its elements without enumerating them, and can be quickly sampled for as many representatives as needed regardless of size:
class Product attr_reader :size def initialize(parts) self.parts = parts self.part_sizes = parts.map(&:size) self.size = part_sizes.inject(1, :*) end def slice(index) parts.zip(part_indexes_for(index)).map { _1.slice(_2) } end def sample(n) Enumerator.new do |yielder| sample_indexes(n).each do |index| yielder.yield(slice(index)) end end end private attr_accessor :parts, :part_sizes attr_writer :size def part_indexes_for(index) Enumerator.new do |yielder| part_sizes.each do |part_size| index, remainder = index.divmod(part_size) yielder.yield(remainder) end end end def sample_indexes(n) if size <= n size.times else Enumerator.new do |yielder| n.times do yielder.yield(rand(size)) end end end end end
It works recursively, so it’s easy to compose small products to make larger ones:
>> bits = 0, 1 => [0, 1] >> bytes = Product.new(8.times.map { bits }) => #<Product @parts=[[0, 1], [0, 1], [0, 1], [0, 1], [0,... >> bytes.size => 256 >> bytes.sample(5).each { |byte| puts byte.inspect } [0, 1, 0, 0, 0, 1, 0, 0] [0, 1, 0, 1, 0, 1, 1, 1] [1, 1, 1, 1, 0, 1, 1, 1] [1, 0, 1, 1, 0, 0, 1, 0] [0, 1, 1, 1, 0, 1, 1, 0] >> possible_args = Product.new([bits, bytes, bytes, bits]) => #<Product @parts=[[0, 1], #<Product @parts=[[0, 1],... >> possible_args.size => 262144 >> possible_args.sample(5).each { |args| puts args.inspect } [0, [0, 0, 1, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 1, 0, 1], 1] [1, [0, 1, 1, 0, 0, 0, 1, 1], [1, 1, 1, 0, 1, 0, 0, 1], 1] [0, [0, 1, 0, 1, 1, 0, 1, 1], [1, 0, 0, 1, 0, 1, 1, 1], 0] [1, [1, 0, 1, 0, 0, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 1], 0] [0, [1, 1, 0, 1, 0, 1, 1, 0], [1, 0, 0, 1, 1, 1, 0, 1], 0]
I’m pretty happy with this and it works well enough for my purposes. I was tempted to use a block cipher to properly enumerate a random permutation of the indices instead of the hack of repeatedly picking (perhaps duplicate) random ones, but that felt like it would turn into a rabbit hole and I don’t really care about uniqueness anyway.
-
I nearly didn’t include the jam content last week but I’m glad I did because it led to some good jam chat on the socials. You can’t go wrong with jam.