An animated Firewatch wallpaper for macOS

A while ago Daka made a series of great Firewatch wallpapers. Then dynamicwallpaper.clup user Jagoba turned them into a nice macOS dynamic wallpaper for macOS.

However the previous dynamic wallpaper was timed to the clock – and so didn’t account for the current sunrise and sunset time at your location. Which means that during winter, the wallpaper would still be a bright blue while outside the night was already dark.

So I edited the wallpaper metadata, to make them dependant on the sun position rather than the time of the day.

Finding out sun coordinates

On macOS, making the wallpaper dependant on the sun position means adding fictive sun altitude and azimuth to the images.

Rather than computing the sun angle and elevation ourselves, an easiest way is to set the time and GPS position of the pictures. Wallpaper-makers can then automatically convert this time+location data into the sun angle and elevation.

First, the location. I chose the wallpapers to be in Shoshone National Forest, for obvious reasons.

Then, the time. The game takes place during summer 1989, so it seems like a good fit. I’ve arbitrarily chosen the 3rd of July 1989, which roughly matches the first days of the game.

Now we need to time the different wallpapers. For the middle of the night, this is easy enough: jut set the time to midnight. But for the various sunrise and sunset pictures, this is harder to define.

Which leaves us with interesting questions. For instance: at what time was the first light of dawn visible in Shoshone Forest on the 3rd of July 1989?

Turns out there are softwares that can tell the light of the day at a given time and location: planetariums. For instance, by using the atmosphere simulation of stellarium-web.org, we can get a rough idea of the time of each picture.

Here are the exact timing used for the wallpapers:

macOS wallpaper engine will then use these informations to display the correct picture to you, depending on the season at your place. A little trickery though: if the middle-of-the-day picture is timed at 12:00, it’s sun elevation will be very high. Which means that during winter, when the sun is low, the wallpaper engine will simply skip this picture. To make sure the middle-of-the-day will be displayed even during winter, I inserted instead two pictures, timed early in the morning and late in the afternoon.

End result

So here you are! A nice Firewatch dynamic wallpaper for macOS, adjusted to the sun’s position. Enjoy!

EDIT: I fixed some timing issues, and re-uploaded a new version under the name “Firewatch Sun”. It makes sure the ‘Sunset’ variant doesn’t start displaying too soon in the afternoon, even during winter.

Experiments with Gradual Typing in Ruby – Part 1

I recently started toying with Gradual typing in Ruby.

Of course, Ruby is already typed: messages are dispatched to objects depending on their type. That’s dynamic typing, enforced at runtime.

But for larger programs, it can be useful to have some static type-checking, that can be enforced by a type-checker without running the whole program.

Enter type annotations. By adding some explicit informations about the expected types in our program, a type-checker will be able to catch some errors using a static analyzer.

What does it look like?

Gradual typing is not standardized in Ruby yet (although some efforts are ongoing). So there are different tools available. Currently, the best way to add gradual typing to Ruby programs seems to be Sorbet.

Here’s a simple Sorbet example, using an existing Ruby function:

def to_hex(i)
  "%x" % i
end

We can add type informations to this function using the sig function decorator:

extend T::Sig

sig {params(i: Integer).returns(String)}
def to_hex(i)
  "%x" % i
end

Granted, the syntax is a bit weird. But it has the merit of being valid Ruby code, which allows it to be accepted by the standard Ruby parser without modifications. And like many syntaxes that seem unusual at first, our eyes quickly get used to it (hi, Objective-C square brackets).

Letting “Gradual” shine

Now, the neat thing with gradual typing is that you don’t have to provide type informations everywhere. This is useful in many ways.

First, you may start adding type checks to an existing codebase. In that case, declaring all types from the start can be a daunting task. Fortunately, in the absence of types, the type-checker will consider that we know what you’re doing. Which means we can start adding a few types here and there, and already have useful type-checks – but the parts of the program which are still type-annotations-free will not results in warnings or errors.

Second, a lot of Ruby elegance and fun comes from its dynamic nature. Sometimes the most elegant way to express some code is to use dynamic method resolution, or other dynamic-oriented constructs which cannot be type-checked. And this is okay! In that case, gradual typing means you have an escape-hatch: as types are not mandatory, they just won’t be type-checked. We can get the benefits of type-checking for 95% of the code, and still use neat dynamic features in the remaining 5%.

And last, at times it can be useful to just experiment and prototype some code quickly, to see how the structure would look. In these cases, you won’t have to fight your way through types: you can just omit type annotations, and quickly let the code flow without thinking too much about production-ready reliability.

Experimenting with a standalone Ruby script

I’ve never used Sorbet before, so I wanted to start small, and get used to the type system.

Fortunately, the Sorbet website provides an online playground: just type in some Ruby code, add some type annotations, and the type checker will start telling you what’s right and wrong with the types you provided. Neat.

I used a standalone Ruby script I wrote some times ago. This script reads assembly source code and debug symbols from the local filesystem, and infers from this the probable location of more debug symbols. Here the initial version of the script, and the final version after finishing adding types.

When I copy-pasted the script, without adding anything yet, Sorbet immediately told me about two errors: <function> is not available on NilClass (https://srb.help/7003).

Wow: it detected that in two different places, my code was sending a message to a potentially nil object. And gave me an URL to learn more about the issue.

How to fix this? I followed the URL, and a well-written document explained me that I could either:

So the two reported errors were easy to fix. I was quite impressed that Sorbet found two relevant mistakes without even starting to add types. And even more impressed that it not only does nil-checks, but also type propagation (that is, when some code checks if a value is nil, Sorbet considers that after this point the variable can not longer be nil).

Adding types

After this, I started adding type annotations to a single method (a constructor). That was easy enough: just a matter of adding the correct sig {…} incantation.

But right after that, Sorbet told me about a new error: my signature stated that the method argument was a String, but elsewhere I was calling the constructor with a T.Nilable(String) – that is, an object that may be nil. Interesting. Like before, to fix it, I had to add the proper nil check before calling the constructor.

I then gradually added type annotations to more methods, and found it almost fun. I had the feeling that I was strengthening my program, and uncovering the hidden assumptions that had been there before.

All of this went rather smoothly (except having to convert Ruby Structs, unsupported by Sorbet, into T::Structs). The weird syntax quickly became bearable, and eventually even read like being a part of the documentation.

Refactoring

In the end, this even led me to write better code. For instance, consider this function :

class Address
  def self.from_string(str)
    bank, offset = str.split(':')
    self.new(bank.to_i(16), offset.to_i(16))
  end
end

It extracts the two components of a semicolon-separated string – like 03:4A2F.

When adding type annotations, Sorbet initially told me “Hey, offset.to_i(16) is not valid on NilClass”. Because of course, it detected that if the input string is badly formatted, offset may be nil.

So I quickly wrapped the value in T.must(…), to silent the warning away. After all, there’s not so much we can do to prevent badly-formatted input; crashing at runtime seems a sensible option.

class Address
  sig {params(str: String).returns(Address)}
  def self.from_string(str)
    bank, offset = str.split(':')
    self.new(T.must(bank).to_i(16), T.must(offset).to_i(16))
  end
end

But wait, there’s better than crashing at runtime: and that’s “crashing at runtime with a meaningful error message”. What if we rely on nil-propagation to write instead:

class Address
  sig {params(str: String).returns(Address)}
  def self.from_string(str)
    bank, offset = str.split(':')
    raise "Invalid address format" if bank.nil? || offset.nil?
    self.new(bank.to_i(16), offset.to_i(16))
  end
end

Nice: a bad input now gives us a readable error message. And we can even remove the T.must checks, because, thanks to nil-propagation, Sorbet is now sure that offset.to_i(16) is not called on nil.

So far

After toying with the Sorbet’ playground, here are my first impressions:

The good

The bad

The ugly

What’s next

For now I haven’t tried to type-check a program locally, nor to type-check code that relies on external gems.

So my next step is probably to add some minimal type-checking to a small Rails app, and see how Sorbet’s tooling deals with the many dynamic constructs of the framework.

La Zone du dehors et le management par objectifs

Il y a quelques mois, j’ai fini La zone du dehors d’Alain Damasio. J’en suis ressorti avec des impressions mitigées, qui ont fini par se décanter. Pour une bonne partie, j’ai eu l’impression que la narration avait du mal à savoir où elle allait, que les séquences se juxtaposent dans une certaine confusion thématique – et que même si j’ai du mal avec les arcs narratifs lisses et stéréotypés, un peu de cohérence ne fait pas de mal non plus.

Mais y’a quelques points qui m’ont bien intéressés. En vrac :

Le vitalisme

Une des choses que La zone du dehors a clarifié pour moi, c’est que Damasio me semble attaché moins à l’anarchisme qu’à un genre de vitalisme. “Oui, tout foisonne, tout bouillonne, ça pose plein de problèmes mais c’est génial”.

J’ai l’impression que cette appréciation du vitalisme se trouve aussi dans Les furtifs, et dans une moindre mesure dans La horde du Contrevent – mais aussi beaucoup dans ce que Damasio a pu écrire sur la zad de Notre-Dame des Landes il y a quelques années.

Et ça m’aide à comprendre pourquoi tout ne me parle pas dans les récits de Damasio : le vitalisme c’est sa came à lui, et pas trop la mienne. Et que y’a aussi d’autres choses que ça dans l’anarchisme (genre des courants qui sont plus “l’ordre moins le pouvoir”). Et que c’est bien aussi.

Le patriarcat

Ouch. La zone du dehors, en terme de féminisme, c’est tendu. Et j’ai l’impression que c’est un point aveugle des écrits de Damasio depuis longtemps.

Je renvoie ici à la série d’articles (encore en cours) de Mélissa et Lunar, qui ont fait une analyse des Furtifs sous ce prisme, avec plein de dataviz : https://dérivation.fr/furtifs/.

La société de l’évaluation

Les passages sur la société de l’évaluation m’ont bien parlé. En bonne partie parce que pour moi ça donnait du sens à ce qui se passe dans le néo-management qui se retrouve fréquemment dans la startup-nation.

Vous savez, c’est cette forme de management à la cool où l’évaluation est omni-présente. Toutes les deux semaines, un one-to-one avec un manager. Toutes les six semaines, faire une liste d’objectifs hyper-précis à atteindre – et définissez des cibles chiffrées. Tous les six mois, une “review à 360°” où vous êtes évaluées par des managers et des pairs – dans l’espoir d’obtenir une augmentation qui ne viendra jamais. Et en permanence, devoir participer aux nombreuses évaluations de vos collègues. Mais c’est cool, parce que c’est vous-même qui définissez vos objectifs, tout ça.

Évidemment, tout ça ressemble fortement au Clastre, le grand système de classement social général de La zone du dehors. Et ce qu’en dit Damasio dans la grande tirade deleuzo-foucaldienne, c’est que l’objectif de tout ça est de re-former la personnalité des individus.

Il s’agit bien sûr d’une part les individualiser (chacun essaie de suivre ses objectifs le nez dans son guidon, ça atomise), mais même au sein de l’individu, de découper chaque individu en compétences, en traits de caractères désirables, de diviser l’individu lui-même. Et ensuite l’évaluation des traits permet de dissoudre en partie l’individu, et à la place d’encourager les traits désirables par l’entreprise / l’État / etc.

Au même moment, dans ma vie, j’avais eu vent de discussion dans une startup de la fintech française où des managers commentaient des traits de personnalité. Par exemple « Finalement, toi tu sais défendre tes idées, mais tu respectes aussi l’autorité quand il faut », des choses de ce genre.

Mais en fait c’est des discours complètement performatifs : ça sert pas à remarquer ce qui est bien, mais à dégager un trait, et à dire aux individus « Ça serait bien que tu cultives ça, c’est valorisé par le management, ça t’aidera à avoir une augmentation aux prochains 6 mois. »

Bref, cette idée de “Le micro-management (même auto-organisé) sert à non seulement individualiser, mais séparer les individus eux mêmes en plusieurs traits, et à les modeler” ça m’a semblé éclairant.