Re: [go-nuts] Dynamic composition of interfaces at runtime?

2020-09-02 Thread roger peppe
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 
> 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 
>>> 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
 

Re: [go-nuts] Dynamic composition of interfaces at runtime?

2020-09-02 Thread Jesper Louis Andersen
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  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 
>> 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
>>> 
>>> .
>>>
>> --
> 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
> 

Re: [go-nuts] Dynamic composition of interfaces at runtime?

2020-08-26 Thread cpu...@gmail.com
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  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
>>  
>> 
>> .
>>
>

-- 
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.


Re: [go-nuts] Dynamic composition of interfaces at runtime?

2020-08-26 Thread 'Axel Wagner' via golang-nuts
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  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+unsubscr...@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
> 
> .
>

-- 
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/CAEkBMfEQq%2BErDOGbX5jD1PrnZQdrYut_E6zajRpYYbM3NiBe1g%40mail.gmail.com.