From my own personal experience dealing with languages that handle this on three different ways:
In C#, you have exceptions and need to try..except them or be bugged by unhandled exceptions. Knowing what can fail requires some digging and it's often necessary to go through additional documentation when the API is not clear or is a black box;
In Go, by convention, errors are types and there's no try..catch/except construct. It feels weird, but over time it makes you consider errors in your design. "An error happened" becomes more important than "what error happened" over 90% of scenarios, and the difference from an error to an expection is also made obvious by making exceptions panics. The disadvantage here is that you need to adopt a "hive mindset" to do this thing property and maybe that's why Go peeps are seen as members of a cult;
In Scala, you follow the monadic approach towards exceptions. It's very elegant, reads clearly, follows a great flow. But it becomes difficult for people that need to read the code later, if they need to touch a certain code path that can raise an intermediate exception.
---
As such, from the standpoint of "It's more important to deliver features using simple code that people will be able to read later, and that allows us to move quicker", Go is still my personal choice, although it looks kinda "primitive" (and that's the whole point of the language). For the following reasons:
- readable code for posterity is more important than clever code;
- clever code makes you feel good once you write it, but becomes a burden once you have to return to it;
- for the same reason that we read from left to right, it's important to understand execution flow from beginning to end. The various `if err = nil` feel weird, but we are reading a description of what our program is supposed to do, and errors are part of that description.
I feel you! The problem with languages like C# or JVM based languages is that they need to deal with legacy. Legacy not in the sense of legacy code, but in the sense of language design decisions made in its conception. After so many years doing something in a certain way, there's no going back to drop a certain feature and make people do it differently. At this point many relevant libraries and frameworks are built around such features, and it's practically impossible to change. Go made the right decision of dealing with errors as values. Functional languages tend to favor result types instead of exceptions, which is as good as Go if not better (IMHO since the type system is more powerful and expressive). I just wished Go had an error handling that could compose nicely. I think there's an opportunity here to have a language more "safe" targeting Go's platform and coexisting, like C# and F#, for example.
From my own personal experience dealing with languages that handle this on three different ways:
In C#, you have exceptions and need to try..except them or be bugged by unhandled exceptions. Knowing what can fail requires some digging and it's often necessary to go through additional documentation when the API is not clear or is a black box;
In Go, by convention, errors are types and there's no try..catch/except construct. It feels weird, but over time it makes you consider errors in your design. "An error happened" becomes more important than "what error happened" over 90% of scenarios, and the difference from an error to an expection is also made obvious by making exceptions panics. The disadvantage here is that you need to adopt a "hive mindset" to do this thing property and maybe that's why Go peeps are seen as members of a cult;
In Scala, you follow the monadic approach towards exceptions. It's very elegant, reads clearly, follows a great flow. But it becomes difficult for people that need to read the code later, if they need to touch a certain code path that can raise an intermediate exception.
---
As such, from the standpoint of "It's more important to deliver features using simple code that people will be able to read later, and that allows us to move quicker", Go is still my personal choice, although it looks kinda "primitive" (and that's the whole point of the language). For the following reasons:
- readable code for posterity is more important than clever code;
- clever code makes you feel good once you write it, but becomes a burden once you have to return to it;
- for the same reason that we read from left to right, it's important to understand execution flow from beginning to end. The various `if err = nil` feel weird, but we are reading a description of what our program is supposed to do, and errors are part of that description.
---
Regardless, as always, great article!
I feel you! The problem with languages like C# or JVM based languages is that they need to deal with legacy. Legacy not in the sense of legacy code, but in the sense of language design decisions made in its conception. After so many years doing something in a certain way, there's no going back to drop a certain feature and make people do it differently. At this point many relevant libraries and frameworks are built around such features, and it's practically impossible to change. Go made the right decision of dealing with errors as values. Functional languages tend to favor result types instead of exceptions, which is as good as Go if not better (IMHO since the type system is more powerful and expressive). I just wished Go had an error handling that could compose nicely. I think there's an opportunity here to have a language more "safe" targeting Go's platform and coexisting, like C# and F#, for example.