Skip to content
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

proposal: spec: add sum types / discriminated unions #19412

Open
DemiMarie opened this issue Mar 5, 2017 · 333 comments
Open

proposal: spec: add sum types / discriminated unions #19412

DemiMarie opened this issue Mar 5, 2017 · 333 comments

Comments

@DemiMarie
Copy link

@DemiMarie DemiMarie commented Mar 5, 2017

This is a proposal for sum types, also known as discriminated unions. Sum types in Go should essentially act like interfaces, except that:

  • they are value types, like structs
  • the types contained in them are fixed at compile-time

Sum types can be matched with a switch statement. The compiler checks that all variants are matched. Inside the arms of the switch statement, the value can be used as if it is of the variant that was matched.

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Mar 6, 2017

This has been discussed several times in the past, starting from before the open source release. The past consensus has been that sum types do not add very much to interface types. Once you sort it all out, what you get in the end is an interface type where the compiler checks that you've filled in all the cases of a type switch. That's a fairly small benefit for a new language change.

If you want to push this proposal along further, you will need to write a more complete proposal doc, including: What is the syntax? Precisely how do they work? (You say they are "value types", but interface types are also value types). What are the trade-offs?

Loading

@bradfitz bradfitz added this to the Proposal milestone Mar 6, 2017
@rsc rsc changed the title Proposal: Discriminated unions proposal: spec: add sum types / discriminated unions Mar 6, 2017
@rsc
Copy link
Contributor

@rsc rsc commented Mar 6, 2017

Loading

@griesemer
Copy link
Contributor

@griesemer griesemer commented Mar 6, 2017

I think this is too significant a change of the type system for Go1 and there's no pressing need.
I suggest we revisit this in the larger context of Go 2.

Loading

@rsc rsc added the Go2 label Mar 13, 2017
@rogpeppe
Copy link
Contributor

@rogpeppe rogpeppe commented Mar 22, 2017

Thanks for creating this proposal. I've been toying with this idea for a year or so now.
The following is as far as I've got with a concrete proposal. I think
"choice type" might actually be a better name than "sum type", but YMMV.

Sum types in Go

A sum type is represented by two or more types combined with the "|"
operator.

type: type1 | type2 ...

Values of the resulting type can only hold one of the specified types. The
type is treated as an interface type - its dynamic type is that of the
value that's assigned to it.

As a special case, "nil" can be used to indicate whether the value can
become nil.

For example:

type maybeInt nil | int

The method set of the sum type holds the intersection of the method set
of all its component types, excluding any methods that have the same
name but different signatures.

Like any other interface type, sum type may be the subject of a dynamic
type conversion. In type switches, the first arm of the switch that
matches the stored type will be chosen.

The zero value of a sum type is the zero value of the first type in
the sum.

When assigning a value to a sum type, if the value can fit into more
than one of the possible types, then the first is chosen.

For example:

var x int|float64 = 13

would result in a value with dynamic type int, but

var x int|float64 = 3.13

would result in a value with dynamic type float64.

Implementation

A naive implementation could implement sum types exactly as interface
values. A more sophisticated approach could use a representation
appropriate to the set of possible values.

For example a sum type consisting only of concrete types without pointers
could be implemented with a non-pointer type, using an extra value to
remember the actual type.

For sum-of-struct-types, it might even be possible to use spare padding
bytes common to the structs for that purpose.

Loading

@bcmills
Copy link
Member

@bcmills bcmills commented Mar 22, 2017

@rogpeppe How would that interact with type assertions and type switches? Presumably it would be a compile-time error to have a case on a type (or assertion to a type) that is not a member of the sum. Would it also be an error to have a nonexhaustive switch on such a type?

Loading

@josharian
Copy link
Contributor

@josharian josharian commented Mar 22, 2017

For type switches, if you have

type T int | interface{}

and you do:

switch t := t.(type) {
  case int:
    // ...

and t contains an interface{} containing an int, does it match the first case? What if the first case is case interface{}?

Or can sum types contain only concrete types?

What about type T interface{} | nil? If you write

var t T = nil

what is t's type? Or is that construction forbidden? A similar question arises for type T []int | nil, so it's not just about interfaces.

Loading

@rogpeppe
Copy link
Contributor

@rogpeppe rogpeppe commented Mar 22, 2017

Yes, I think it would be reasonable to have a compile-time error
to have a case that can't be matched. Not sure about whether it's
a good idea to allow non-exhaustive switches on such a type - we
don't require exhaustiveness anywhere else. One thing that might
be good though: if the switch is exhaustive, we could not require a default
to make it a terminating statement.

That means that you can get the compiler to error if you have:

func addOne(x int|float64) int|float64 {
    switch x := x.(type) {
    case int:
        return x + 1
    case float64:
         return x + 1
    }
}

and you change the sum type to add an extra case.

Loading

@rogpeppe
Copy link
Contributor

@rogpeppe rogpeppe commented Mar 22, 2017

For type switches, if you have

type T int | interface{}

and you do:

switch t := t.(type) {
case int:
// ...
and t contains an interface{} containing an int, does it match the first case? What if the first case is case interface{}?

t can't contain an interface{} containing an int. t is an interface
type just like any other interface type, except that it can only
contain the enumerated set of types that it consists of.
Just like an interface{} can't contain an interface{} containing an int.

Sum types can match interface types, but they still just get a concrete
type for the dynamic value. For example, it would be fine to have:

type R io.Reader | io.ReadCloser

What about type T interface{} | nil? If you write

var t T = nil

what is t's type? Or is that construction forbidden? A similar question arises for type T []int | nil, so it's not just about interfaces.

According to the proposal above, you get the first item
in the sum that the value can be assigned to, so
you'd get the nil interface.

In fact interface{} | nil is technically redundant, because any interface{}
can be nil.

For []int | nil, a nil []int is not the same as a nil interface, so the
concrete value of ([]int|nil)(nil) would be []int(nil) not untyped nil.

Loading

@bcmills
Copy link
Member

@bcmills bcmills commented Mar 22, 2017

The []int | nil case is interesting. I would expect the nil in the type declaration to always mean "the nil interface value", in which case

type T []int | nil
var x T = nil

would imply that x is the nil interface, not the nil []int.

That value would be distinct from the nil []int encoded in the same type:

var y T = []int(nil)  // y != x

Loading

@jimmyfrasche
Copy link
Member

@jimmyfrasche jimmyfrasche commented Mar 22, 2017

Wouldn't nil always be required even if the sum is all value types? Otherwise what would var x int64 | float64 be? My first thought, extrapolating from the other rules, would be the zero value of the first type, but then what about var x interface{} | int? It would, as @bcmills points out, have to be a distinct sum nil.

It seems overly subtle.

Exhaustive type switches would be nice. You could always add an empty default: when it's not the desired behavior.

Loading

@rogpeppe
Copy link
Contributor

@rogpeppe rogpeppe commented Mar 22, 2017

The proposal says "When assigning a value to a sum type, if the value can fit into more
than one of the possible types, then the first is chosen."

So, with:

type T []int | nil
var x T = nil

x would have concrete type []int because nil is assignable to []int and []int is the first element of the type. It would be equal to any other []int (nil) value.

Wouldn't nil always be required even if the sum is all value types? Otherwise what would var x int64 | float64 be?

The proposal says "The zero value of a sum type is the zero value of the first type in
the sum.", so the answer is int64(0).

My first thought, extrapolating from the other rules, would be the zero value of the first type, but then what about var x interface{} | int? It would, as @bcmills points out, have to be a distinct sum nil

No, it would just be the usual interface nil value in that case. That type (interface{} | nil) is redundant. Perhaps it might be a good idea to make it a compiler to specify sum types where one element is a superset of another, as I can't currently see any point in defining such a type.

Loading

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Mar 22, 2017

The zero value of a sum type is the zero value of the first type in the sum.

That is an interesting suggestion, but since the sum type must record somewhere the type of the value that it currently holds, I believe it means that the zero value of the sum type is not all-bytes-zero, which would make it different from every other type in Go. Or perhaps we could add an exception saying that if the type information is not present, then the value is the zero value of the first type listed, but then I'm not sure how to represent nil if it is not the first type listed.

Loading

@jimmyfrasche
Copy link
Member

@jimmyfrasche jimmyfrasche commented Mar 22, 2017

So (stuff) | nil only makes sense when nothing in (stuff) can be nil and nil | (stuff) means something different depending on whether anything in stuff can be nil? What value does nil add?

@ianlancetaylor I believe many functional languages implement (closed) sum types essentially like how you would in C

struct {
    int which;
    union {
         A a;
         B b;
         C c;
    } summands;
}

if which indexes into the union's fields in order, 0 = a, 1 = b, 2 = c, the zero value definition works out to all bytes are zero. And you'd need to store the types elsewhere, unlike with interfaces. You'd also need special handling for the nil tag of some kind wherever you store the type info.

That would make union's value types instead of special interfaces, which is also interesting.

Loading

@shanemhansen
Copy link
Contributor

@shanemhansen shanemhansen commented Mar 22, 2017

Is there a way to make the all zero value work if the field which records the type has a zero value representing the first type? I'm assuming that one possible way for this to be represented would be:

type A = B|C
struct A {
  choice byte // value 0 or 1
  value ?// (thing big enough to store B | C)
}

[edit]

Sorry @jimmyfrasche beat me to the punch.

Loading

@jimmyfrasche
Copy link
Member

@jimmyfrasche jimmyfrasche commented Mar 22, 2017

Is there anything added by nil that couldn't be done with

type S int | string | struct{}
var None struct{}

?

That seems like it avoids a lot of the confusion (that I have, at least)

Loading

@jimmyfrasche
Copy link
Member

@jimmyfrasche jimmyfrasche commented Mar 22, 2017

Or better

type (
     None struct{}
     S int | string | None
)

that way you could type switch on None and assign with None{}

Loading

@bcmills
Copy link
Member

@bcmills bcmills commented Mar 22, 2017

@jimmyfrasche struct{} is not equal to nil. It's a minor detail, but it would make type-switches on sums needlessly(?) diverge from type-switches on other types.

Loading

@jimmyfrasche
Copy link
Member

@jimmyfrasche jimmyfrasche commented Mar 22, 2017

@bcmills It wasn't my intent to claim otherwise—I meant that it could be used for the same purpose as differentiating a lack of value without overlapping with the meaning of nil in any of the types in the sum.

Loading

@jimmyfrasche
Copy link
Member

@jimmyfrasche jimmyfrasche commented Mar 22, 2017

@rogpeppe what does this print?

// r is an io.Reader interface value holding a type that also implements io.Closer
var v io.ReadCloser | io.Reader = r
switch v.(type) {
case io.ReadCloser: fmt.Println("ReadCloser")
case io.Reader: fmt.Println("Reader")
}

I would assume "Reader"

Loading

@bcmills
Copy link
Member

@bcmills bcmills commented Mar 22, 2017

@jimmyfrasche I would assume ReadCloser, same as you'd get from a type-switch on any other interface.

(And I would also expect sums which include only interface types to use no more space than a regular interface, although I suppose that an explicit tag could save a bit of lookup overhead in the type-switch.)

Loading

@jimmyfrasche
Copy link
Member

@jimmyfrasche jimmyfrasche commented Mar 22, 2017

@bcmills it's the assigment that's interesting, consider: https://play.golang.org/p/PzmWCYex6R

Loading

@rogpeppe
Copy link
Contributor

@rogpeppe rogpeppe commented Mar 22, 2017

@ianlancetaylor That's an excellent point to raise, thanks. I don't think it's hard to get around though, although it does imply that my "naive implementation" suggestion is itself too naive. A sum type, although treated as an interface type, does not have to actually contain direct pointer to the type and its method set - instead it could, when appropriate, contain an integer tag that implies the type. That tag could be non-zero even when the type itself is nil.

Given:

 var x int | nil = nil

the runtime value of x need not be all zeros. When switching on the type of x or converting
it to another interface type, the tag could be indirected through a small table containing
the actual type pointers.

Another possibility would be to allow a nil type only if it's the first element, but
that precludes constructions like:

var t nil | int
var u float64 | t

Loading

@rogpeppe
Copy link
Contributor

@rogpeppe rogpeppe commented Mar 22, 2017

@jimmyfrasche I would assume ReadCloser, same as you'd get from a type-switch on any other interface.

Yes.

@bcmills it's the assigment that's interesting, consider: https://play.golang.org/p/PzmWCYex6R

I don't get this. Why would "this [...] have to be valid for the type switch to print ReadCloser"
Like any interface type, a sum type would store no more than the concrete value of what's in it.

When there are several interface types in a sum, the runtime representation is just an interface value - it's just that we know that the underlying value must implement one or more of the declared possibilities.

That is, when you assign something to a type (I1 | I2) where both I1 and I2 are interface types, it's not possible to tell later whether the value you put into was known to implement I1 or I2 at the time.

Loading

@jimmyfrasche
Copy link
Member

@jimmyfrasche jimmyfrasche commented Mar 22, 2017

If you have a type that's io.ReadCloser | io.Reader you can't be sure when you type switch or assert on io.Reader that it's not an io.ReadCloser unless assignment to a sum type unboxes and reboxes the interface.

Loading

@jimmyfrasche
Copy link
Member

@jimmyfrasche jimmyfrasche commented Mar 22, 2017

Going the other way, if you had io.Reader | io.ReadCloser it would either never accept an io.ReadCloser because it goes strictly right-to-left or the implementation would have to search for the "best matching" interface from all interfaces in the sum but that cannot be well defined.

Loading

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Sep 30, 2020

I've opened #41716 to discuss the way that a version of sum types appears in the current generics design draft.

Loading

@ProximaB
Copy link

@ProximaB ProximaB commented Oct 21, 2020

I just wanted to share an old proposal from @henryas about Algebraic Data Types. It's very good written with provided use cases.
#21154
Unfortunately, It has been closed by @mvdan on the same day without any appreciation of the work. I'm pretty sure that person really felt that way and thus there are no further activities on the gh account. I feel sorry for that guy.

Loading

@andig
Copy link
Contributor

@andig andig commented Oct 22, 2020

I really like #21154. It seems to be a different thing though (and hence @mvdan's) comment closing it as dupe not quite hitting. Reopen there or include in the discussion here?

Loading

@maxekman
Copy link

@maxekman maxekman commented Oct 22, 2020

Yeah, I would really like to have the ability to model some more high level business logic in a similar way as described in that issue. Sum types for enum-like, restricted options, and the suggested accepted types as in the other issue would be awesome in the toolbox. Business/domain code in Go sometimes feels a bit clunky at the moment.

Loading

@tj
Copy link

@tj tj commented Oct 22, 2020

My only feedback is that type foo,bar inside of an interface looks a bit awkward and second-class, and I agree that there should be a choice between nullable and non-nullable (if possible).

Loading

@Merovius
Copy link

@Merovius Merovius commented Oct 22, 2020

@ProximaB I don't understand why you say "there are no further activities on the gh account". They have since created and commented on a bunch of other issues as well, many of them on the Go project. I see no evidence that their activity has been influenced by that issue at all.

Furthermore, I strongly agree with Daniel closing that issue as a dupe of this one. I don't understand why @andig says that they propose something different. As far as I can understand the text of #21154, it proposes exactly the same thing we are discussing here and I wouldn't be at all surprised if even the exact syntax was already suggested somewhere in this megathread (the semantics, as far as described, most certainly were. Multiple times). In fact, I would go so far as to say Daniels closing is proven right by the length of this issue, because it already contains quite a detailed and nuanced discussion of #21154, so repeating all of that would have been arduous and redundant.

I agree and understand that it's probably disappointing to have a proposal closed as a dupe. But I don't know of a practical way to avoid it. Having the discussion in one place seems beneficial to everyone involved and keeping multiple issues for the same thing open, without any discussion on them, is clearly pointless.

Loading

@andig
Copy link
Contributor

@andig andig commented Oct 22, 2020

Furthermore, I strongly agree with Daniel closing that issue as a dupe of this one. I don't understand why @andig says that they propose something different. As far as I can understand the text of #21154, it proposes exactly the same thing we are discussing here

Rereading this issue I agree. Seems I confused with this issue with generics contracts. I'd strongly support sum types. I didn't mean to sound harsh, please take my apology if it came across like that.

Loading

@mvdan
Copy link
Member

@mvdan mvdan commented Oct 22, 2020

I'm a human and issue gardening can be tricky at times, so by all means point out when I make a mistake :) But in this case I do think that any specific sum types proposal should fork from this thread just like #19412 (comment)

Loading

@henryas
Copy link

@henryas henryas commented Oct 22, 2020

I'm a human and issue gardening can be tricky at times, so by all means point out when I make a mistake :) But in this case I do think that any specific sum types proposal should fork from this thread just like #19412 (comment)

@mvdan is not human. Trust me. I am his neighbor. Just kidding.

Thank you for the attention. I am not that attached to my proposals. Feel free to mangle, to modify, and to shoot down any part of them. I have been busy in real life, so I haven't got a chance to be active in the discussions. It is good to know that people read my proposals and some actually like them.

The original intention is to allow grouping of types by their domain relevance, where they don't necessarily share common behaviors, and have the compiler enforce that. In my opinion, this is just a static verification problem, which is done during compilation. There is no need for the compiler to generate code that retains the complex relationship among types. The generated code may treat these domain types normally as if they are the regular interface{} type. The difference is that the compiler now does additional static type checking upon compilation. That is basically the essence of my proposal #21154

Loading

@ProximaB
Copy link

@ProximaB ProximaB commented Oct 22, 2020

@henryas Great to see you! 😊
I'm wondering if Golang hadn't used duck typing would have that made the relationship between types much more strict and allow grouping object by their domain relevance as you described in your proposal.

Loading

@henryas
Copy link

@henryas henryas commented Oct 23, 2020

@henryas Great to see you! 😊
I'm wondering if Golang hadn't used duck typing would have that made the relationship between types much more strict and allow grouping object by their domain relevance as you described in your proposal.

It would, but that would break the compatibility promise with Go 1. We probably wouldn't need sum types if we have explicit interface. However, duck typing is not necessarily a bad thing. It makes certain things more lightweight and convenient. I enjoy duck typing. It is a matter of using the right tool for the job.

Loading

@ProximaB
Copy link

@ProximaB ProximaB commented Oct 24, 2020

@henryas I agree. It was a hypothetical question. Go creators definitely deeply considered all ups and downs.
On the other hand coding guid like verifying interface compliance would never appear.
https://github.com/uber-go/guide/blob/master/style.md#verify-interface-compliance

Loading

@Merovius
Copy link

@Merovius Merovius commented Oct 24, 2020

Can you please have this off-topic discussion somewhere else? There are a lot of people subscribed to this issue.
Open interface satisfaction has been part of Go since its inception and it's not going to change.

Loading

@ibraheemdev
Copy link

@ibraheemdev ibraheemdev commented Aug 21, 2021

#45346 has been accepted, and sum types seem like a very natural extension to "type sets", that are currently only allowed in constraints:

type SignedInteger interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}

Possible future step: permitting constraints as ordinary interface types

We have proposed that constraints can embed some additional elements. With this proposal, any interface type that embeds anything other than an interface type can only be used as a constraint or as an embedded element in another constraint. A natural next step would be to permit using interface types that embed any type, or that embed these new elements, as an ordinary type, not just as a constraint.

We are not proposing that today. But the rules for type sets and methods set above describe how they would behave.
Any type that is an element of the type set could be assigned to such an interface type. A value of such an interface type would permit calling any member of the corresponding method set.

This would permit a version of what other languages call sum types or union types. It would be a Go interface type to which only specific types could be assigned. Such an interface type could still take the value nil, of course, so it would not be quite the same as a typical sum type.

In any case, this is something to consider in a future proposal, not this one.

Loading

@chabad360
Copy link

@chabad360 chabad360 commented Nov 15, 2021

Can we start discussion on this now(/soon)?

It seems to me that this wouldn't be very complicated to allow, and for that matter the current behavior with constraints is actually confusing (being that it is still an interface but can't be used as an interface). As I see it, since interfaces are already constraints this is also a natural extension of interfaces (i.e. specifying which types are allowed as this interface).

This would still depend on #45380 and how approximation elements are handled, as well as a resolution to #45346 (comment) .

Loading

@DeedleFake
Copy link

@DeedleFake DeedleFake commented Nov 15, 2021

I'm a bit worried that restricting this is going to result in undue usage of generics. In a recent talk, one of the developers said that generics shouldn't be used just for the sake of using them when regular interfaces will work just fine, but by restricting this I think some people will start shoving generics in where it makes no sense just to get access to something like sum types. It doesn't work in every situation, but it does in some and I'm worried, though not much, admittedly, that it will have some strange incentives.

Loading

@chabad360
Copy link

@chabad360 chabad360 commented Nov 15, 2021

I'm not quite sure what the problem you're seeing is. Regular interfaces don't work fine here. Having a sum type allows you to take advantage of the type checker instead of returning an error, which reduces the amount code most interface{} based systems need (by at least a few lines per method). As well as reduce the amount of bugs that result from not being prevented at compile time from providing a bad value.

Also, this can hardly be called an (undue) usage of generics, as it only uses the type constraint syntax.

Loading

@Merovius
Copy link

@Merovius Merovius commented Nov 15, 2021

@DeedleFake

by restricting this I think some people will start shoving generics in where it makes no sense just to get access to something like sum types.

I'm not worried about that. I don't think there are a lot of use-cases where people would want to use sum-types and can use generics (but where generics would be misused). For example, you can't use them in protocol decoding, because you would have to know the actual value of the type used statically, which defeats the entire point.

Loading

@Splizard
Copy link

@Splizard Splizard commented Nov 18, 2021

@Merovius
On the point of protocol decoding, if/when we do get sum types, it would be useful to reflect on the possible concrete types that a sum-type interface can contain in order to avoid having to register everything ie. gob.Register.

Loading

@dsnet
Copy link
Member

@dsnet dsnet commented Nov 19, 2021

Using sum types with protocol decoding can be dangerous since it assumes that we leak type names to the serialized data. Type names are not guaranteed to be stable due to the existence of type aliases. See #36345.

Loading

@rogpeppe
Copy link
Contributor

@rogpeppe rogpeppe commented Nov 19, 2021

Using sum types with protocol decoding can be dangerous since it assumes that we leak type names to the serialized data

I'm not sure that's necessarily the case. I think a codec could potentially rely on a discriminator/name supplied by the types that are components of the sum type rather than the type names themselves. That would require that we can enumerate members of a sum type with reflect, but I'd be surprised it that wasn't possible if this proposal were accepted.

Loading

@dsnet
Copy link
Member

@dsnet dsnet commented Nov 19, 2021

That would require that we can enumerate members of a sum type with reflect, but I'd be surprised it that wasn't possible if this proposal were accepted.

Suppose we had the following:

type IntAlias = int
type Sum interface {
    IntAlias | foopkg.SomethingType | barpkg.SomethingType | ~float64
}

What would an API for enumerating the members report?

  • Would it report the fully qualified type (i.e., resolving any aliases)? If yes, then it would still run into the problem I describe in #36345, which occurs because the exact type can move around through the use of aliases.
  • Would it report the local name of the type at declaration (similar to how embedded fields take the name of the type)? What happens if two types have the same local name (e.g., both named SomethingType)?
  • Would it report both pieces of information?

Loading

@rogpeppe
Copy link
Contributor

@rogpeppe rogpeppe commented Nov 19, 2021

Isn't #36345 a problem specifically for gob because it uses Go type names on the wire? I don't think that's a great idea in general, and it doesn't seem necessary to me.

FWIW I'd expect the API for enumerating the members to report the fully qualified type without aliases, just like all the other reflect APIs.

Loading

@dsnet
Copy link
Member

@dsnet dsnet commented Nov 19, 2021

I don't think that's a great idea in general, and it doesn't seem necessary to me.

Agree that it's not a great idea, but I'm not sure I understand why you say "it doesn't seem necessary". In order for the unmarshaler to distinguish which type in the sum type was encoded, it needs to encode the name of the type somehow. If it's not using the fully-qualified name, what is it encoding instead?

Loading

@rogpeppe
Copy link
Contributor

@rogpeppe rogpeppe commented Nov 19, 2021

If it's not using the fully-qualified name, what is it encoding instead?

It could use a name that it finds by calling a method on type (you'd need to make that method work on the zero value of each type, but that might not be too much to ask).

Loading

@chabad360
Copy link

@chabad360 chabad360 commented Nov 22, 2021

As I understand it, when these values are getting marshaled, they usually can't all be marshaled the same way (different encoding methods, etc.). But I believe most binary formats have a way of telling the parser what what is the type of the value they're storing, and don't rely on having some form of reflection available to figure it out. In this context, having a sum type is useful because it allows one to limit the allowed types that are fed to the marshaler, avoiding a class of runtime errors in favor of just using the type checker.

But to be more specific to this question here (cause I don't think that answered it directly), how would this be different from the way we currently handle an interface{}? You put it in a type switch, right? What is more complicated here?

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet