4 ms·
The problem is that you can’t really make this work within the runtime in the way you think it would work. The runtime relies on being able to figure out where
by klodolph 2y ago
The problem is that you can’t really make this work within the runtime in the way you think it would work. The runtime relies on being able to figure out where pointers and non-pointers are. When you assign to a union, you’re not just changing the value, you’re (potentially) changing which parts of it are pointers. If you just let this be, you have to redesign the runtime (significant changes). Alternatively, you could make the union store everything like it were a struct containing all variants… but that would be inefficient. These are not good options.
- noelwelsh 2y agoWhy do you think this is a problem? It's solved in other languages that have much more expressive type systems than Go and comparable or better performance. Rust is an obvious example. The standard implementation is pretty simple: the different variants of a sum have a tag that tells the runtime which variant they are, and hence where to find pointers. Go already has a "type switch" for interfaces, so it must already store tags with values in some cases, which is exactly what is needed to make this work.
- aatd86 2y agoYou should be able to commute terms of an union without changing its identity. So this is not the right solution I'm afraid.
- noelwelsh 2y agoI must admit I don't understand what you are trying to say here. Perhaps there is some confusion around terminology. What OP calls union types are known in type theory as sum types, and are actually different from what type theory calls union types. Type theory union types are like set unions.
- aatd86 2y agoYou can't have the zero value of a union be the zero value of the first written term or (a | b) becomes different from (b | a).
- noelwelsh 2y agoCommutativity doesn't apply to sum types, though it does apply to union types. Here I'm using the type theory terms, not the terms used by OP. So this isn't really an issue. Zero values don't make any sense anyway. As the original comment said "You might think this is a pretty dumb design decision that can lead to programmer error, but it meshes perfectly with the other mistakes in the language."
- klodolph 2y ago> The standard implementation is pretty simple: the different variants of a sum have a tag that tells the runtime which variant they are, and hence where to find pointers. That won’t work in Go, because it would not be safe. You would need to coordinate the change with the concurrent garbage collector. It’s not designed to do this. Redesigning it to do this would be non-trivial. Go already has a lesser-known safety problem similar to this involving interface types, but that safety problem is at least avoidable if you write code without data races in it. With the union safety problem, the data race is harder to avoid.
- foldr 2y agoAs parent mentions, Go already does exactly this for interface values, so the problem has already been solved. This is at most a problem if you want to avoid boxing individual union variants. Otherwise, the runtime representation of a union can just be an interface value (with some extra type system stuff to enforce exhaustive matching, etc. etc.) Previous discussion here: https://news.ycombinator.com/item?id=42305999 https://news.ycombinator.com/item?id=42305999
- yencabulator 2y agoInterface type implementation was explicitly changed to not trigger this problem. Now small values cannot be stored inline in the interface, where they can be confused for pointers. All things stored in interfaces have to be allocated, and the GC associates "what parts are pointers" with the memory location, not with the interface type tag.
- foldr 2y agoYes, that's true. But that means the problem is solved, no? I think my previous comment was badly worded. I just meant to say that Go has a concurrent GC and a runtime representation of a value that can instantiate one of a number of different types (i.e. interface values). So as long as one is willing to accept that as an implementation for an enum/union type, all of the major runtime problems are already solved.