They could've at least looked at what other languages were doing at the time, and that would have been much better than what they ended up with. However, as with many other aspects of Go, the creators ignored existing work and went with what seemed like improvements to problems from decades ago.
Other languages at the time were doing the exact same thing, except, maybe, wrapping the error in a monad such that you have to check the error before doing anything with value, but still with "if" or an if-like construct.
But that is unnecessary in Go as it believes that values should always be useful – which means you don't need to even consider the error to use the value.
That's how you get garbage (but valid) values fed as input to your code, eventually resulting in garbage output produced on the other end of the pipe. Always fun to debug.
If you face a function author who doesn't know what the hell they are doing and, as a result, foolishly feeds you garbage, then yes, it is possible for that garbage to propagate unknowingly. But when faced with a function author that doesn't know what the hell they are doing, that's going to be a problem no matter what. No language construct ever conceived can save you from someone who doesn't know what the hell they are doing. They will do foolish things in every language ever created. -- That is not a reasonable position.
If a function is written sensibly, the value can never be garbage. How could it be?
First things first, how can you read from a file that doesn't exist? Even you questioned that later, so this is a strange question. – Do you mean if you try to open a file that doesn't exist? nil would be a reasonable value. It is how Go signifies the absence of something. nil is always useful and with `if file == nil` will tell you that the file isn't there. No need to observe the error.
If you need to know why the file isn't there then, sure, you're also going to have to look at the error. Sometimes that is important. In the case of opening a file you realistically will need to know why it failed to open so that you can resolve the condition, but in other cases you don't need to worry about why it failed. Checking the error would be unnecessary in those cases, assuming the function author has some understanding of how to make even half-decent APIs.
Let's assume here, for the sake of discussion, that you have no need to know why the file was unable to opened. Again, how do you envision garbage propagating into the rest of your program?
But also, let's say the read function in question is named ReadInt32. Then what does it return on error?
Is this true? Are values always useful? You shouldn't even need to consider the error?
For example, if GPS errored while calling an Uber, is it useful to ignore the error and instead display the Uber driver as being in the middle of the ocean? https://www.reddit.com/r/uberdrivers/comments/zjpv69/well_ho...
There is a fairly wide-adopted and battle-proven pattern. It's called monads. Basically, you put a "context" over parts of your code and then some type of branching becomes implicit.
Good languages generalize it and allow their developers to create and use their own contexts. Other languages at least have special cases for very common cases. async/await is such an example.
If by fairly wide-adopted you mean it is adopted in a handful of language nobody uses, sure.
> Other languages at least have special cases for very common cases.
Where they usually screw it up. Look at the canonical example of a "monad" in Rust:
let result = divide(3.0, 2.0);
match result {
Just(x) => println!("Answer: ", x),
Nothing => println!("division failed; we'll get 'em next time."),
}
That is just "if" by another name!Please precisely define your criteria so that I can end this discussion by giving you a concrete counter example.
> That is just "if" by another name!
It appears you haven't understand the difference. Here's a counter example:
let first_result = divide(10, ???);
let second_result = divide(20, ???);
let final_result = first_result.or(second_result).unwrap_or(42);
println!("Result: {}", final_result);
Logic being, do two divisions and print the result of the first one, or if it failed, of the second one, or fall back to 42.We can go one with using lists of results, results of results and doing conditional calculations and so on and so on - and all of that without writing a single if.
Now do that in go without using if. Won't look as nice for sure.
You introduced the term. It is on you to define it. My stab in the dark to try and get us to an understanding may have failed, but that's where you would logically come in and explain yourself in better detail.
> so that I can end this discussion by giving you a concrete counter example.
A single counter example? I definitely would have never guessed that by wide-adoption you were thinking something singular. No wonder you've been so afraid to share your definition.
> Logic being, do two divisions and print the result of the first one, or if it failed, of the second one, or fall back to 42.
That too is just "if" with different syntax, and worse syntax – treading into Perl one-liner territory, but I concede that this particular example may not be a good basis on which to start from.
And my example is not "if". There is no if. Maybe there is "if" being used in the underlying functions? Again, eventually everything is machine code, but then every discussion about PLs is meaningless...
It doesn't literally include the keyword "if", but you've only rewritten an "if" statement in different syntax – and with worse syntax at that. It doesn't take things to a higher level.
But, again, I understand that this is probably not a good example on which to start from. We only ended up with it because it is a common snippet found elsewhere. How about you pick something that is a better demonstration?
> Again, eventually everything is machine code
Right, but we have higher level languages that enable expressing ideas beyond what the machine is directly capable of. The "if" statement itself is one such abstraction, albeit a fairly low-level one.
I don't understand what you mean. There is, to my knowledge, no different syntax for if-statements. In my code example there simply is no if being used.
Care to elaborate?
Can we agree that the ternary operator in C provides a way to write if-statements with different syntax?
> In my code example there simply is no if being used.
Assuming we do agree, there is no literal "if" being used when using the ternary operator. However, it doesn't take the concept to a higher level. It is still leans on the exact same conceptual if-statement expression, albeit written in a slightly different way.
The same is true of your code example.
I can neither agree or disagree because I don't know, but we are not talking about C and my code example didn't use a ternary operator either, so from my POV it doesn't matter.
> async/await was a better example. That moves something to a higher level of abstraction. However, it is not clear how to make that generalize
It is clear. Just not to you. Check Haskels do-notation or Scala for-comprehension or F#'s do!-notation and how they work. They all generalize over what async/await does (all with slightly different syntax though)
It matters because it is an attempt to explain what I am trying to convey. Technical people in particular tend to not share a common vernacular, so it is likely that I am using words in ways that do not match your understanding of them.
Communication is hard. I may have fallen short in my attempt, but surely we can try again? What do you hope to gain from this standoffishness?
> It is clear. Just not to you.
Fair enough, but that's the whole reason we're here, isn't it? To change that. If it were already clear, what purpose would the discussion serve?
So, let's go back to the opening comment with the age check problem. That is the explicit case where a higher level abstraction is being sought, and I think it serves as a good place to think about generalization without getting caught in the weeds.
I looked at Haskell's do notation and don't see how it helps. I prompted several LLMs in hopes that maybe they could resolve, but in every case they reversed back to using the exact same if-statements we started with. We're going to have to rely on your wisdom here.
> Fair enough, but that's the whole reason we're here, isn't it? To change that.
I'm afraid to tell you that I can't change that with my explanation. You will have to actually apply this in practice for a while. So long, that you have gotten used to it. And then compare it to how you work in golang. Because otherwise the code will just look unfamiliar to you and you'll potentially suffer from the blub paradox.
Of course this takes tons of time, so I can't expect you (or anyone) to do it. I can only tell how it looks from my POV since I've done it both ways.
I did. But you said it doesn't matter. Now it does matter?
> I'm afraid to tell you that I can't change that with my explanation.
No need for an explanation. Code will suffice. Perhaps I cannot write it myself, but I will eventually be able to understand it.
> Because otherwise the code will just look unfamiliar to you
That may be true in the first instant, but time marches forward and familiarity grows. This isn't an issue.
Is the real problem here that the code is so long and arduous that you don't have the time to write it? I don't think that satisfies the intent, if so. The idea is that the abstraction should be better than an if-statement, not the same or worse.
> Assuming we do agree, there is no literal "if" being used when using the ternary operator. However, it doesn't take the concept to a higher level.
So does it mean that you take back your original claim of "if in a different syntax" then and move the goalpost to "higher level"?
If so, I think "higher level" is too subjective and I'm tired of discussing this topic.
> No need for an explanation. Code will suffice. Perhaps I cannot write it myself, but I will eventually be able to understand it.
Then please do me a favor and explain the requirements again, because I'm not sure what exactly you have in mind for the age constraint(s).
> That may be true in the first instant, but time marches forward and familiarity grows. This isn't an issue.
I don't think so. You have to actually use it yourself, over time. At least that's true for everyone I know. Otherwise the abstraction will not and can not feel better to you, because you assess it in the wrong context.
Its like showing someone prolog code who is used to C. That just doesn't work. I can write you the code, but I doubt it will help you. You already called the other example of the divide function to not look great, so from there it only gets worse for you if that style is unfamiliar to you.
Take back what now? I said that your code example demonstrated an "if" statement written in different syntax; that it did not approach things from a higher level. The same would be true if you had written it with a ternary operator, if the language in question had such a feature. How does restating the same thing in a slightly different way lead to some kind of contradiction or whatever it is you are seeing here?
> If so, I think "higher level" is too subjective and I'm tired of discussing this topic.
I suspect once again you're getting too caught up in what words mean to you and not what they mean to me. As much as I'd like to use words as you know them, I haven't quite figured out how to drill into your brain to extract that information. Best I can do is use what I have and work with you to clarify intent as needed.
But we've already discussed that to death. There was no need to say it again – especially if it has tired you. Why did you bring it up again, exactly...?
> You have to actually use it yourself, over time.
Time I have. All we lack is your code.
> I can write you the code, but I doubt it will help you.
Humorously, you've put way more effort into telling me this than the supporting if-statement approach would have required. Does this one again suggest that what you envision is so long and arduous that you are avoiding it because of the effort required?
Otherwise you may as well try me. If it doesn't help we're still in the same place with much less energy expenditure on your part as compared to whatever this is.
> It's like showing someone prolog code who is used to C. That just doesn't work.
No, it works just fine. Prolog is perfectly understandable to read. It is harder to write without first understanding a bunch of technical nuance, but we don't need the uninitiated to write code here.
Why not admit that you can't write the code in question? This act isn't fooling anyone.
Sorry, it was late and you indeed mentioned that it wasn't making it "high level". My bad. Unfortunately, "high level" is quite subjective and I'm not willing to put the time to discuss if something falls under this definition or not.
> Why not admit that you can't write the code in question? This act isn't fooling anyone.
I asked you to clarify what exactly you want to see (which you didn't quote, even though you quoted basically everything else in my reply). In this particular thread here, there was no mention of age, that was in one of the other threads. So before I write code for something that you don't want to you
Again, please explain the requirements for the code again. What exactly do you want to see written in this style?
Uh... https://news.ycombinator.com/item?id=43487167
With respect, are you okay? Between this and your other comment [https://news.ycombinator.com/item?id=43502693] that was completely off the rails, I'm worried for you. You seem to have lost your sense of what is going on.
If it is simply that you are posting too much and can't keep things straight, making up stories like that you could code something but won't because I wouldn't be able to understand your brilliance to cover your tracks, maybe it's time to stop using the internet for a while?
I ask because it's very much not "canonical," Just and Nothing are Haskell terms, not Rust terms. You'd expect at least Some and None.
Regardless, I do agree that in this specific circumstance, you're emulating an if. There's even a form in this case that emulates it even more closely:
if Some(x) = divide(3.0, 2.0) {
println!("Answer: ", x);
} else {
println!("division failed; we'll get 'em next time.");
}
But these simple cases don't show off where Option/Result truly shine.Sent one in: https://en.wikipedia.org/w/index.php?title=Monad_(functional... we'll see if it gets reverted. Honestly, I find this whole section kind of awkward. I fully agree with you that it doesn't really show off why this stuff works, and works well. Real-world code doesn't get written like this.
First, to understand how to handle errors differently, you have to understand how errors are different.
Like, is a person's age an error? Your gut reaction is almost certainly "What? No. A person's age isn't an error." Yet soon enough you're writing age verification checks like: if age < 18 { /* not an adult */ }. if age < 21 { /* not old enough to drink in the USA */ } – with all the exact same problems if err != nil {} has. Clearly it is an error in certain contexts.
Keep going and you start to wonder what branching situation isn't error handling. So, really, it seems to me what we really want is a better way to express branching operations. "if" is one of the earliest additions to programming languages, so it stands to reason that it is getting a little long in the tooth.
The effort to improve error handling is clearly there. Core team member Ian Lance Taylor submitted a new proposal and built a reference implementation just within the last few months. There have been ~200 error handling proposals! It is a super hard problem, though. A "tiny bit more" thinking is not sufficient.