For example, instead of having to have http.HandlerFunc, a function with the signature func(http.ResponsWriter, *http.Request)
would automatically implement http.Handler.
Where I work we have at least 4 internal packages which use some form of interface layering/middleware, similar to http.Handler
and it's middlewares like http.StripPrefix and http.TimeoutHandler. It's a very powerful pattern which I don't think needs justification (I could give one, but it's not really what this proposal is about).
Having now written a few versions of what's essentially the exact same type I have a few different reasons I think this change is warranted:
It reduces confusion for programmers new to go. Anecdotally, pretty much every new go developer I've worked with has tried to pass a function like this and been confused as to why they can't. While I understand that this is a fairly small blip on the go learning-curve, I do think this is an indication that this function-as-interface behavior fits better with devs' mental model of how interfaces work.
It would allow for removing some public-facing types/functions from packages like http (if such a thing is on the table, at the very least it allows for reducing api surface area in new and internal packages). By the same token it reduces code-clutter in cases where something like HandlerFunc
isn't available and you have to wrap your function in the WhateverFunc
type inline.
It would be almost completely backwards compatible. There's no new syntax, and all existing code using types like http.HandlerFunc
would continue to work as they are. The only exception I can think of is if someone is doing something with a switch v.(type) {...}
, where v
_might_ be a function, and the dev expects that the function _won't_ implement the interface as part of that switch behaving correctly (or the equivalent with if-statements or whatever).
These reasons don't justify a language change individually, but in sum I think it becomes worth talking about. Sorry if this has been proposed already, I searched for it but wasn't able to find anything related.
I have wanted this many times. I often find myself creating custom io.Reader
and io.Writer
in unit tests via closured functions. I often have to have this separate type defined so I can convert the function to satisfy the interface:
type ReaderFunc func([]byte) (int, error)
func (f Readerfunc) Read(b []byte) (int, error) { return f(b) }
So a func foo()
implements all of the single-method no arguments interfaces? I don't like the sound of that. The method name matters.
How many single-method interfaces are there with no inputs and outputs?
I don't think the concern of interfaces being accidentally implemented due to unspecific signatures is constrained to just this, that concern could just as well be applied to "all structs with a method with signature func()
", which is something which already exists and is fairly common.
This has come up before and various people have proposed more or less the same thing. See for instance: https://groups.google.com/forum/#!searchin/golang-nuts/single-method$20interface%7B%7D/golang-nuts/AAVCVpWqGCo/cWtHROB2K_8J
I wished for this a few years back for "completeness" sake, when we introduced method values: We can go from a method to a function; so why shouldn't we be able to go from a function to a method (very loosely speaking).
It would be easy to go nuts allocating wrappers if something got passed back and forth between func and interface, as demonstrated in this very useful program below:
package main
type Fooer interface { foo() }
func foo() {}
func R(fooer Fooer) {
R(fooer.foo) // interface to method value then func to interface
}
func main() {
R(foo)
}
I wished for this a few years back for "completeness" sake, when we introduced method values: We can go from a method to a function; so why shouldn't we be able to go from a function to a method (very loosely speaking).
To me it would make more sense to be able to create an interface implementation out of one or more functions like so:
type FooBarer interface {
Foo() string
Bar() string
}
func main() {
foobar := FooBarer{
Foo: func() { return "foo" },
Bar: func() { return "bar" },
}
handler := http.Handler{myHandlerFunction}
}
Then you can truly go full circle:
func fullCircle(fb FooBarer) FooBarer {
foo := fb.Foo
bar := fb.Bar
return FooBarer{
Foo: foo,
Bar: bar,
}
}
I'm not sure I would actually suggest such a change... but it's the one that came to mind when I saw the mention of "completeness", and I think it would happen to satisfy the original proposal's need as well.
@ccbrown, your idea also makes it explicit which method the function applies to, alleviating the concern that @tv42 had.
@ccbrown interestingly that idea appears to have been considered #4146 except that issue was for adding it to reflect only. But if it's good enough for reflect . . .
@dsnet: A single non-exported func()
method is a common way to mark interfaces as impossible for outsiders to implement.
Next up, func([]byte)
, or func([]byte) error
. That means nothing without the method name!
Isn't that why @ccbrown's proposal avoids that problem?
w := io.Writer{Write: f} // Gives an explicit name to f
Note, that you can also lose the name when you extract a function from an interface (we can do this today):
f := w.Write // f is just a function, no particular "name" to it
@dsnet I wasn't responding to ccbrown, I was responding to the original proposal and to your question. Method values are just functions, there's no confusion there; the confusing only arises from accidentally implementing an interface.
@ccbrown This idea (https://github.com/golang/go/issues/21670#issuecomment-325521711) has also been proposed in the past by @Sajmani, albeit only Go Team internally, I believe.
There's something definitely appealing about the symmetry of @ccbrown's construction.
You can kind of do something similar now using
type FooBarer interface {
Foo() string
Bar() string
}
type FooBar struct {
DoFoo func() string
DoBar func() string
}
func (f FooBar) Foo() string { return f.DoFoo() }
func (f FooBar) Bar() string { return f.DoBar() }
It's a very useful construction for satisfying interfaces in terms of closures and the equivalent of the http.HandleFunc
pattern extended to multi-method interfaces.
The major differences from @ccbrown's syntax are that the type name is different and the field names cannot correspond to the method names.
However, it does provide a greater flexibility. Namely, you can define methods in addition to the func fields. In particular, you can define methods that operate on data:
//example borrowed from the thread that introduced sort.Slice
type Sorter struct {
Length int
Swapper func(i, j int)
Lesser func(i, j int) bool
}
func (s Sorter) Len() int { return s.Length }
func (s Sorter) Swap(i, j int) { s.Swapper(i, j) }
func (s Sorter) Less(i, j int) bool { return s.Lesser(i, j) }
Not pictured above, as it doesn't really apply, but you could also add sensible defaults to a method if the corresponding func field is nil.
Having an interface consider a field value that's a func with the correct name and signature to be the same as a method would reduce Sorter
to
type Sorter struct {
Length int
Swap func(i, j int)
Less func(i, j int) bool
}
func (s Sorter) Len() int { return s.Length }
That saves some boilerplate. Having the field names correspond to the method names makes it read a little better. Though you do lose the ability to provide a sensible default implementation if one of the pseudo-methods is nil, but you could still do that by using a field name that doesn't match the interface.
I'm not sure that buys enough to be worth it, though.
It's slightly more verbose at the definition and call sites but for the sake of completion you can also do this:
type Sorter struct { //data struct with the names we want
Len int
Swap func(i, j int)
Less func(i, j int) bool
}
type sorter struct { //actual implementation of the interface
len int
swap func(i, j int)
less func(i, j int)
}
func (s sorter) Len() int { return s.len }
func (s sorter) Swap(i, j int) { s.swap(i, j) }
func (s sorter) Less(i, j int) bool { return s.less(i, j) }
func NewSorter(s Sorter) sort.Interface { //translator
//could also assign default implementations for nil Swap/Less,
//if it were applicable
return sorter{
len: s.Len,
swap: s.Swap,
less: s.Less,
}
}
That let's you have the field names be the method names which is clearer, but there's the extra step to build the implementation.
It would actually look pretty good if you could elide the struct name when calling NewSorter
, though:
return packagename.NewSorter({
Len: n,
Swap: func(i, j int) {
// ...
},
Less: func(i, j bool) {
// ...
},
})
I had proposed the idea of "interface literals", where an interface value
can be specified using struct literal syntax and the interface type name.
The methods are specified using function values:
s := sort.Interface{
Len: func() int {...},
Swap: func(i, j int) {...},
Less: func(i, j int) bool {...},
}
This would work automatically for any interface type.
We decided the convenience of this syntax did not outweigh the added
implementation complexity, in particular
implications for stack traces and other runtime support.
By comparison, allowing function values to satisfy single method interfaces
is a much simpler change.
S
On Tue, Aug 29, 2017 at 11:43 AM jimmyfrasche notifications@github.com
wrote:
There's something definitely appealing about the symmetry of @ccbrown
https://github.com/ccbrown's construction.You can kind of do something similar now using
type FooBarer interface {
Foo() string
Bar() string
}
type FooBar struct {
DoFoo func() string
DoBar func() string
}
func (f FooBar) Foo() string { return f.DoFoo() }
func (f FooBar) Bar() string { return f.DoBar() }It's a very useful construction for satisfying interfaces in terms of
closures and the equivalent of the http.HandleFunc pattern extended to
multi-method interfaces.The major differences from @ccbrown https://github.com/ccbrown's syntax
are that the type name is different and the field names cannot correspond
to the method names.However, it does provide a greater flexibility. Namely, you can define
methods in addition to the func fields. In particular, you can define
methods that operate on data://example borrowed from the thread that introduced sort.Slice
type Sorter struct {
Length int
Swapper func(i, j int)
Lesser func(i, j int) bool
}
func (s Sorter) Len() int { return s.Length }
func (s Sorter) Swap(i, j int) { s.Swapper(i, j) }
func (s Sorter) Less(i, j int) bool { return s.Lesser(i, j) }Not pictured above, as it doesn't really apply, but you could also add
sensible defaults to a method if the corresponding func field is nil.Having an interface consider a field value that's a func with the correct
name and signature to be the same as a method would reduce Sorter totype Sorter struct {
Length int
Swap func(i, j int)
Less func(i, j int) bool
}
func (s Sorter) Len() int { return s.Length }That saves some boilerplate. Having the field names correspond to the
method names makes it read a little better. Though you do lose the ability
to provide a sensible default implementation if one of the pseudo-methods
is nil, but you could still do that by using a field name that doesn't
match the interface.I'm not sure that buys enough to be worth it, though.
It's slightly more verbose at the definition and call sites but for the
sake of completion you can also do this:type Sorter struct { //data struct with the names we want
Len int
Swap func(i, j int)
Less func(i, j int) bool
}type sorter struct { //actual implementation of the interface
len int
swap func(i, j int)
less func(i, j int)
}
func (s sorter) Len() int { return s.len }
func (s sorter) Swap(i, j int) { s.swap(i, j) }
func (s sorter) Less(i, j int) bool { return s.less(i, j) }func NewSorter(s Sorter) sort.Interface { //translator
//could also assign default implementations for nil Swap/Less,
//if it were applicable
return sorter{
len: s.Len,
swap: s.Swap,
less: s.Less,
}
}That let's you have the field names be the method names which is clearer,
but there's the extra step to build the implementation.It would actually look pretty good if you could elide the struct name when
calling NewSorter, though:return packagename.NewSorter({
Len: n,
Swap: func(i, j int) {
// ...
},
Less: func(i, j bool) {
// ...
},
})—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/golang/go/issues/21670#issuecomment-325704780, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AJSK3VLx3dek5ZEx-j6PHE7SnYZVj_waks5sdDGmgaJpZM4PE-TN
.
@Sajmani wouldn't the same implementation complexity be required for #16522 / #4146 ? #16522 is blocking useful features (always discussed in #4146 but in the end often turning out to be better implemented via #16522 )
Possibly yes. I haven't considered those issues in the context of my
proposal.
On Tue, Aug 29, 2017 at 2:03 PM jimmyfrasche notifications@github.com
wrote:
@Sajmani https://github.com/sajmani wouldn't the same implementation
complexity be required for #16522
https://github.com/golang/go/issues/16522 / #4146
https://github.com/golang/go/issues/4146 ? #16522
https://github.com/golang/go/issues/16522 is blocking useful features
(always discussed in #4146 https://github.com/golang/go/issues/4146 but
in the end often turning out to be better implemented via #16522
https://github.com/golang/go/issues/16522 )—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/golang/go/issues/21670#issuecomment-325745290, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AJSK3bh6BM-VdqHN2Zhbjjo9C25zI1IPks5sdFJGgaJpZM4PE-TN
.
Although it goes against Go conventions, I would question, if an interface is needed in the first place, if it contains just a single method.
Simply accepting a callback works for both cases, is less code to write and more flexible:
func handler1(http.ResponsWriter, *http.Request) {}
type handler2 struct {}
func (h *handler2) HandleA(http.ResponsWriter, *http.Request) {}
func (h *handler2) HandleB(http.ResponsWriter, *http.Request) {}
func WantsHandler( handler func(http.ResponsWriter, *http.Request) {}
func main() {
WantsHandler(handler1) // works
h2 := handler2{}
WantsHandler(h2.HandleA) // also works
WantsHandler(h2.HandleB) // this too
}
Another benefit: The user is not forced to include a package, if all she needs is the reference to the interface name.
Agreed, it looks a bit ugly... ;-)
@metakeule that's a good point. Method values and type aliases†overlap the use-case for single method interfaces somewhat, but what you lose with that approach is the ability to feature test other methods with type asserts (like being able to ask, is this io.Reader
also an io.ReaderFrom
?).
Being able to more easily go from the one to the other would be useful, but I'd rather have the more explicit and general "interface literal" syntax. But it's not really that great a pain to make the explicit wrappers with a func/struct now.
†so you can write
//Handler is [documented here]
type Handler = func(ResponseWriter, *Request)
instead of repeating the signature/docs everywhere or having to worry about casts from defined types
@jimmyfrasche
Regarding the "feature" to ask for other interface implementations (aka "is this io.Reader also an io.ReaderFrom"):
I think the problem this is supposed to solve is "optional features" of the receiving function. It is used in the standard library (e.g. http.ResponseWriter -> http.Flusher etc) and I also used it.
But for some time now I consider this a misfeature for the following reasons:
It is a hidden feature. While the function signature says it expects an io.Writer it is really expecting an io.Writer and/or an io.WriteCloser. That behavior is only visible within the source code and maybe the documentation. However it the author decides to add some optional behavior in an update, it may break the user without a compiler error (since the signature does not change) and creates errors that are hard to debug (e.g. io.WriteCloser being closed too early). Also the user struct implementing a new interface might break suddenly (the authors update being done long before without issues) and would make new type variations neccessary in order to prevent triggering the unwanted option.
It makes it hard to wrap the interface. E.g. it looks as it would be easy to wrap an http.ResponseWriter with another type that implements the same interface. Until you notice that the http server is checking for other interfaces as well (like http.Flusher etc). Now you have to wrap each of the other interfaces (that are not easily visible unless you read the full source code).
There are better ways to offer optional features that don't have the disadvantages: Allow option functions to be passed to the constructor that set the appropriate not exported options in the struct. Having them as variadic argument allows for easy expansion (simply add a new top level option function overwriting a compatible defaults) without breaking anything and with clear visibility. It also decouples the options on the user side (makes the user code more robust for refactoring / smaller surface of coupling between user and provider).
I'm not keen on this proposal. I think it will be confusing.
var f = func([]byte) (int, error) {return 0, io.EOF}
var r io.Reader = f
What happens when we reflect on the value of r? What should reflect.ValueOf(r).NumMethod() return?
I see two possibilities:
Both cases would be unexpected IMHO, because we expect the value inside an interface to be the value we started with. It would be unexpected to inspect the methods on an io.Reader value and find that Read isn't among them; conversely it would be unexpected for a anonymous function value to have a method (what would the method name be?).
Another possibility might be to actually change the underlying type when converting to a one-method interface. Then reflect.TypeOf(f) != reflect.TypeOf(io.Reader(f)) (the latter type would have a Read method), but that seems pretty unexpected too, because interface{}(io.Reader(r)) is currently the same as interface{}(r), and this would make it different, confusingly so in my view.
@Sajmani's suggestion of io.Reader{Read: f} (io.Reader{f} for short?) seems like it would work better to me - at least there's a clear constructor there. But on balance, I think that it's probably not worth it.
I think if we were to allow function values to satisfy interfaces, I'd
prefer an explicit conversion: io.Reader(f). This provides two benefits:
(1) it helps the user avoid errors, E.g., reversing the args to io.Copy;
(2) it requires the caller be able to name the interface, avoiding
confusion when the interface name is unexported and the caller is in
another package.
On Wed, Aug 30, 2017 at 5:04 PM Roger Peppe notifications@github.com
wrote:
I'm not keen on this proposal. I think it will be confusing.
var f = func([]byte) (int, error) {return 0, io.EOF}
var r io.Reader = fWhat happens when we reflect on the value of r? What should
reflect.ValueOf(r).NumMethod() return?
I see two possibilities:
- it could return 0, because the underlying value has no methods.
- it could return 1, because any value satisfying io.Reader must have
at least one method.Both cases would be unexpected IMHO, because we expect the value inside an
interface to be the value we started with. It would be unexpected to
inspect the methods on an io.Reader value and find that Read isn't among
them; conversely it would be unexpected for a anonymous function value to
have a method (what would the method name be?).Another possibility might be to actually change the underlying type when
converting to a one-method interface. Then reflect.TypeOf(f) !=
reflect.TypeOf(io.Reader(f)) (the latter type would have a Read method),
but that seems pretty unexpected too, because interface{}(io.Reader(r)) is
currently the same as interface{}(r), and this would make it different,
confusingly so in my view.@Sajmani https://github.com/sajmani's suggestion of io.Reader{Read: f}
(io.Reader{f} for short?) seems like it would work better to me - at least
there's a clear constructor there. But on balance, I think that it's
probably not worth it.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/golang/go/issues/21670#issuecomment-326118586, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AJSK3TNAUrligtYfJ2_7rvEHGLD-WGsYks5sdc54gaJpZM4PE-TN
.
I think if we were to allow function values to satisfy interfaces, I'd
prefer an explicit conversion: io.Reader(f).
I'm not sure that this is much better. Explicit conversion to other interface
types is still possible, and has quite different semantics.
f := func() {}
x := interface{}(f)
y := interface{F()}(f)
if reflect.TypeOf(x) != reflect.TypeOf(y) {
panic("unexpected")
}
I believe that as proposed, the above code would need to panic, but that
seems wrong to me.
I wouldn't mind interface{F()}{f} as a syntax (by analogy with SliceType{x} vs SliceType(x)),
especially as it could later be expanded to interface{F(); G()}{F: f, G: g} at some later
point to make it easier to implement multiple method interfaces too.
We'd still need to decide what the underlying type's Kind and Name etc would look like.
I believe I have an interesting use case for interface literals as proposed by @ccbrown and @Sajmani .
I've been considering an ioctx
package which would provide equivalent interfaces to io.Reader
et al., only with an added Context
parameter. This package would also provide some simple wrappers for adapting between methods which take or do not take a Context.
package ctxio
type Reader interface {
Read(ctx context.Context, p []byte) (n int, err error)
}
// ReaderWithContext curries a ctxio.Reader with a Context, producing an io.Reader.
func ReaderWithContext(ctx context.Context, r Reader) io.Reader { return ctxReader{ctx, r} }
type ctxReader struct { ctx context.Context, r Reader }
func (r ctxReader) Read(p []byte) (n int, err error) { return r.Read(r.ctx, p) }
But there's a problem with the ReaderWithContext
function: Functions like io.Copy use type assertion to probe for methods like WriteTo
. ReaderWithContext
strips these methods from the returned Reader
.
ReaderWithContext
could use type assertions to detect the methods on the passed in r
and return an appropriate wrapper type, but there's an obvious combinatorial explosion here--we'll need to define four different return types to support Reader
, ReaderAt
, and WriterTo
...and what if we want to preserve the writer functions as well? This is a problem.
What if we could instead write this?
type io interface {
Read(p []byte) (n int, err error)
ReadAt(p []byte, off int64) (n int, err error)
WriteTo(w io.Writer) (n int64, err error)
}
func ReaderWithContext(ctx context.Context, r Reader) io.Reader {
// ctxReader has Read and WriteTo methods.
ctxr := ctxReader{ctx, r}
var writeToFn func(w io.Writer) (n int64, err error)
if rr, ok := r.(io.WriterTo); ok {
writeToFn = ctxr.WriteTo
}
wrap := io{
Read: ctxr.Read,
WriteTo: writeToFn,
}
return wrap
}
If r
has a WriteTo
method, we return a value of type io.Reader
that may be converted with a type assertion into an io.WriterTo
.
If r
does not have a WriteTo
method, then things get tricky. We construct a value of type io
, which does have a WriteTo
; attempting to call WriteTo
on this value will panic, however, since we initialized it with a nil function pointer. Converting this value to an io.Reader
on return produces a value which cannot be converted into an io.WriterTo
again.
This handling of nil methods is quite subtle, enough so that I'm extremely hesitant to suggest that it's something that we actually should do even if we do add interface literals. On the other hand, I haven't been able to come up with any good solutions to the problem of wrapping types which may or may not contain certain methods that aren't equally subtle.
What if you were required to declare all methods of an interface literal? Sure someone could do:
wrap := io{
Read: ctxr.Read,
ReadAt: nil,
WriteTo: writeToFn,
}
But the jokes on them... they explicitly trying to shoot themselves in the foot.
The way I was looking at it was that:
x = interfaceType {m1:f1, m2:f2, ...}
would be syntax sugar for:
type _scratch struct {
f1_ func typeof(interfaceType.m1)
...
}
func (s _scratch) f1(args) ret {
return s.f1_(arg)
}
etc
x = interfaceType (_scratch{f1_: f1, ...})
I think it would be very odd if a non-nil something of a given interface type turned out not to actually implement that type.
That is, for all non-nil variables v of some interface type I, we should be able to do (interface{}(v)).(I) without panicking. Invariants like this are important for automatic code transformation and vetting tools.
That said, I do appreciate the issue encountered by @neild. I suspect the answer to that is more of a cultural one - define your interfaces in such a way that even if the interface is implemented, the it might not actually work. For example WriteTo could be defined such that it could return a "not implemented" error to cause io.Copy to fall back to the naive Read-based behaviour.
Then we can define an interface type with all the known methods on it and implement suitable behaviour when there's no function available.
Embedding unknown methods is another story.
@rogpeppe I think the known vs unknown methods wrapping is the same story—or at least similar enough that the same solution would work.
In both cases, you want to create a value whose method set is determined by the dynamic type of another value.
In https://github.com/golang/go/issues/4146#issuecomment-318200547 this was discussed (starting at linked comment) in terms of the classic problem of hiding methods such as Temporary on a wrapped error.
The eventual solution was fairly general. A function Wrap(value, with interface{}) interface{}
that conceptually returns
struct {
underlyingTypeOfValue
underlyingTypeOfWith
}
except that, unlike ordinary embedding, ambiguous methods always dispatch to underlyingTypeOfWith
.
With that you could run Wrap(arbitraryErr, fmt.Errorf("mypkg: %s", arbitraryErr)).(error)
and not lose any of the methods on arbitraryErr
while replacing the Error
method to provide the appropriate context.
In @neild's case this could be used like
func ReaderWithContext(ctx context.Context, r Reader) io.Reader {
// ctxReader just has Read method.
var ctxr interface{} = ctxReader{ctx, r}
if rr, ok := r.(io.WriterTo); ok {
//ctxWriteTo just has WriteTo method.
withWriteTo := ctxWriteTo{ctx, rr}
ctxr = Wrap(ctxr, withWriteTo)
}
//and so on for all the other various possibilities,
//only adding methods to ctxr when necessary
return ctxr.(io.Reader)
}
If r
has a WriteTo
method, so does the returned value. If it does not, neither does the returned value.
The implicit use of reflect is unsatisfying but it manages to sidestep the combinatorial explosion of types by generating only the appropriate combination at runtime.
Unfortunately Wrap
is blocked on #16522
@rogpeppe I think the known vs unknown methods wrapping is the same story—or at least similar enough that the same solution would work.
It's true that the suggested solution for promoting unknown methods would work for known methods too, but I don't think that this proposal could be used to promote unknown methods (hence "a different story" IMHO). FWIW I suspect that the proposed solutions for promoting unknown methods will turn out to be more heavyweight than desired for the routine cases where they might be used.
@rogpeppe
FWIW I suspect that the proposed solutions for promoting unknown methods will turn out to be more heavyweight than desired for the routine cases where they might be used.
I don't think this is necessarily true, though it certainly would not be free.
One way to implement it would be to use the same essential construct as you pointed out interface literals would likely use in https://github.com/golang/go/issues/21670#issuecomment-329497953
Say you had an interface ABer
with methods A
and B
and two values a
with only the method A
and b
with only the method B
.
With an interface literal you could construct a "combined" value, v
:
v := ABer{
A: a.A,
B: b.B,
}
Calling v.A
would indirect though the interface and the function pointer in the _scratch
struct.
A means of promoting unknown methods could work mostly the same. The only differences are it would need to compute and then create the method set and the _scratch
struct at runtime. That only needs to happen when the value is created, though.
Going back to the ABer
example, v := Wrap(a, b).(ABer)
would create operationally the same value as constructed above. How it creates v
would be slower, but using the value would have the same double-indirect performance characteristics as when it was created by the interface literal.
For Wrap
to be implemented this way, it would need some inside information, so it would probably have to be implemented in reflect
(and would require reflect to be able to construct method sets at runtime per #16522).
Perhaps http.Handler would be better described as a function instead of an interface?
func FileServer(root FileSystem) Handler
returns a *fileHandler that has an interface field. A closure could be returned instead: https://play.golang.org/p/1TErgasMfc6
The original proposal is for essentially the reverse of a method value. Perhaps it should be called a value method. The idea is to permit a conversion from a function value to an interface type with a single method, where the signature of the method is the same as the signature of the function. The conversion would involve creating an unnamed type with a single method, where the method would be implemented by calling the original function value with the arguments. This conversion could be implicit, or could require an explicit conversion.
Since this is the reverse of a method value, it is worth considering the reverse of a method expression (an expression method). That would start with a function with at least one argument. It would convert to a value whose type is the type of the first argument, with an attached method whose type is the remaining arguments of the function. This could only be used in a conversion to a single method interface, as above, where the method has the appropriate type.
If anybody wants to seriously push any of the other ideas discussed above, please open a separate proposal. They might be good ideas but they are more complex than this proposal and should be considered separately.
Since this is the reverse of a method value, it is worth considering the reverse of a method expression (an expression method). That would start with a function with at least one argument. It would convert to a value whose type is the type of the first argument, with an attached method whose type is the remaining arguments of the function.
A method expression moves the receiver parameter to the first argument position.
An “expression method” would move the first argument to the receiver position.
It's related to #21401, but not attached to the receiver type.
It's related to #23185, but presumably requires an explicit type conversion rather than changing assignability of existing types
The syntax for a method value is mv := v.Method
, and the syntax for a method expression is me := T.Method
.
Just sketching some composite literals, here's one with good symmetry:
v := T{Method: mv}
type T = {Method: me}
In the first declaration v
is the function mv
viewed as an interface of type T
.
In the second declaration, T
is an alias for “the type of the first argument to me
augmented with Method
”. Values assignable to the first argument to me
can be converted to T
.
In the more general form,
type T = {
Method1: me1,
Method2: me2,
}
T is an alias for some type that is assignable to the first arguments of its methods, augmented by those methods. If the first-argument types are interfaces, the underlying type of T
is an interface containing a union of their methods; if they are concrete types, the underlying type of T
is a type assignable to all of those concrete types. (If any of them are defined types rather than type literals or interfaces... I'd have to think about that some more.)
There is an interesting asymmetry between method expressions and “expression methods”.
If we add a method to an existing value based on an arbitrary expression, the effective type definition for that value is computed at run-time and depends on a value. That's something we support today via reflect
, but not in the language proper.
On the one hand, adding “expression methods” would significantly complicate the type system. (For example, can these new method-augmented types themselves be used as function parameter or return types?) On the other hand, it would close a gap between reflect
and the compile-time language.
(In other words: I think “method expressions” would be plausible but they would merit a whole separate proposal.)
Can this be added to #33892 for review?
We reviewed it a while back (before we started keeping minutes) and marked it NeedsDecision
. At some point we will go back through the NeedsDecision
issues and try to actually make a decision.
Most helpful comment
To me it would make more sense to be able to create an interface implementation out of one or more functions like so:
Then you can truly go full circle:
I'm not sure I would actually suggest such a change... but it's the one that came to mind when I saw the mention of "completeness", and I think it would happen to satisfy the original proposal's need as well.