4 ms·
Seems to be trying to fit Haskell into Go, which is probably not what you want to do. What exactly do "sum types" get you that a more direct implementation does
by pbnjay 12y ago
Seems to be trying to fit Haskell into Go, which is probably not what you want to do. What exactly do "sum types" get you that a more direct implementation does not? This may be a case where the simplified example isn't describing the problem very clearly.
Instead of using interfaces for simple "tagging", have them do something useful to all the types in the system, for example return the value (identity or the operation): http://play.golang.org/p/bDbzb_ItfZ http://play.golang.org/p/bDbzb_ItfZ
This gets you a shorter, more idiomatic representation which is easier to follow and doesn't rely on an eval function.
It also allows you to later add in type-checking:
http://play.golang.org/p/WkEWF0rg-P http://play.golang.org/p/WkEWF0rg-P
or even further down the road, automatic casting of bool to int:
http://play.golang.org/p/YfCTcoPwuh http://play.golang.org/p/YfCTcoPwuh
- TheHydroImpulse 12y agoSum types are much more direct than other variants in a language that effectively implements them. For example, in Rust, who's `enum`s are effectively sum types, we can take advantage of the type system while using channels that are well-typed. https://github.com/servo/servo/blob/master/components/msg/constellation_msg.rs#L197-L214 https://github.com/servo/servo/blob/master/components/msg/co... pub enum Msg { ExitMsg, FailureMsg(Failure), InitLoadUrlMsg(Url), LoadCompleteMsg, FrameRectMsg(PipelineId, SubpageId, Rect<f32>), LoadUrlMsg(PipelineId, LoadData), ScriptLoadedURLInIFrameMsg(Url, PipelineId, SubpageId, IFrameSandboxState), NavigateMsg(NavigationDirection), PainterReadyMsg(PipelineId), ResizedWindowMsg(WindowSizeData), KeyEvent(Key, KeyState, KeyModifiers), /// Requests that the constellation inform the compositor of the title of the pipeline /// immediately. GetPipelineTitleMsg(PipelineId), /// Requests that the constellation inform the compositor of the a cursor change. SetCursorMsg(Cursor), } Here we can define a sum type called `Msg` that has various variants. These are the only thing a channel constraint to that type can consume. Each destructing or pattern matching has to cover all possible cases, otherwise it's a compiler error. This makes it effortless to have complex communication where the data is composed of multiple things, multiple types. [1] Here's another example of a sum type being used as a messaging type in Servo. [2] Shows pattern matching on all variants and variants of variants, which produces well-typed code. [1]: https://github.com/servo/servo/blob/master/components/net/resource_task.rs#L134-L139 https://github.com/servo/servo/blob/master/components/net/re... [2]: https://github.com/servo/servo/blob/master/components/net/resource_task.rs#L171-L175 https://github.com/servo/servo/blob/master/components/net/re... I probably use `enum`s pretty frequently, especially when working with channels. I don't use primitive types-based channels too often come to think of it.
- jerf 12y ago"Instead of using interfaces for simple "tagging", have them does something useful to all the types in the system," I said that already: "Of course, the tag interface is only used if you have no meaningful interface to define for your sum type. If you do have a real interface you can define you don't need a fake is* function, just define the interface." The point is precisely that this is still useful even if you've got nothing else in common with the types. In the spirit of Go simplicity (cough), I actually prefer that the central pump define all the processing in one place when using a message pump, rather than scatter them around OO-style.