2024-03-28

  • I’ve become fairly anti exceptions” in programming languages. I think they, broadly speaking, negatively impact readability and (ironically) safety in most languages. Of course, you very much need a way to represent failure” modes in the course of execution. But I increasingly find myself uncomfortable with approaches like Python, where you wrap code that can fail in try/catch for specific exceptions. I think my aversion largely boils down to a perspective on programming that centers a networked services” model of a process. In that model, a large percentage of function calls can/will hit the network in some way to fetch/send data. And each of those calls can fail in some set of known/unforeseen ways. So, in Python as an example, you end up with a lot of try/catch blocks littering the codebase. Furthermore, in many of these blocks, multiple calls could raise a given exception, and it’s not clear which one comes from which place. Additionally, in the event that some function you call changes in the future to throw a new exception, your process will just explode if you don’t update your code to appropriate catch the new exception. In a networked services” view of processes, that can lead to cascading failures when something goes wrong in one component. Unfortunate when your language pushes you into those kinds of designs. A better model ends up looking like Elixir/Erlang in the strictest sense and Kotlin/Go in other cases. That great GOTO 2019 Talk talk about Elixir highlights the benefits of Elixir for networked services.” Elixir/Erlang avoid littering the code with try/catch blocks by enforcing a particular style of message passing between processes that more cleanly encapsulates error conditions and process isolation. I still have yet to really try Elixir (though am looking forward to playing with it soon, to learn more about the details of its tradeoffs as a language), so will leave my commentary there for now. On the Kotlin front, I have more experience, and love what they’ve done with the language. Kotlin has this really beautiful pattern where you combine these concepts of sealed interfaces/classes” with compile-time-verified, exhaustive switch statements. These features allow you to write fault/error-tolerant code without relying on exceptions. You can model all the possible outcomes of a given function using a sealed class” and then client code can guarantee they handle all known cases by using the when statement in its exhaustive form. The compiler will verify that the when statement has code to handle all the enumerated outcomes. And when a new thing is added that changes the set of outcomes in a later version of your software, clients can upgrade and be told by the compiler all the locations that need updating. It’s a beautiful way to handle network-focused code in a compile-time typesafe way. It lends itself to highly expressive, linear, easily readable code. I should probably give examples here, but I just wanted to use this space to jot down some thoughts/feelings about this topic, as I’m doing a decent amount of Python programming and finding myself wishing for a better approach to error handling/type safety. It feels like Kotlin got so much right.

Date
March 28, 2024