Go does have sum types — but the syntax is awkward and a bit transparent, so many don't recognize it as being there, and those that do don't love using it.
This forms a closed set of types (A, B, nil -- don't forget nil!) but the compiler doesn't understand it as such and complains that the following type-switch is not exhaustive ("missing return"):
func Foo(s SumType) bool {
switch s.(type) {
case A: return true
case B: return true
case nil: return true
}
}
Also, you, the package author, may know what constitutes SumType, but the consumers of your package don't, at least not without source. Moreover, you can spread A, B, and any other implementations of SumType across many source files, making it hard to answer the question even with source. This is even a problem for the standard library, just consider go/ast and its Decl, Expr, and Stmt interfaces, none of which document what types actually implement them.
Right — While it does has sum types, it doesn't have some other features found in other languages.
But, of course, if one wanted those features they would talk about those features. In this discussion, we're talking specifically about sum types, which Go most definitely does have.
> nil -- don't forget nil!
This is why alternative syntax has never been added. Nobody can figure out how to eliminate nil or make it clear that nil is always part of the set in a way that improves upon the current sum types.
interfaces in go aren’t types, so no, that’s not a sum type, it’s just an interface.
The set of objects that can fulfill that interface is not just string and int, it’s anything in the world that someone might decide to write an isSumType function for.
> it’s anything in the world that someone might decide to write an isSumType function for.
No. Notice the lowercase tag name. It is impossible for anyone else to add an arbitrary type to the closed set.
Unless your argument is that sum types fundamentally cannot exist? Obviously given a more traditional syntax like,
type SumType tagged {
A | B
}
...one can come along and add C just the same. I guess that is true in some natural properties of the universe way. It is a poor take in context, however.
It should be the same handling as all other types. If it feels clunkier than any other type, you've not found a good design yet. Keep trying new ideas.
Well two things to me feel clunky, first is less serious but leads to lots of verbosity:
1. if err != nil is verbose and distracting and happens a lot. I'd prefer say Ian Lance Taylor's suggestion of something like this where you're just going to return it vs standard boilerplate which has to return other stuff along with the error:
// ? Returns error if non-nil, otherwise continue
data, err := os.ReadFile(path) ?
// Current situation
data, err := os.ReadFile(path)
if err != nil {
return x,y,z,err
}
The second is a problem of culture more than anything but the stdlib is to blame:
2. The errors pkg and error interface has very basic string-based errors. This is used throughout the stdlib and of course in a lot of go code so we are forced to interact with it. It also encourages people to string match on errors to identify them etc etc. Yes you can use your own error types and error interfaces but this then creates interop problems and inevitably many pkgs you use return the error interface. I use my own error types, but still have to use error a lot due to stdlib etc. The wrapping they added and the annotation they encourage is also pretty horrible IMO, returning a bunch of concatted strings.
So these are not things that end users of the language can fix. Surely we can do better than this for error handling?
> if err != nil is verbose and distracting and happens a lot.
if err != nil is no more or less verbose than if x > y. You may have a point that Go could do branching better in general, but that isn't about errors specifically.
If there is something about errors that happening a lot then that still questions your design. Keep trying new ideas until it isn't happening a lot.
> Surely we can do better than this for error handling?
Surely we can do better for handling of all types? And in theory we can. In practice, it is like the story of generics in Go: Nobody smart enough to figure out a good solution wants to put in the work. Google eventually found a domain expert in generics to bring in as a contractor to come up with a design, but, even assuming Google is still willing to invest a lot of money in the new budget-tightening tech landscape, it is not clear who that person is in this case.
Ian Lance Taylor, as you mention, tried quite hard — with work spanning over many years — in both in both cases to find a solution, which we should commend him for, but that type of design isn't really his primary wheelhouse.
> if err != nil is no more or less verbose than if x > y. You may have a point that Go could do branching better in general, but that isn't about errors specifically.
In practice though, there's not nearly as many cases where someone needs to repeat `if x > y { return x }` a bunch of times in the same function. Whether the issue is "about errors" specifically doesn't really change the relatively common view that it's an annoying pattern. It's not surprising that some people might be more interested in fixing the practical annoyance that they deal with every day even if it's not a solution to the general problem that no one has made progress on for over a decade.
> there's not nearly as many cases where someone needs to repeat `if x > y { return x }` a bunch of times
In my evaluating of a fairly large codebase, if err != nil makes up a small percentage of all if statements. I think you may have a point that branching isn't great, but I'm still not sure trying to focus that into errors isn't missing the forest for the trees.
> it's an annoying pattern.
But, again, if it is so annoying, why is it the pattern you are settling on? There are all kinds of options here, including exception handlers, which Go also supports and even uses for error handling in the standard library (e.g. encoding/json). If your design is bad, make it better.
> It's not surprising that some people might be more interested in fixing
If they were interested in fixing it, they'd have done so already. The Go team does listen and has made it clear they are looking for solutions. Perhaps you mean some people dream about someone else doing it for them? But, again, who is that person going to be?
Philip Wadler, the guy who they eventually found to come up with a viable generics approach, also literally invented monads. If there was ever someone who might have a chance of finding a solution in this case I dare say it is also him, but it is apparent that not even he is willing/able.
> In my evaluating of a fairly large codebase, if err != nil makes up a small percentage of all if statements. I think you may have a point that branching isn't great, but I'm still not sure trying to focus that into errors isn't missing the forest for the trees.
I don't agree with the premise that a frustrating pattern has to comprise a large percentage of the instances of the general syntax for people to want to change it. I can tell you do, but I don't think this is something that people will universally agree with, and I'd argue that telling people "you can't have the opinion you have because it doesn't make sense to me" isn't a very effective or useful statement.
> But, again, if it is so annoying, why is it the pattern you are settling on? There are all kinds of options here, including exception handlers, which Go also supports and even uses for error handling in the standard library (e.g. encoding/json). If your design is bad, make it better.
Empirically, people don't seem to think they have better options, or else they'd be using them. If you try to solve someone's problem by giving them a different tool, and they still say they have the problem even with that, you're probably not going to convince them by telling them "you're doing it bad".
> If they were interested in fixing it, they'd have done so already. The Go team does listen and has made it clear they are looking for solutions. Perhaps you mean some people dream about someone else doing it for them? But, again, who is that person going to be?
> Philip Wadler, the guy who they eventually found to come up with a viable generics approach, also literally invented monads. If there was ever someone who might have a chance of finding a solution in this case I dare say it is also him, but it is apparent that not even he is willing/able.
I'd argue there have been plenty of solutions for the specific problem that's being discussed here proposed that are rejected for not being general solutions to the problem that you're describing. My point is that there's a decent number of people who aren't satisfied with this, and would prefer that this is something solved in the specific case rather than the general for the exact reason you pointed out: it doesn't seem like anyone is willing or able to solve it in the general case.
My point is that I think a lot of people want a solution to a specific problem, and you don't want that problem solved unless it solves the general problem that the problem is a specific case of. There's nothing wrong with that, but your objections are mostly phrased as claiming that they don't actually have the problem they have, and I think that's kind of odd. It's totally fair to hold the opinion that solving the specific problem would not be a good idea, but telling people that they don't care about what they care about is just needlessly provocative.
I like nice things. You've identified a clear pain point that I agree with. If something can be better, why not make it better? "I am not able to think beyond the end of my nose, therefore we have to stop there" is a silly response.
> Empirically, people don't seem to think they have better options, or else they'd be using them.
I have read many Go codebases that do a great job with errors, without all the frustration you speak of. I've also read codebases where the authors crated great messes and I know exactly what you're talking about. That isn't saying Go couldn't improve in any way, but it does say that design matters. If your design sucks, fix it. Don't design your code as if you are writing in some other language.
> My point is that there's a decent number of people who aren't satisfied with this
Including the Go team. Hence why Ian Lance Taylor (who isn't on the core team anymore, granted, but was at the time) went as far as to create a build of Go that exhibited the change he wanted to see. But, once it was tried, we learned it wasn't right.
Nobody has been able to find a design that actually works yet. Which is the same problem we had with generics. Everyone and their brother had half-assed proposals, but all of them fell down to actual use. So, again, who is going to be the person who is able to think about the bigger picture and get it right?
Philip Wadler may be that person. There is unlikely anyone else in the world with a more relevant background. But, if he has no interest in doing it, you can't exactly force him — can you? It is clearly not you, else you'd have done it already. It isn't me either. I am much too stupid for that kind of thing.
> I am not able to think beyond the end of my nose, therefore we have to stop there" is a silly response.
This is exactly what I mean by needlessly provocative. You're almost directly saying that people who happen to care more about one specific case than you do are stupid or naive rather than having a different technical opinion than you. If you genuinely think that people who disagree with you are stupid or naive, then I don't understand why you'd bother trying to engage with them. If you think they aren't, but their ideas are, I don't think you're going to be effective at trying to educate them by talking down to them like this.
> Nobody has been able to find a design that actually works yet. Which is the same problem we had with generics. Everyone and their brother had half-assed proposals, but all of them fell down to actual use. So, again, who is going to be the person who is able to think about the bigger picture and get it right?
Whether a design "actually works" is dependent on what the actual thing it's trying to solve is, since a design that works for one problem might not solve another. This is still circular; you're defining the problem to be larger than what the proposals were trying to solve, so of course they didn't solve what you're looking for. You're obviously happier with nothing changing if it doesn't solve the general problem, which is a perfectly valid opinion, but you're talking in absolute terms as if anyone who disagrees with you is objectively wrong rather than having a subjectively different view on what the right tradeoff is.
> Philip Wadler may be that person. There is unlikely anyone else in the world with a more relevant background. But, if he has no interest in doing it, you can't exactly force him — can you? It is clearly not you, else you'd have done it already. It isn't me either. I am much too stupid for that kind of thing.
Once again, this is exactly the reason that I'd argue that it's reasonable to consider a solution to a specific subset of the problem than trying to solve it generally. If nobody is capable of solving a large problem, some people will want to solve a small one instead. The issue isn't that I can't personally see beyond the end of my nose, but that unless someone comes up with the solution, it's impossible to tell the difference between whether it's a few hundred yards outside my field of view or light-years away in another galaxy we'll never reach. I'd argue that there should be some threshold where after enough time, it's worth it to stop holding out for a perfect solution and accept one that only solves an immediate obvious problem, and further that we've reached that threshold. You can disagree with that, but condescending to people who don't have the same view as you isn't going to convince anyone, so I don't understand what the point of it is other than if you're just trying to feel smugly superior.
> This is exactly what I mean by needlessly provocative.
This doesn't make sense. Comment on the internet cannot be provocative. If you have interpreted it as such, you need to read it again.
> so of course they didn't solve what you're looking for.
What I am looking for is irrelevant. They straight up didn't solve the needs of Go.
> Once again, this is exactly the reason that I'd argue that it's reasonable to consider a solution to a specific subset of the problem than trying to solve it generally.
> After a decade of writing Go I still don’t have a good rule of thumb for when I should wrap an error with more info or return it as-is.
When writing your tests:
1. Ensure all error cases are identifiable to the caller — i.e. using errors.Is/errors.AsType
2. Ensure that you are not leaking the errors from another package — you might change the underlying package later, so you don't want someone to come to depend on it
As long as those are satisfied, it doesn't matter how it is implemented.
Not usually they aren't. They can be made to be, but it requires extra effort and tradeoffs. Hence why there is a lot of work put into reproducible builds — something you would get for free if compilers were actually always deterministic.
Unless you are taking a wider view and recognizing that, fundamentally, nothing running on a computer can be nondeterministic, which is definitely true.
> Animal agriculture is around 15% of global emissions
The majority of which is methane, which only has a 7-12 year life. Which means — unless for some reason you started eating way more animals than you did yesterday — that your emissions today simply replace your emissions from 12 years ago. In other words, it is a stable system, unlike carbon, which basically sticks around forever.
> - Climate change is causing increasingly worse turbulence for airplanes
Cutting out air travel is the single most accessible and impactful thing an individual can do with respect to climate change. You can stop turbulence from getting worse, but since you won't be flying in the first place...
If you can press a button to understand what is going on, "it’s possible to overuse" most definitely applies. Dependency injection, as the name implies, is for dealing with dependencies — things that you cannot observe until runtime.
Hence the benefit to testing; allowing you to inject a deterministic implementation while under test.
"That does sound like something nice to have. However, recreating Google Sheets is a substantial undertaking. First, we need to evaluate the business case for duplicating something that already exists to ensure that there is a net benefit in doing so. Second, we need to determine if the business has sufficient capital to see the project through."
I suspect a lot of businesses are going to make this mistake in the "SaaS is dead" era as companies try to eliminate $50k/mo subscriptions for boring business software, and they figure it's easier to burn AI tokens creating an internal solution they didn't plan on maintaining in the far future.
My day job has me working on code that is split between two different programming languages. I'd say LLMs are pretty good at TDD in one of those languages and a hot mess in the other.
Which, funny enough, is a pretty good reflection of how I thought of the people writing in those languages before LLMs: One considers testing a complete afterthought and in the wild it is rare to find tests at all, and when they are present they often aren't good. Whereas the other brings testing as a first-class feature and most codebases I've seen generally contain fairly decent tests.
reply