4 ms·
Using struct embedding for pure data to implement discriminated unions is fine, better than MarshalJSON() that is lost on a type definition. Using it to save ty
by oefrha 1y ago
Using struct embedding for pure data to implement discriminated unions is fine, better than MarshalJSON() that is lost on a type definition. Using it to save typing, or going crazy with it (I consider embedding two things going crazy) is bad.
- nine_k 1y agoI think that using embedding for discriminating unions if a good idea. It would work, but it does not force the user to do the discrimination. I would say that explicit typecasting at the point of discrimination is safer. Without it, nothing prevents you from using one field from one variant of the union, and another from a different variant. Introduction of proper discriminated unions would be great.
- oefrha 1y agoI'm not sure I understand you, or you understand me. I'm saying this is okay: type Order struct { Type OrderType CommonAttr1 int CommonAttr2 string } type OrderTypeA struct { Order TypeAAttr1 int TypeAAttr2 string } type OrderTypeB struct { Order TypeBAttr1 int TypeBAttr2 string } And yes you should convert to OrderTypeA or OrderTypeB at the first opportunity in domain code, and only convert from them at the latest opportunity. You seem to be under the impression that I'm advocating for something like type OrderUnion struct { CommonAttr1 int CommonAttr2 string TypeAAttrs TypeBAttrs } That's what I consider going crazy.
- debugnik 1y ago> And yes you should convert to OrderTypeA or OrderTypeB at the first opportunity in domain code, Go can only downcast through interfaces so there's something missing to your approach to unions, isn't there?
- oefrha 1y agoThe missing something is manually creating the structs, not casting.
- Thorrez 1y ago>And yes you should convert to OrderTypeA or OrderTypeB at the first opportunity How would you convert Order to OrderTypeA? You would need some other source to fill TypeAAttr1 and TypeAAttr2 with.
- jerf 1y agoNeeding further external information to "convert" from one struct type to another is fairly common and completely normal. One I happen to have encountered in multiple places over the years is normalizing user names. Sometimes there is no mechanical process to normalize user names, such as simply lowercasing them; you may need access to an LDAP server to get a canonical name/account, or access to information about local email munging rules (like "which character do you use to allow users to specify multiple addresses for themselves, like gmail uses '+'?" - not all systems use +), or you may need DB access to verify the user name exists if you want a value of the given type to represent a user that is not only normalized but guaranteed to exist.
- debugnik 1y agoYou said struct embedding could be used for discriminated unions, but there's no mechanism to discriminate between union variants here.
- oefrha 1y agoGo simply doesn't have discriminated unions, so a number of pattern can all be called "discriminated unions" in Go. I was simply emphasizing that sharing common fields between pure data structs with struct embedding (commonly seen in but not limited to discriminated unions) but now people are weirdly hung up on discriminated unions. I just showed a data model with a discriminator (.Type), there are a number of mechanisms to discriminate depending on your actual needs. You can make the types conform to an interface, pass an interface value around and cast to specific types. You can get a fat row with a bunch of left joins from your database then immediate create type-specific structs and call type-specific methods with them. You can get a discriminated union on the wire, unmarshal the type field first, then choose the type-specific struct to unmarshal to. Etc. These are largely irrelevant in a discussion about type embedding.