Live data from Hacker News

Compile-time safety for enumerations in Go

vladimir.varank.in

41–50 of 116 posts

Re: Compile-time safety for enumerations in Go

#41
post #28

Earlier quoted context omitted.

At some point I think it's worth asking who are we trying to stop and why.

Indeed. Go types are like simple locks: they keep honest users honest, but they don't prevent dishonest users from breaking your assumptions. The point is just that "compile-time type safety for enumerations" isn't actually true, or even possible. You always have to do some amount of runtime validation of received values.

No, you don't. If your module relies on compile time checks for validity, there's no reason for you to also include runtime checks. Other developers breaking your defined contract is not your responsibility, it's not even a bug.

Re: Compile-time safety for enumerations in Go

#42
post #32

This is not elegant. It also has overhead. Stick with the simple `const Red Color = "red"`, you're not gaining a lot by doing all these strange type contortions in Go. Seriously consider what you are protecting against, and whether it's an imagined bogeyman. "Oh but someone may try to cast arbitrary values to my package type" Okay but who is that going to hurt? You or them? Will they get hit with errors early on in t…

It's stuff that just creeps in. Enum values can come from outside (json, anything non-literal). Now you have to do validation, because the bad values parse and for sure exist at some stage in your program.

It's not a bogeyman.

Re: Compile-time safety for enumerations in Go

#44
post #4
post #2

I dunno if I'd consider having a dummy method on an interface as "elegant", but it does work. A trade-off of keeping the language very simple, for sure. What I really wish Go had was sum types, Rust style. That'd cover enumerations and more.

I go back and forth on this. On one hand, I _love_ Rust/ML style sum types. They're such a fantastic feature. On the other hand, I wonder how much use they'd get in a language like Go. If it's introduced, would it radically change the way some problems get solved? Is it that different than an traditional enum (which Go also skirts around)? Pattern matching and exhaustive checks are massive benefits of them though. I…

Sum types with pattern matching would likely take the place of (foo, error) in a lot of codebases.

This is especially true in cases where a function returns a collection of results where each result is independent from other results and a result can succeed or fail.

For instance, a batch report generator that is called every $INTERVAL might return a ([]Report metadata, error) today, but each report could have succeeded or failed without impacting the others.

The output is emailed to $SOMEONE to let them know the reports ran and information about each.

In today's world, the "error" could be a special error type that capture failures on a per report basis. The ReportMetadata type could also have an Error field, but one is not forced to check either.

Sum types could force checking for error on a per report basis, increasing the odds something is done with the error.

Re: Compile-time safety for enumerations in Go

#45
post #34
post #30

Earlier quoted context omitted.

The counter point is that this is like arguing that an article about safe knife skills are silly, because you can still just stab yourself?

I'm not sure how that's comparable. Compile-time type safety is a boolean thing, you either have it or you don't. If you have it, then my example shouldn't compile. But it does compile, so you don't have it.

I think by insisting to view it only as a boolean thing is very limiting. By that measure, are the type safety hints of Python not really giving any type safety? Just give up and go home, as it were? :D

I'm sympathetic to the points, I think. But I also don't have a problem with someone pushing for some type safety as long as you avoid actively trying to go against it.

Re: Compile-time safety for enumerations in Go

#46

I would be interested to read bug reports from people who thought they had a complete enumeration but learned in production that their enumeration was incomplete. I've never seen it myself.

An easy way to get that is when you use a third-party library, update that to get some new feature, and get a new enum value for free, say because the html parser library started supporting a new node type or added a new error type (the latter will be rare in go because of its lack of sum types).

Ideally, of course, the release notes of the library would spell out the issue, and you’d read them, but even then, making sure you fix this everywhere is way easier if the compiler refuses to compile your code if it doesn’t handle the new case.

Re: Compile-time safety for enumerations in Go

#47
post #31

Earlier quoted context omitted.

Agreed. A slight wart is that default initialization would require either boxing or a somewhat arbitrary decision about which variant should be the default. So if you have e.g. var foo interface{ A | B } then foo either has to be boxed (so the default value is a nil interface) or unboxed and arbitrarily initialized as an A or a B.

To me it would make sense that this be implemented as a normal interface at least initially, it would reuse all the existing bits of the language as is. Note that whether an interface boxes depends on escaping. I don't see how unboxing would have to be "arbitrarily initialized as an A or a B" either. Even with a novel bespoke implementation you still need a discriminant between the two nested. You could keep a nil de…

I agree with your first paragraph.

I think it would be weird if an unboxed union of A and B was initialized to a value that wasn't the default value of either A or B, although I take your point that it's a technically possible implementation.

Re: Compile-time safety for enumerations in Go

#48
post #46

I would be interested to read bug reports from people who thought they had a complete enumeration but learned in production that their enumeration was incomplete. I've never seen it myself.

An easy way to get that is when you use a third-party library, update that to get some new feature, and get a new enum value for free, say because the html parser library started supporting a new node type or added a new error type (the latter will be rare in go because of its lack of sum types). Ideally, of course, the release notes of the library would spell out the issue, and you’d read them, but even then, making…

Did this actually happen to you or are you just theorizing that it could happen?

Re: Compile-time safety for enumerations in Go

#49
post #47

Earlier quoted context omitted.

To me it would make sense that this be implemented as a normal interface at least initially, it would reuse all the existing bits of the language as is. Note that whether an interface boxes depends on escaping. I don't see how unboxing would have to be "arbitrarily initialized as an A or a B" either. Even with a novel bespoke implementation you still need a discriminant between the two nested. You could keep a nil de…

I agree with your first paragraph. I think it would be weird if an unboxed union of A and B was initialized to a value that wasn't the default value of either A or B, although I take your point that it's a technically possible implementation.

> I don't quite understand the second one (is 'boxing' supposed to be 'unboxing'?)

Yep, fixed, sorry about that.

> I think it would be weird if an unboxed union of A and B was initialized to a value that wasn't the default value of either A or B

On the one hand yes, on the other hand it would be consistent with the langage semantics (the default value of an interface type is a nil), and I don’t think defaulting to the default value of an arbitrary variant is better.

Although I can see the similarity with iota / newtype constants, the first item being the default, and similarly people could set the first type as relevantly named (possibly unexported) empty struct if they want / need the signal.

Re: Compile-time safety for enumerations in Go

#50
post #28

Earlier quoted context omitted.

Indeed. Go types are like simple locks: they keep honest users honest, but they don't prevent dishonest users from breaking your assumptions. The point is just that "compile-time type safety for enumerations" isn't actually true, or even possible. You always have to do some amount of runtime validation of received values.

No, you don't. If your module relies on compile time checks for validity, there's no reason for you to also include runtime checks. Other developers breaking your defined contract is not your responsibility, it's not even a bug.

> If your module relies on compile time checks for validity,

...then it's broken, because the Go compiler, factually, cannot (in general) check or enforce the validity of specific values.

> there's no reason for you to also include runtime checks. Other developers breaking your defined contract is not your responsibility, it's not even a bug.

It is absolutely the responsibility of the function

    func PrintColor(c Color)
to ensure, at runtime, that `c` is a valid Color. My example code -- which allowed an invalid Color value of "orange" -- is absolutely a bug in PrintColor.
Post reply on HN