-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Empty value vs Option/Result vs panic #2512
Comments
I personally think that depending on what you are asking to be retrieved you should either return an optional or an empty value or panic.
IMO, it's just a matter of logic behind what you should return, but I'm not sure if it'd be very consistent. |
Yes, this is important and interesting question. It defines part of philosophy of the language, it's "character". We want V to be "safe language". But what does that really mean. We should define safety first and agree on the level of the safety we want, because everything has it's own cost. For the mentioned cases, the most "safe" way, probably, would be to return Option in all cases and enforce developers to handle this. But then all the code becomes like files := os.ls(path) or { panic(err) } and this is not very nice. But you would be sure that you are handling all the possible undesirable outcomes and your program would not panic at run time if you handle situations in some better way. A bit "less safe" mode would be to panic in every undesirable/wrong situation. This is still much better than silently producing undefined result. With panic, your program can fail at run-time, but if What if we make all operations or functions that can fail on something returning Options, but we do not enforce developer explicitly handle this in the code, if he/she is fine the program to panic at run-time in these situations. If you are not fine with that, then please handle this explicitly in you code in a way that suits you. Examples: // panics if `index` is out of bounds
x := numbers[index]
// deal with that if you want
x := numbers[index] or default_value
// panic if no directory or no access
files := os.ls('bad_path')
// deal with these cases
files := os.ls('bad_path') or {
match err {
.not_exists { println('directory not found') }
.no_access { println('cannot access directory') }
}
}
// panic if map key does not exist
element := map[bad_key]
// deal with that if panic is not what you want
element := map[bad_key] or default_value
// on assigment, replace if exists, create new if does not exist
map[bad_key] = element
// created if did not exist
// default if not found
idx := str.index('bad string') or -1 Am I missing some important things here that would not work well for some reason? |
Some other languages go with the variants of x := numbers[large_index]! to force unwrap optionals when you are sure that it has a value, but I personally am not a fan of this. |
I'm against Rust's Typing |
This goes against the one way philosophy. Go has a nice policy that no library should panic. I agree with that. I think keeping the optional and adding the |
I think just adding the syntax for ? at the end (which is syntax sugar for |
It's actually going to be error propagation for all functions except fn main. In fn main (and V scripts) it will lead to a panic. |
About the maps returning optionals, that is also ok imo. |
Yeah it's more of a Python/Perl programming style. Using nested maps for all objects. |
What will happen if the containing function does not return an optional ? |
panic(err) ? |
Then using |
hm, but scripts (and main programs) do not return an option too... |
that will lead to an inability to use ? exactly where it will be most useful, where brevity is important |
|
oh, so it will be special cased for main .. ok |
Sorry if I miss some important point here, but how the syntactic sugar of
is different from the "syntactic sugar" of os.ls() // panic if optional is unhandled For me it looks that the only difference is that in the first place you say "yes, I know it returns optional and panic (or error propagation) is ok for me", and in the second you say "I don't care to handle optionals, please panic (or propagate error) if my optional does not have value somewhere". Why V has to ask me to put |
I really like the fn main() {
f := find_files or {
eprintln('Something went wrong when finding the files.')
// :)
}
}
fn find_files() []string? {
...
ls := os.ls()?
...
} |
With my proposal |
if “or {panic ()} everywhere” ,Looks and writes like a headache. |
Well, what if you do care to handle optionals but forgot that |
|
@shsr04 Valid question! That could be a reason to use |
For maps I'm thinking it should panic... you should always check the key exists before access, like array: |
Or just have UB behavior for maps like arrays :P But I definitely think that bad index and bad key should only panic and there shouldn't be optional for it. |
Idea:
remains short for
but
becomes short for
If users don't want
to panic, they could do
Or maybe the potential panic is made more explicit by disallowing
altogether and forcing
Making the unsafe thing ugly like this might be a good idea, given that users should do something more like
instead. I see #2512 (comment) but I thought I'd try anyway :-), given that Furthermore, making |
I think the Golden Rule of Safety in V should be: no invisible I think everything @avitkauskas said in #2512 (comment) is pretty brilliant, especially
and
but with one exception: let's remove the invisible panics by disallowing
and instead make people do
if they're willing to allow the |
You cant have invisivble panic, its no longer a panic then |
I think @elimisteve meant that in my initial proposal there was nothing in the code that would suggest a statement could panic. But with |
I see, what I mean is I dont think there should be any way to disable a panic |
@avitkauskas Exactly. I should have said: potential panics should be explicit in the code. No land mines allowed! Possible exception: division by zero. Even there I suppose forcing the user to check that the denominator != 0 could be enforced. A generalized version of this idea is another way to approach all this that I wonder if Alex may like if it's feasible enough to implement: forcing users to ensure that, for example, a key exists in a map before it is used, where |
So indexing an array should panic. In generic programming, operations like indexing should behave the same whatever the container. For consistency, indexing a map should also panic. This makes generic code work the same whatever container is passed to it.
Exactly, we need to be able to assign to an indexing expression.
But how do we assign to an unwrapped optional value and have that update the map value? The optional would have to contain a reference to the mutable value. If doing this, it shouldn't be magic for maps, it must work for container methods that return an optional too.
If
I don't think we need that exceptional case - make the user write |
But V is trying to be more safe, not merely as safe, as other languages; let us please not commit the same design errors of the past! Making V Safe(r)I have programmed in Go for 9 years now, and even though Go code is extremely safe compared to any scripting language or memory-unsafe language like C/C++, there are still some issues, namely with:
V doesn't a brilliant job of solving #1, #2, and #3, and partially solves #4. One problem that almost all languages have that Go doesn't is exploding maps; that is, maps that throw exceptions when you try to look up a value with a non-existent key. If maps throw exceptions in V, I think this would be a clear step backward. When I write Python, I've trained myself to always do Years ago I wrote a 4500-line API in Go that never crashed in production. On the test server, it did crash once, and I remember exactly why: I was accessing a nested map of stock quotes by symbol:
I only checked to see if the outer map was Bad:
Good:
I believe that, in the case of V, it is very important to design this problem out of existence. This could be done in several ways, but if we don't even agree that it is important to protect V programs against this source of instability, then we will not try hard enough to solve this problem. Part of the brilliance of Go, to paraphrase Rob Pike, is its built-in realization of the idea that errors are not exceptional. Trying to read a file that doesn't exist? Return an error rather than blowing up/throwing an exception/panicking, because that's not that weird, and destabilizing our programs doesn't help to solve it. Purposely Destructive Considered HarmfulI once had a colleague tell me that it is a good idea for an unexpected error in our production servers to crash production for all of our users, because that would "get our attention" more than internally throwing an error that could loudly notify us without taking down production. His suggestion is completely insane; allowing an edge case that affects 0.01% of requests to destroy 100% of the value obtained by users is a profoundly irrational trade-off. I think that the "exceptions/panics are actually good" school of thought has a lot of takers, and that this is A Bad Thing that V need not perpetuate. A Smarter, Safer Path
I think these should be the 3 valid ways to access a map. All of them are either safe or clearly unsafe, none of them are unclearly unsafe, which is what must be avoided.
If this innocuous-looking line can panic and thus unexpectedly kill your program, I think this would be a serious mistake that severely compromises the supposed safety guarantees of V. |
I'm thinking that my proposal to have
be syntactic sugar for
may result in people constantly asking, "when should I use Or, maybe we have
become shorthand for
so that it feels more different from and (correctly) looks more dangerous than @medvednikov Maybe this is crazy, but I think that literally making nested maps invalid in V is a totally fine thing to do; make people use structs, so that
would have to be
or
|
@medvednikov @ntrel Good points regarding ^^that^^. |
Edit: Empty value panic Option/Result |
I just realised we need to use optional or some default value, to avoid an extra map lookup we would need to do if it panics |
@elimisteve I actually suggested that if map indexing can panic, V should require a My point was that indexing should behave the same regardless of container. If map indexing should return an optional, then so should array indexing, so that generic code doesn't have to handle arrays specially with compile time introspection. |
@netmute this discussion has been going on since the very beginning. I think it's clear now that option/result should be used, and That gives us safety and a clean readable way to do things like |
@ntrel Awesome; I missed that you had suggested that and really like your reasoning 👍 🙂 . |
My 2 cents are; go with options/results like Rust did. |
Indexing a HashMap in rust actually panics: |
With an array of integers, if indexing gives an Option then this has to be less efficient than C, because it needs (at least) another byte for the |
I'm kind of confused - are you @avitkauskas and @medvednikov proposing removal of both With the linked approach there is no syntax/semantics conflict and you can "catch" and react to the error at any depth (this is the very initial motivation behind the concept of exceptions). All use cases should be covered including l-values handling:
Let's leave the expressiveness up to the programmer as it's highly situational. Don't try to make rules which become obstacles by definition.
In case of the above solution it's easy - the code will not compile unless you either catch it with To distinguish errors coming from different function calls There might be a compiler argument to suppress this verbosity for certain functions or modules or any other logical unit or the whole app if the time will prove, that people like to write code with too many optionals. This approach also leaves a lot of room for potential future syntax extensions if the compiler argument for suppression won't be enough (which I hope will be enough if safety in V is really that important). E.g. implementing the proposal of others to additionally require |
My opinion goes for having |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Hi,
This is a very important topic.
All of these can result in errors, and there are 3 ways to handle them: return empty values, return a Result type with an error, or panic.
It's pretty standard across all languages to panic in out of bounds array access. The rest of these are not as clear.
Go returns zero values or -1, Rust uses
Result
, C++ panics. Each approach has its pros and cons.Recently
os.ls() []string
was changed toos.ls ?[]string
, and I'm not sure it was the right choice. Do we really need to handle this error? Does it matter whether there are 0 files in the directory, or the directory doesn't exist? In most cases people do something likewhich works the same without an error check.
Making maps return optionals makes code like
config[foo][bar] = true
impossible.What do you think?
The text was updated successfully, but these errors were encountered: