[HN Gopher] A rough proposal for sum types in Go (2018)
___________________________________________________________________
A rough proposal for sum types in Go (2018)
Author : isaacimagine
Score : 69 points
Date : 2021-11-15 18:14 UTC (4 hours ago)
(HTM) web link (manishearth.github.io)
(TXT) w3m dump (manishearth.github.io)
| titzer wrote:
| I didn't see a discussion of equality checking here, so I'll
| mention it. It's really useful if sum types (aka variants or
| algebraic data types) do _not_ have identity, i.e. that creating
| two identical values with the same tag (or case) with the same
| values compare equal. This allows the compiler to represent sums
| efficiently, e.g. by packing two 32-bit values into a single
| 64-bit word. With reference identity (two constructed values are
| only equivalent if they refer to the same on-heap
| representation), then this optimization becomes harder (basically
| only works with escape analysis).
|
| Virgil has variants and also enums. They are slightly different
| things, though treated much the same under the hood. Neither have
| identity. (How they are different is that enums can have
| arguments in Virgil, but there are still is a fixed list of named
| values, whereas variants have named cases and are potentially
| recursive).
| kadoban wrote:
| Yes please! I really dislike every golang function returning
| "foo*, error" when it really means either foo or an error.
| bborud wrote:
| I disliked it initially. After 5-6 years of using Go as my
| primary language I've come to appreciate it. It is clearer than
| having to figure out what is returned. And if an error can be
| returned. It's easier when it is right there in the function
| signature.
| pkaye wrote:
| So why not use Rust instead since it already implements sum
| types?
| tialaramex wrote:
| They said they liked sum types, that doesn't automatically
| mean they want to learn Rust. I liked the STL containers when
| I read about them in the 1990s, but I don't like C++ and the
| existence of the containers did not change that.
| kadoban wrote:
| Because I have a job and am not yet a dictator at my company
| for technical decisions.
| [deleted]
| ziml77 wrote:
| Because Rust throws out the GC which means that you have to
| deal with complexities of lifetime management.
|
| (I don't agree that sum types should be added to Go, as much
| as I dislike the language as it is)
| akira2501 wrote:
| Many functions return foo AND the error. This is occasionally
| very useful.
| tialaramex wrote:
| That's not an error then, or, that's not what I'd consider a
| result, or both.
|
| Compare Rust's https://doc.rust-
| lang.org/std/str/fn.from_utf8.html
|
| If you've got a buffer full of bytes, which you hope are UTF8
| text but maybe aren't, call std::str::from_utf8 and you
| _either_ get back a string slice with your text in it, _or_
| you get a std::str::Utf8Error structure which explains what
| the problem was in an actionable format. You can write code
| to process this Utf8Error and your bytes - maybe you 're
| reading blocks of data and it's all valid UTF8 but the last
| character is continued in the next block for example, you can
| discern that from Utf8Error and just turn the valid part into
| UTF8 and keep the rest until the next block is available.
| kadoban wrote:
| Ocassionally, but pretty rarely in my experience. Both types
| of functions should exist, it should not be so hard to return
| an either/or.
| feffe wrote:
| Sum types would be nice to describe protocols such a grpc as
| well (given suitable syntactic sugar to switch on them).
|
| I think sum types, possibly in combination with tuples would
| have been nicer than multiple return values. But I guess
| everyone has their particular wish list of what a programming
| language should be :-)
| Andys wrote:
| Won't the generics feature type sets fulfill this use case?
| xiaodai wrote:
| Stop trying to make Go complicate. Stop try my to add templates.
| Just stop. KISS
| avaldes wrote:
| > Stop trying to make Go complicate.
|
| You mean enterprise-y.
| ben0x539 wrote:
| Templates? Generics are already on the way, this is a very
| unrelated suggestion, and seems much less complicated.
| kibwen wrote:
| Note that this is from three years ago, and should have (2018)
| appended to the title.
| dang wrote:
| Added. Thanks!
| ibraheemdev wrote:
| The type sets proposal for Go has already been accepted as a
| clarification to the generics proposal [0]:
| type SignedInteger interface { ~int | ~int8 | ~int16
| | ~int32 | ~int64 }
|
| Interfaces that contain type sets are only allowed to be used in
| generic constraints. However, a future extension might permit the
| use of type sets in regular 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.
|
| This along with exhaustive type switches would bring Go something
| close to the sum types of Rust and Swift.
|
| [0]: https://github.com/golang/go/issues/45346
| adtac wrote:
| What's the best way to read about all the new _accepted_
| grammar developments in Go in the last year or two?
| benhoyt wrote:
| The Go grammar/language changes very slowly -- most of the
| improvements are in the tooling and libraries, so there isn't
| much. But the best way is the release notes. For the last two
| years (four versions):
|
| https://golang.org/doc/go1.17: three very minor changes for
| array pointers and unsafe
|
| https://golang.org/doc/go1.16: no language changes
|
| https://golang.org/doc/go1.15: no language changes
|
| https://golang.org/doc/go1.14: minor change to allow
| "overlapping interfaces"
|
| Of course, in 1.18 -- coming out in about three months --
| there's the huge change to add generics (type parameters).
| The best way to read about that is in the type parameters
| proposal here: https://go.googlesource.com/proposal/+/refs/he
| ads/master/des...
|
| Go 1.18 will also add built-in fuzzing support, but again,
| that's a tooling/library change, not a grammar/language one.
| saghm wrote:
| One difference between this proposal and Rust enums (i.e.
| tagged unions) is that enums let you use the same type more
| than once with a different tag. Obviously Go doesn't have
| generics yet, but something like `Result<String, String>`
| doesn't seem like it would be straightforward as a non-generic
| type either with a type set, since you don't have any way of
| differentiating between which "type" of string you might have.
| I _think_ this might be possible with a typeset by defining a
| newtype for one or both of the string types, but I haven't used
| Go in long enough that I don't remember if newtypes will
| implicitly convert to the type they wrap or not.
| masklinn wrote:
| With this proposal the variants are simply types themselves,
| so you'd have a struct Ok[T] and Err [T], and thus Result<T,
| E> would be something like interface
| Result[T, E] { for Ok[T], Err[E] }
|
| And there's no issue with having T=string and E=string.
| pcwalton wrote:
| Yeah, the standard solution is to create a newtype for each
| such variant. I believe this is the preferred idiom in
| languages like TypeScript that have union types but not
| _discriminated_ union types.
| cube2222 wrote:
| They don't implicitly convert (though you can still use
| literals to create them). So yes, specifying Ok and Error
| string types should hypothetically work for this.
| ibraheemdev wrote:
| I believe this would work with type sets:
| type Result[T any, E any] interface { T | E
| }
| pcwalton wrote:
| That looks like a great feature to add to Go! It seems to make
| the original article (which is from 2018) obsolete.
| jerf wrote:
| What does this get you that you don't get by putting an
| unexported method in your Go interface today? Honest question,
| since I'm not 100% sure I'm sharing the terminology of the
| author, and I don't know if this is a matter of the author being
| unaware of this possibility, or aware and not satisfied for some
| reason I don't understand.
|
| You don't get a literal enumeration of all implemented types in
| the interface specification itself, but it's still
| trivial/mechanical to extract all implementations of such an
| interface with some code tool, and no external package can add to
| the list.
|
| (Since I've learned from experience this always comes up: If you
| put a method named 'unexported' on your interface, it doesn't
| matter if a value in some other package creates a method called
| 'unexported', the compiler will not consider it a match. An
| interface in some package with an unexported method can not be
| implemented by any other package.)
| ben0x539 wrote:
| In this proposal, the compiler will yell at you if you switch
| over the contained type of a value of the interface type and
| don't have a case for each possible type.
| renlo wrote:
| I think a difference is that current behavior is to check the
| interface at runtime vs static check ensuring that the
| interface's underlying value is one of those types. Have
| personally seen a number of bugs (ie panics) from the runtime
| checks
___________________________________________________________________
(page generated 2021-11-15 23:00 UTC)