Repository: go
Issue: proposal: spec: allow explicit conversion from function to 1-method interface
Labels: LanguageChange,Proposal,LanguageChangeReview

This is forked from #21670. I'd be fine having this closed as a duplicate, but some people suggested that it was different enough to warrant its own tracking issue. As the title suggests, I would like to be able to convert function values to single-method interfaces, if their respective signatures match.

## Motivation

My motivation is the same as that proposal. I don't want to have to repeatedly create a type of the form

```go
type FooerFunc func(A, B) (C, D)

func (f FooerFunc) Foo(a A, b B) (C, D) {
    return f(a, b)
}
```

to use a closure to implement an interface. #21670 mentions `http.HandlerFunc`, but for me the cases that comes up most often are `io.Reader` and `io.Writer`, which I want to wrap with some trivial behavior. For example, if I want an `io.Writer`, which counts the numbers of bytes written, I can write

```go
type countingWriter struct {
    w io.Writer
    n int64
}

func (w *countingWriter) Write(p []byte) (n int, err error) {
    n, err = w.w.Write(p)
    w.n += int64(n)
    return n, err
}

func main() {
    cw := &countingWriter{w: os.Stdout}
    // write things to cw
    fmt.Println(cw.n, "bytes written")
}
```

However, this makes the code look more heavyweight than it is, by adding a separate type, when really, the only really relevant line is `w.n += int64(n)`. It also *looks* like it breaks encapsulation. `(*countingWriter).n` is used both by methods of the type and to communicate the end-result.

Compare this to

```go
func main() {
    var N int64
    cw := io.Writer(func(p []byte) (n int, err error) {
        n, err = os.Stdout.Write(p)
        N += int64(n)
        return n, err
    })
    // write things to cw
    fmt.Println(N, "bytes written")
}
```

Here, the usage of a closure removes the whole consideration of encapsulation. It's not just less code - it's also code that is more localized and makes the interactions clearer.

We can get part of that currently, by implementing a type like `http.HandlerFunc`. But especially if it's only used once, that smells of over-abstraction and still has the same boiler-plate.

## Proposal

I propose to add a bullet to the [list of allowed conversions of a non-constant value to a type](https://golang.org/ref/spec#Conversions), saying (roughly):

> `T` is an interface type containing exactly one method and `x`'s type is identical to the type of the [method value]( https://golang.org/ref/spec#Method_values) of `T`s single method. The resulting `T` will call `x` to implement its method.

One way to implement this, would be for the compiler to automatically generate a (virtual) defined type, with an arbitrary unexported name in the runtime package.

## Discussion

* The main difference to #21670 is that they propose *assignability*, while I propose *convertibility*. This is specifically to address a common concern about that proposal: `io.Reader` and `io.Writer` (for example) use the same signature for their method and it seems unsafe to have a function implement both, implicitly. This proposal addresses that concern, by requiring an explicit type-conversion - the programmer has to *explicitly* say if the function is intended to be an `io.Reader` or an `io.Writer`.
* The proposal suggests to generate a virtual defined type, so that no changes to tooling, `reflect` (except to allow the conversion itself) or the use of type-assertions is needed.
    * This handles the same as if, say, the `io` package defined an unexported `type readerFunc func(p []byte) (int, error)` implementing `io.Reader` and a `func ReaderFunc(f func(p []byte) (int, error)) Reader { return readerFunc(f) }`.
    * We don't have the mismatch of having methods on non-defined types: It's not `func([]byte) (int, error)` that carries the method, but `runtime.io_reader`.
    * type-assertion/switches to unpack the `func` are not possible, as the type is unexported. That is a plus, because again, the `func` type can't carry methods so it would be strange to use it in a type-assertion.
    * However, it is already possible to transform from `var r io.Reader` to a `func([]byte) (int, error)`, by simply evaluating `r.Read`.
* The proposal suggest to put the generated type in the `runtime` package, because it seems like if two different packages do a `func`->`interface` conversion for the same interface, they should use the same type. I don't think the difference is observable though (the types are not comparable) so we could also put the type in the current package, similar to how closures turn up as `pkgpath.func1` in stack-traces.
* In `var f func(); g := Fooer(f).Foo`, `g` should not be `nil`, even though `f` is. Method expressions are never `nil`, currently, and we should maintain this invariant. This requires that the compiler generates wrapper-methods.

---

Further, more general discussion can be found in #21670 - some arguments applying to that proposal, also apply here. As I said, I'm fine with this being closed as a duplicate, if it's deemed to similar - but I wanted to explicitly file this to increase the chance of moving forward.