On Wed, 2 Sep 2020 at 10:56, Jesper Louis Andersen < jesper.louis.ander...@gmail.com> wrote:
> 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. > An alternative approach is to use a struct containing function fields rather than an interface, which allows easy inspection of capabilities too. That's the approach that Nick Craig-Wood ended up using for rclone; see this type for an example: https://pkg.go.dev/github.com/rclone/rclone/fs?tab=doc#Features > 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 > <https://groups.google.com/d/msgid/golang-nuts/CAGrdgiUJQSJwvY1eYWiRN%2BGGumJsDXfarhwpvhLoVRub0d%2BR3g%40mail.gmail.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/CAJhgacg0zAZ_ffrt4NWh%2Bbz4S_BavQPhmo7tiv_D0sMjYBvJAQ%40mail.gmail.com.