News for hackers. Stuff that matters.
Re:Union types ('enum types') would be complicated in Go by foldr on Tuesday December 03, 2024 @08:51AM (#42305999)
I think this is mistaken. Go already has a way to represent 'open' union types (interfaces), so all of these runtime problems have already been solved. What's missing is just the type system support to do exhaustive matching on the members of the union. With the addition of generics, 'all' that would be necessary is to make the following a legal variable definition:

    var foo interface {
        struct { A int } | struct { B string }
    }
It currently fails with the following error:

"cannot use type interface{struct{A int} | struct{B string}} outside a type constraint: interface contains type constraints"

Re:Union types ('enum types') would be complicated in Go by siebenmann on Wednesday December 04, 2024 @12:39PM (#42319851)
Interfaces aren't bit-packed and they force storing all values as a separate allocation that the interface contains a pointer to (escape analysis may allow this separate value to be on the stack, along with the interface itself). I believe that Go used to have an optimization where values that fit in a pointer were stored directly in the interface value, but abandoned it, perhaps partly because of the GC 'is it a pointer or not' issue. In my view, some of what people want union types for is exactly efficient bit-packing that uses little or no additional storage, and they'd be unhappy with a 'union values are just interface values' implementation.

(I'm the author of the linked to article.)

Re:Union types ('enum types') would be complicated in Go by foldr on Wednesday December 04, 2024 @03:51PM (#42321918)
A separate allocation is not forced. The implementation could allocate a block of memory large enough to hold the two pointers for the interface value together with the largest of the types that implements the interface. (You can't do that with an open interface because there's no upper bound, but the idea here is to let you define closed interfaces.)

In cases where there is a lot of variance in the size of the different interface implementations, separate allocations could actually be more memory efficient than a tagged union. In any case, I'm not sure that memory efficiency is the main reason that people miss Rust-style enums in Go.

Re:Union types ('enum types') would be complicated in Go by siebenmann on Wednesday December 04, 2024 @04:07PM (#42322060)
The problem with allocating bit-packed storage is that then you are into the issue where types don't agree on where any pointers are. Interface values solve this today because they are always mono-typed (an interface value always stores two pointers), so the runtime is never forced to know the current pointer-containing shape of a specific interface value. And the values that interface values 'contain' are also always a fixed type, so they can be allocated and maintained with existing GC mechanisms (including special allocation pools for objects without pointers and etc etc).

I agree with you about the overall motivation for Rust-style enums. I just think it's surprisingly complex to get even the memory efficiency advantages, never mind anything more ambitious.

Re:Union types ('enum types') would be complicated in Go by foldr on Thursday December 05, 2024 @04:21AM (#42326442)
I was saying that you can have a single allocation without using bit packing.

I'm not sure what you're referring to with 'anything more ambitious'.

Re:Union types ('enum types') would be complicated in Go by treyd on Friday December 06, 2024 @10:25PM (#42346996)
> The problem with allocating bit-packed storage is that then you are into the issue where types don't agree on where any pointers are.

The solution to this should be trivial. You just have to extend the gcshape concept to account for the enum discriminator.

Re:Union types ('enum types') would be complicated in Go by ryanpetrich on Friday December 06, 2024 @10:57PM (#42347144)
The bigger problem is mutability. Any pointers into the bit-packed enum storage become invalid as soon as you change its type. To solve this you can either prohibit pointers into bit-packed enum storage, which is very limiting, or introduce immutability into the language. Immutability is particularly difficult to add to go, where default zero-values emerge in unexpected places (such as the spare capacity of slices and the default state of named return values)
Re:Union types ('enum types') would be complicated in Go by jerf on Friday December 06, 2024 @10:36PM (#42347043)
The problem with that is that in Go, I need to be able to put methods on those things, for reasons possibly unrelated to the interface in question. For that they need to be named. For that you might as well do what has worked since Go 1.0 and just put an unexported method in the interface and declare several instances of that interface in your package.

Honestly interfaces with unexported methods are 90%+ of what people want. It's just not spelled the way they expect. And if you're not going to be happy except at absolutely 100%, a position I can and do respect, there's no point waiting for Go to get any better because I can guarantee you no Go proposal for sum types will fix that you will be forced to have a "nil" value in the sum type, so there's no point in waiting.

Re:Union types ('enum types') would be complicated in Go by wbl on Saturday December 07, 2024 @12:36AM (#42347531)
The zero would obviously be the zero value of one of the constituents. Coproducts in most of Go are easy: it's interfaces that make it hard.
Re:Union types ('enum types') would be complicated in Go by masklinn on Saturday December 07, 2024 @02:48AM (#42348024)
> The zero would obviously be the zero value of one of the constituents.

If interfaces are used for union types then the obvious zero is nil, not any constituent. Nil is the zero value of interfaces.

It’s perfectly consistent and in line with the rest of the langage.

Re:Union types ('enum types') would be complicated in Go by wbl on Monday December 09, 2024 @03:53PM (#42370452)
It's not clear to me that that would satisfy the categorical properties of coproduct that are wanted.
Re:Union types ('enum types') would be complicated in Go by foldr on Tuesday December 10, 2024 @04:19AM (#42375189)
Nobody cares about the categorical properties of the coproduct. But in any case, there's no theoretical issue here, since it would be just as if every enum had a 'Nil' variant implicitly defined.
Re:Union types ('enum types') would be complicated in Go by foldr on Monday December 09, 2024 @11:16AM (#42367559)
You would want to layer some sugar on top to enable naming of the variants. I was just trying to keep my example as close as possible to valid Go code.

Even if you have to manually name the structs, you'd still have the benefit of exhaustivity checking in type switches, which you can't get with the (perfectly reasonable) approach that you mention.

Re:Union types ('enum types') would be complicated in Go by eru on Saturday December 07, 2024 @04:13AM (#42348390)
You also want to support having the same type on both sides:

    var foo interface {
        struct { A string } | struct { B string }
    }
Eg in Rust that would be 'Result<String, String>', where your success happens to be a String and your errors happens to be a String error message.