5 ms·
Not as simple as it seems. For instance, what is the zero value of a sum type? Let's say, the zero value of a Result<int, error>?
by sacado2 5y ago
Not as simple as it seems. For instance, what is the zero value of a sum type? Let's say, the zero value of a Result<int, error>?
- kortex 5y agoPerfect example of a caveat to "without sacrifice". For context, go really cares about having "zero values" - every initialized variable of some type must have a zero value. The zero of Error is nil, so logically this would be Result(0, nil). But what do you set the internal state? It's not Ok, cause that int isn't used, and it's not Err either. Option would work fine though and it would be amazing to map over options instead of nil checks. I think you'd need to build Result on top of Option, it's naturally kind of ternary - "I have value" "I have error" "idk", either internally or externally. What might be awesome would be if the Error were a slice type, nil would be "uninitialized" and empty slice means "initialized". still not...pleasant.
- DylanSp 5y agoZero values are definitely an issue, and it'd be hard to get rid of them while sticking to Go's imperative/statement-based style. Still, it'd nice to use an Option type instead of pointers.
- Lev1a 5y agoIIRC in Rust it has to be either of the Ok or Err variants and the enclosed type can then provide its own "zero value" via the standard library "Default" [0] trait or your own implementation for the particular type. That trait is also implemented not for "Result" but for "Option" giving it a default value of "None". [0]: https://doc.rust-lang.org/std/default/trait.Default.html https://doc.rust-lang.org/std/default/trait.Default.html
- sacado2 5y agoYes, for Option the default value is quite obvious. I'm talking about Result, because it's the second best-known sum type, and it doesn't have an obvious default value.
- deleted 5y ago[deleted]
- Lev1a 5y agoBindings in Rust have to initialized with an explicit value if the first use comes before the first modification/assignment otherwise [0] you get an error message with error E0381 [1]. Even if that explicit assignment is just through the use of the type's default method [2], if available. Since it's implemented for Option<T> you can do it the same way [3] for that, but that's not available for Result<T,U> since the semantics are different from Option which is basically just Rust's alternative for 'null'/'nil' (but verifiable by the compiler). Thus you'd have to choose what variant of the enum you want to have at initialization, after which you can use the Default implementation of the enclosed type, if available (and so on, if there are more nested types). Though honestly I can't recall using the kind of pattern in Rust that would "need" something like that. Through the handling of code blocks as expressions, early returns etc. you for example don't need to initialize a variable before having an if-/match-/for-/while-/etc. block, you can simply use that block as an expression on the right side of the assignment [4]. [0]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=50e8cbc37459e52eadad4384261a479e https://play.rust-lang.org/?version=stable&mode=debug&editio... [1]: https://doc.rust-lang.org/stable/error-index.html#E0381 https://doc.rust-lang.org/stable/error-index.html#E0381 [2]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=938c7a8674d72f87f0817eaa31c88f93 https://play.rust-lang.org/?version=stable&mode=debug&editio... [3]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=062ec5c467bba12fc76fa08a404a7adc https://play.rust-lang.org/?version=stable&mode=debug&editio... [4]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=fab6733af1a9cd98dfd8afff1ea5c7df https://play.rust-lang.org/?version=stable&mode=debug&editio...
- foldr 5y agoIn Go, every type has a sensible built-in zero value. In Rust there is no need to worry about what the 'default' value of a Result type should be. So choosing a sensible default value for result would be a problem in Go (but not in Rust).
- lucian1900 5y agoThe zero value of a sum type can be the zero value of the first (or chosen) constituent. In this case, it could be Result<0>.
- sacado2 5y agoI'm not sure that would be any better, in practice, than doing return 0, nil.
- lucian1900 5y agoIt would allow functions to be treated generically as returning error-able types. Since some functions even return 3 arguments where the third is the error, this isn't possible today. It would also allow defer-like syntax to be used on the value a function returns, to return early from the calling function. Rust's ? is an excellent example of this, allowing explicit delegation of error handling that is still very lightweight syntactically.
- kortex 5y agoFunction composition becomes a lot easier with Result/Option, because you can write functions in terms of assuming a "good" value (not nil/err or otherwise "bad") and then map over it. Or if we really wanna be fancy, flatmap/bind over it. Foo, err requires either that all receiving functions accept (foo, err), or manually nil-checking at every stage. Try/catch is the least composable, because deeper error types can "jump out" from any layer. Which means you have to keep wrapping catches, until you hit some base layer you can recover from. You get stack traces, but you lose type information along the way. Sum (result) and product (foo, nil) types preserve type information. Meaning you can operate more generically over it. Meaning less code duplication.