Usually, my experience is that these highly dynamic interfaces is an indication you want to approach the problem from a different angle. You will converge on something that gets closer and closer to having an interface{}, and this means you are converging toward a world of dynamic typing. It often happens because you spot an initial pattern and try to hoist out common code. But there are situations where taking a completely different route yields far simpler code which is easier to handle.
Code will have to branch based on the data you have available. This is the primary reason why languages add sum-types. Go doesn't have sum-types, so you'll have to build your own variant, usually by having an indicator in a struct what you are looking at. However, this means you have no exhaustiveness checks, and no automatic optimization, which is the price you pay for this simplification. On Wed, Aug 26, 2020 at 7:10 PM cpu...@gmail.com <cpui...@gmail.com> wrote: > Great post, thank you! Found you Wrap method looks exactly like my > decorate :). > > While not entirely satisfying it does solve the problem and the set of > optional methods in my case is always below 5 so the effort is manageable. > Since the optional interfaces are really narrow (always 1 method) I guess I > can proceed with this approach... > > Kind regards, > Andi > > On Wednesday, August 26, 2020 at 5:59:13 PM UTC+2 > axel.wa...@googlemail.com wrote: > >> Hi, >> >> no, there isn't really a solution to this. I've blogged about this: >> >> https://blog.merovius.de/2017/07/30/the-trouble-with-optional-interfaces.html >> A combination of generics and embedding can help. So, you can do >> >> type Wrapper[T] struct { >> T >> otherFields >> } >> >> func (w *Wrapper) SomeNewMethod() { >> } >> >> But even then, you need to know the static type of what you're wrapping >> (so http Middleware, for example, couldn't use this). >> >> I'm also not super sure if it's a good idea to do this even if you could. >> An interface might contain multiple, interdependent methods. As an example, >> `http.ResponseWriter` implicitly calls `WriteHeader` the first time you >> call `Write`. So, if you wrap a `ResponseWriter` and overwrite >> `WriteHeader`, that overwritten method has to be called explicitly (the >> underlying `ResponseWriter` will call its own `WriteHeader`). So if you >> don't know what the methods of the wrapped type are and do, you might >> introduce very hard to find bugs. >> >> On Wed, Aug 26, 2020 at 5:49 PM cpu...@gmail.com <cpu...@gmail.com> >> wrote: >> >>> Hi All, >>> >>> using interfaces for behavioural testing seems a common pattern in go: >>> >>> if typed, hasCapability := d.(Capability); hasCapability... >>> >>> I have the requirement to dynamically compose interfaces at runtime, >>> i.e. I cannot know upfront which set of interfaces a resulting type will >>> support: >>> >>> type resultingType interface { >>> basicCapability >>> ...any combination of additional capabilities >>> } >>> >>> If there is only a single additional/optional capability to implement >>> that is simple to solve, e.g. by implementing a wrapping struct that embeds >>> the basic capability and adds an additional capability. Applying this >>> pattern across multiple layers (stack of wrapping structs does not work as >>> the embedded interfaces don't bubble up). >>> >>> However, the number of additional capabilities that a type might have to >>> support could be bigger (think of physical meters which could supply power, >>> energy, currents, frequency etc). >>> >>> I'm solved this by using go:generate which is fed the basic and set of >>> optional types and generates additional types for any _combination_ of >>> additional types, an example is attached (and source at >>> https://github.com/andig/evcc/pull/310/files#diff-fa40c05651b4682eb25a198f8a4a98f0). >>> This is similar to the "old" pattern of simulation generics via generated >>> code. >>> >>> I'm wondering if using a pattern of dynamic composition is sound, if the >>> the go:generate approach is a good one or if there are better ways? >>> >>> Kind regards, >>> Andi >>> >>> -- >>> You received this message because you are subscribed to the Google >>> Groups "golang-nuts" group. >>> To unsubscribe from this group and stop receiving emails from it, send >>> an email to golang-nuts...@googlegroups.com. >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/golang-nuts/217cee40-1e00-442d-a4d2-7d4a37e41544n%40googlegroups.com >>> <https://groups.google.com/d/msgid/golang-nuts/217cee40-1e00-442d-a4d2-7d4a37e41544n%40googlegroups.com?utm_medium=email&utm_source=footer> >>> . >>> >> -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/bacb1b26-daf2-49b3-8d56-e9530803e6f4n%40googlegroups.com > <https://groups.google.com/d/msgid/golang-nuts/bacb1b26-daf2-49b3-8d56-e9530803e6f4n%40googlegroups.com?utm_medium=email&utm_source=footer> > . > -- J. -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAGrdgiUJQSJwvY1eYWiRN%2BGGumJsDXfarhwpvhLoVRub0d%2BR3g%40mail.gmail.com.