3 ms·
Sorry, but I don't follow. Practically, in the Go design above, PrimeNumber is going to be uninitialized only if the current value of Result is PrimeError. Whic
by kgeist 2y ago
Sorry, but I don't follow. Practically, in the Go design above, PrimeNumber is going to be uninitialized only if the current value of Result is PrimeError. Which can be checked with type matching: which value is active. Attempting to dereference an uninitialized option would panic, we just shifted it from compile-time to runtime (which is typical of Go). I don't understand how things like "NonZeroU64 is guaranteed to be >=1" are related to the notion of sum types per se. Sure Go has a weaker type system than Rust and you can't guarantee that a struct will never be zero-initialized, but you can enforce it with conventions, like wrapping values with structs, using constructor functions, etc.
Example (with proposed syntax):
type Username struct {
value string
}
func NewUsername(value string) option[Username, UsernameError] {
if value == "" {
return make(option[Username, UsernameError], NewUsernameError(..))
}
return make(option[Username, UsernameError], Username{value: value})
}
u := NewUsername("")
switch u.(type) {
case Username:
// ...
case UsernameError:
// ..
}
Verbose and non-idiomatic but seems to work. Or I don't understand something.
- yencabulator 2y agoThe idea of types that can only contain "approved" values is the point of sum types for error handling. You either get an error, or a valid value, and there is no way to manufacture a zero value of Username out of thin air. If you don't have that, you're not really improving on `value, ok := foo()`.
- kgeist 2y ago>there is no way to manufacture a zero value of Username out of thin air As I said, with the proposal above, you cannot manufacture a default zero value from a union if you specify in the language's spec that trying to retrieve a value from a non-initialized ("not active") subtype panics (i.e. the union was constructed with a different underlying type) asUsername := myUnion.(UserName) // panics if myUnion doesn't hold UserName internally // if you want to retrieve the value, you type-switch: switch myUnion.(type) { case UserName: // if the underlying type is UserName, it always returns the value as it was passed into the union's constructor by the developer, it doesn't matter if it was "initialized" or the developer passed some default zero'd value; it's not the union's concern at all } It's similar to panicking on nil interfaces, nil maps etc. The article is called "Good union types in Go would probably need types without a zero value", so I responded to that -- you don't need types without a zero value for the use case of union types. It's also different from interfaces (as you noted earlier), because this allows disjoint types. Whether Go must allow non-zero types in general is a totally different topic.