Skip to content

Commit 08b63ce

Browse files
committed
design/6977-overlapping-interfaces.md: various updates
- Updated proposed spec changes to match current spec changes at tip. - Added update on implementation status. - Per comments in https://go-review.googlesource.com/c/proposal/+/188197, revised larger example at the end. - Also, added some more small examples illustrating basic behavior and corner cases. Updates golang/go#6977. Change-Id: Ie3906e1a7379b0b15714512e4cafa15c98b22a11 Reviewed-on: https://go-review.googlesource.com/c/proposal/+/200221 Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 719a277 commit 08b63ce

File tree

1 file changed

+71
-25
lines changed

1 file changed

+71
-25
lines changed

design/6977-overlapping-interfaces.md

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Author: Robert Griesemer
44

5-
Last update: 2019-07-30
5+
Last update: 2019-10-10
66

77
Discussion at [golang.org/issue/6977](https://golang.org/issue/6977).
88

@@ -39,14 +39,47 @@ Currently, in the section on [Interface types](https://golang.org/ref/spec#Inter
3939
4040
We propose to change this to:
4141

42-
> An interface `T` may use a (possibly qualified) interface type name `E` in place of a method specification. This is called _embedding_ interface `E` in `T`. The method set of the resulting interface `T` is the _union_ of the method set of `T`’s explicitly declared methods and the method sets of all of `T`’s embedded interfaces.
42+
> An interface `T` may use a (possibly qualified) interface type name `E` in place of a method specification. This is called _embedding_ interface `E` in `T`. The method set of `T` is the _union_ of the method sets of `T`’s explicitly declared methods and of `T`’s embedded interfaces.
4343
4444
And to add the following paragraph:
4545

46-
> The union of two method sets A and B is the method set containing each method of A and B exactly once. If a method in A has the same name as a method in B, both these methods must have identical signatures.
46+
> A _union_ of method sets contains the (exported and non-exported) methods of each method set exactly once, and methods with the same names must have identical signatures.
4747
4848
Alternatively, this new paragraph could be added to the section on [Method sets](https://golang.org/ref/spec#Method_sets).
4949

50+
## Examples
51+
52+
As before, it will not be permitted to _explicitly_ declare the same method multiple times:
53+
54+
```Go
55+
type I interface {
56+
m()
57+
m() // invalid; m was already explicitly declared
58+
}
59+
```
60+
61+
The current spec permits multiple embeddings of an _empty_ interface:
62+
63+
```Go
64+
type E interface {}
65+
type I interface { E; E } // always been valid
66+
```
67+
68+
With this proposal, multiple embeddings of the same interface is generalized to _any_ (not just the empty) interface:
69+
70+
```Go
71+
type E interface { m() }
72+
type I interface { E; E } // becomes valid with this proposal
73+
```
74+
75+
If different embedded interfaces provide a method with the same name, their signatures must match, otherwise the embedding interface is invalid:
76+
77+
```Go
78+
type E1 interface { m(x int) bool }
79+
type E2 interface { m(x float32) bool }
80+
type I interface { E1; E2 } // invalid since E1.m and E2.m have the same name but different signatures
81+
```
82+
5083
## Discussion
5184

5285
A more restricted approach might disallow embedded interfaces from overlapping with the method set defined by the explicitly declared methods of the embedding interface since it is always possible to not declare those “extra” methods. Or in other words, one can always remove explicitly declared methods if they are added via an embedded interface. We believe that would make this language change less robust. For example, consider a hypothetical database API for holding personnel data. A person’s record might be accessible through an interface:
@@ -87,54 +120,67 @@ This is a backward-compatible language change; any valid existing program will r
87120
The implementation requires:
88121

89122
- Adjusting the compiler’s type-checker to allow overlapping embedded interfaces.
90-
- Adjusting go/types analogously.
123+
- Adjusting `go/types` analogously.
91124
- Adjusting the Go spec as outlined earlier.
92125
- Adjusting gccgo accordingly (type-checker).
93126
- Testing the changes by adding new tests.
94127

95128
No library changes are required. In particular, reflect only allows listing the methods in an interface; it does not expose information about embedding or other details of the interface definition.
96129

97-
Robert Griesemer will do the spec and go/types changes including additional tests, and (probably) also the cmd/compile compiler changes. We aim to have all the changes ready at the start of the [Go 1.14 cycle](https://golang.org/wiki/Go-Release-Cycle), around August 1, 2019.
130+
Robert Griesemer will do the spec and `go/types` changes including additional tests, and (probably) also the `cmd/compile` compiler changes. We aim to have all the changes ready at the start of the [Go 1.14 cycle](https://golang.org/wiki/Go-Release-Cycle), around August 1, 2019.
98131

99132
Separately, Ian Lance Taylor will look into the gccgo changes, which is released according to a different schedule.
100133

101134
As noted in our [“Go 2, here we come!” blog post](https://blog.golang.org/go2-here-we-come), the development cycle will serve as a way to collect experience about these new features and feedback from (very) early adopters.
102135

103136
At the release freeze, November 1, we will revisit this proposed feature and decide whether to include it in Go 1.14.
104137

105-
## Apendix: More examples
138+
**Update**: These changes were implemented around the beginning of August 2019. The [`cmd/compile` compiler changes](https://golang.org/cl/187519) were done by Matthew Dempsky and turned out to be small. The [`go/types` changes](https://golang.org/cl/191257) required a rewrite of the way type checking of interfaces was implemented because the old code was not easily adjustable to the new semantics. That rewrite led to a significant simplification with a code savings of approx. 400 lines. This proposal forced the rewrite, but the proposal was not the reason for the code savings; the rewrite would have been beneficial either way. (Had the rewrite been done before and independently of this proposal, the change required would have been as small as it was for `cmd/compile` since the relevant code in `go/types` and the compiler closely corresponds.)
106139

107-
Starlark is a Python dialect implemented in Go. The base `starlark.Value` interface defines a set of methods that every value must implement, such as `String`, `Type`, and `Truth`, but additional interfaces define optional facets of a type. For example, a value `x` that satisfies the `Iterable` interface may be used in a `for y in x` statement, a value that satisfies `HasFields` may be used in a `x.field` expression, and a value that satisfies `Mapping` may be used in a variadic call `f(**x)`. ([Example from #6977.](https://github.com/golang/go/issues/6977#issuecomment-450440293))
140+
## Apendix: A typical example
141+
142+
Below is [an example](https://golang.org/issues/6977#issuecomment-218985935) by [Hasty Granbery](https://github.com/hasty); this example is representative for a common situation - diamond shaped embedding graphs - where people run into problems with the status quo. A few more examples can be found in [issue 6977](https://golang.org/issue/6977).
143+
144+
In this specific scenario, various different database APIs are defined via interfaces to fully hide the implementation and to simplify testing (it's easy to install a mock implementation in the interface). A typical interface might be:
108145

109146
```Go
110-
package starlark
147+
package user
111148

112-
type Value interface {
113-
String() string
114-
Type() string
115-
Truth() bool
116-
...
149+
type Database interface {
150+
GetAccount(accountID uint64) (model.Account, error)
117151
}
152+
```
118153

119-
type Mapping interface {
120-
Value
121-
Get(key Value) Value
154+
A few other packages may want to be able to fetch accounts under some circumstances, so they require their databases to have all of `user.Database`'s methods:
155+
156+
```Go
157+
package hardware
158+
159+
type Database interface {
160+
user.Database
161+
SaveDevice(accountID uint64, device model.Device) error
122162
}
163+
```
123164

124-
type Iterable interface{
125-
Value
126-
Iterate() Iterator
165+
```Go
166+
package wallet
167+
168+
type Database interface {
169+
user.Database
170+
ReadWallet(accountID uint64) (model.Wallet, error)
127171
}
128172
```
129173

130-
Often a situation arises in which one needs to test whether a value satisfies two interfaces, such as an iterable mapping, but the `Iterable` and `Mapping` interfaces both embed the `Value` interface, and thus contain overlapping sets of methods.
174+
Finally, there is a package that needs both the `hardware` and `wallet` `Database`:
131175

132176
```Go
133-
package mypkg
177+
package shopping
134178

135-
type IterableMapping interface {
136-
starlark.Mapping
137-
starlark.Iterable // error: duplicate method String (et al)
179+
type Database interface {
180+
wallet.Database
181+
device.Database
182+
Buy(accountID uint64, deviceID uint64) error
138183
}
139184
```
140-
With the proposed rule change, `IterableMapping` will become a valid type declaration.
185+
186+
Since both `wallet.Database` and `device.Database` have the `GetAccount` method, `shopping.Database` is invalid with the current spec. If this proposal is accepted, this code will become valid.

0 commit comments

Comments
 (0)