What I really wish Go had was sum types, Rust style. That'd cover enumerations and more.
What I really wish Go had was sum types, Rust style. That'd cover enumerations and more.
type Vehicle interface {
isVehicle()
}
type Car struct {}
func (c Car) isVehicle() {}
type Van struct {}
func (v Van) isVehicle() {}
func VehicleType(vehicle Vehicle) {
switch v := vehicle.(type) {
case Car:
fmt.Println("car")
case Van:
fmt.Println("van")
default:
fmt.Println("unknown vehicle")
}
This covers most, but not all, of the bases, in that you don't get exhaustiveness checking at compile time, unless you adjoin a linter to your compile process: https://github.com/BurntSushi/go-sumtypePattern matching and exhaustive checks are massive benefits of them though. I guess I'm just oddly conflicted here.
I do think that long term, sum types are going to become more prevalent. I'm excited to see it, I'm just interested in how that adoption occurs in existing languages without them.
Sum types are useful, even very useful in the right place, but I do think there's a lot of people who use them a couple of times, probably in one of those "right places" and then mistakenly label them in their mind as "better". Just, universally, Platonically "better". They aren't in fact "better"; they're a tool. Sometimes they're the right tool for the job, but much, much more often, they're just a tool that works, and so do several other tools. People who get too excited about sum types need to be sure they square their understanding of how useful they are with the fact that the vast majority of programs are written without them.
I feel this pain basically any time I hand-write lexical scanners/parsers in Go or even C++.
That's because they are. jerf subtly shifts the point to one they can criticise, but it does not change the basic fact that sum types are a critical tool which is just... missing.
There's probably no tool which can't be misused, even the humble boolean, that's not an issue with the tool, and pointing that out is at best irrelevant. It does not change the fact of the matter: you're missing a critical axis of composition. It's like saying screwdrivers are useful but you can misuse them to hammer nails as if that justified trying to screw with a hammer.
Moreover, Go seems to attract this sort of criticism as if Go is Uniquely Broken and it's nirvana in all the other languages... but I've used enough of them to know better. Sum types are great, until you hit the branch of the expression problem where you really need the other side, and if you're in a language that favors them, you're going to get the same 75% experience, just mirror imaged.
Which other side? Product types (aka structs)? I don't think there are any languages with Sum types that don't also include Product types.
I am not clear what you mean by that, exactly, but generally I model a state machine as a type that composes a state:
type State interface {
isState()
}
// a whole bunch of state types here
type StateMachine struct {
State // exported or not exported, depending on local needs
// additional data
}
func (sm *StateMachine) Event1(args...) error {
// can call current state and change it here
}
You can add an "Execute" method on to the State and have it return an entire state if that's how you want to do it. The state machine can pass in any cross-state data. There's a number of options. You aren't obligated to do exactly, only, and precisely what you'd do in another language, and program X in Y. For some reason, that's common wisdom in the programming world... except for functional programming. I say "don't program X in Y" is simply true and there is no carveout for functional programming in non-FP languages any more than there is the other way around for OO in FP languages.> I don't really care if you do or do not stay angry at the language you're forced to work in for your job. What I do want for you is to be able to make the best of the situation you're in and not be unhappy.
Because maybe enumerations in Go are subpar compared to other languages (FP or not).
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.
enum Message {
Action1(String, Option<Mode>),
Action2(String),
}
Action1 and Action2 are variants not types and I cannot make functions that take an Action1 or an Action2 as a parameter. To get around this people will often make each enum variant just a container for some struct with the same name but that's more verbose and matching becomes slightly uglier. Code like this is fairly common: struct Action1(String, Option<Mode>);
struct Action2(String);
enum Message {
Action1(Action1),
Action2(Action2),
}
Ideally I'd like to be able to succinctly say something like: type A = B | C
and have A be dependent on B and C but have both B and C be completely independent types.Though, I still like the flexibility of adding additional fields to `Action1`. In golang I end up with fields that are conditionally populated based on the state of an Enum which is less than ideal and leads to lots of error checking (though this is relatively rare).
It doesn't have to be one or the other though. A bit more polishing on the Rust-style enums (perhaps in a different language) could lead to pretty ergonomic code.
Is this... right? You can't use enum variants as types in function signatures, variable declarations, etc.