Created
July 26, 2020 18:12
-
-
Save jcorbin/1112aeef88655a681ba32c129ef06069 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "fmt" | |
// rooter is a base interface for example | |
type rooter interface { | |
root() int | |
} | |
// fooRooter is an example extension | |
type fooRooter interface { | |
rooter | |
foo() int | |
} | |
// barRootere is also an example extension | |
type barRooter interface { | |
rooter | |
bar() int | |
} | |
// rootFoo implements the foo extension, delegating through to an underlying rooter | |
type rootFoo struct { | |
rooter | |
f int | |
} | |
// rootBar likewises implements the bare extension minimally | |
type rootBar struct { | |
rooter | |
b int | |
} | |
// zaro is an implementation of the base intereface | |
type zaro struct{ v int } | |
func (z zaro) root() int { return z.v } | |
func (rf rootFoo) foo() int { return rf.f } | |
func (rb rootBar) bar() int { return rb.b } | |
// An example to demonstrate a current stumbling block when implementing | |
// extension interfaces: combinatorics are not in our favor when we want to | |
// implement a suite of orthogonal single-aspect implementations. | |
// | |
// A banal workaround is to implement union types like a `type rootFooBar struct`. | |
// This can be assisted by convenience constructors like `fund withBar(rooter, b int) rooter` | |
// to choose dynamically upgrade to the combined implementation when necessary. | |
// | |
// But that's merely a workaround for a closed setting, and when the number of | |
// aspects is not too large. Another solution that works in an open setting is | |
// the (un)wrapping approach taken by the recent errors package work. | |
// | |
// What would be the downside of making this example work? | |
// Of making it so that interface value casting can pass through to an embedded value? | |
func Example() { | |
a := zaro{1} | |
b := zaro{2} | |
c := zaro{3} | |
for i, r := range []rooter{ | |
a, | |
b, | |
c, | |
rootFoo{a, 4}, | |
rootBar{b, 5}, | |
rootBar{rootFoo{c, 6}, 7}, // NOTE this 6 value will be lost | |
} { | |
var rv, fv, bv int | |
rv = r.root() | |
if f, ok := r.(fooRooter); ok { | |
fv = f.foo() | |
} | |
if b, ok := r.(barRooter); ok { | |
bv = b.bar() | |
} | |
fmt.Printf("[%v] r:%v f:%v b:%v\n", i, rv, fv, bv) | |
} | |
// Output: | |
// [0] r:1 f:0 b:0 | |
// [1] r:2 f:0 b:0 | |
// [2] r:3 f:0 b:0 | |
// [3] r:1 f:4 b:0 | |
// [4] r:2 f:0 b:5 | |
// [5] r:3 f:6 b:7 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
playground: https://play.golang.org/p/oW0_zDXEtuw