I want to discuss a proposal for adding the following reflection feature to Go.
Given any Go interface type, instantiate a Go value that implements this interface, by passing each method call and its arguments to a user-provided handler.
Some use cases for this feature are:
Debugging and instrumentation
Given any interface A that I use (possibly 3rd party), I want to time each method call and log the calls and the times.
RPC APIs
I could write code that given an interface A, and a struct S that implements A, it would implement HTTP REST client and server components that implement interface A over HTTP, using JSON to marshal method arguments.
The server component would use A and S.
The client component would use only A (the interface), and automatically create a struct that implements it. The server part of this can already be done. But the client cannot be done presently, because there is no way to auto-generate something that implements an interface. I’d have to incorporate a code generator that generates a client stub, which will need to be compiled in the client.
I have done this, but it introduces an extra code generation step in the build process.
This exists in Java
Java does this as follows:
package java.lang.reflect;
interface Invocation Handler {
Object invoke(Object proxy,Method method,Object[] args)throws Throwable
}
class proxy {
static newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
}
newProxyInstance() returns an object that implements all the specified interfaces (not just one).
When a method of this object is called, it calls the provided InvocationHandler h.
You can ignore ClassLoader. Go does not load classes dynamically so it does not need a class loader.
Relevant Go features
package reflect
Value.Call(in []Value) []Value
func StructOf(fields []StructField) Type
The Go reflect package has a feature that creates a struct type given any []StructField created at runtime.
Java does not have this.
Proposed Go implementation
I’m not sure exactly what methods such a facility should have, but given the relevant Go features above and the Java implementation, I suggest something like this:
package reflect
ImplementationOf(h func(m Method, in []Value) []Value, u Type) any
Example
Here’s a complete example of how to implement an interface wrapper that prints the execution time of each method call, using the above proposed feature. It includes an implementation of the proposal that is hardcoded to work for this example only.
https://play.golang.com/p/7trKj6TKHsB
package main
import (
"fmt"
"reflect"
"time"
)
type A interface {
Add(n1, n2 int) int
}
type S struct {
}
func (s *S) Add(n1, n2 int) int {
return n1 + n2
}
type TimingWrapper struct {
Type reflect.Type
receiver reflect.Value
}
func NewCallWrapper(receiver any) *TimingWrapper {
var c TimingWrapper
c.receiver = reflect.ValueOf(receiver)
c.Type = reflect.TypeOf(receiver)
return &c
}
func (c *TimingWrapper) Handler(m reflect.Method, in []reflect.Value) []reflect.Value {
// m is the interface method
// we need the method for the receiver type
receiverMethod, ok := c.Type.MethodByName(m.Name)
if !ok {
panic(fmt.Sprintf("no such method: %s", m.Name))
}
// The interface method does not have a receiver input
// We need to prepend one:
rin := make([]reflect.Value, 1, (1 + len(in)))
rin[0] = c.receiver
rin = append(rin, in...)
start := time.Now()
out := receiverMethod.Func.Call(rin)
duration := time.Now().Sub(start)
fmt.Printf("%s: %v\n", m.Name, duration)
return out
}
func main() {
var a A = &S{}
handler := NewCallWrapper(a).Handler
var p *A
v := /*reflect.*/ ImplementationOf(handler, reflect.TypeOf(p).Elem())
a = v.(A)
d := a.Add(1, 2)
fmt.Printf("result: %d\n", d)
}
// reflect.ImplementationOf does not exist,
// The code below is an implementation of it that is hardcoded to work for interface A.
type aImpl struct {
Type reflect.Type
Handler func(m reflect.Method, in []reflect.Value) []reflect.Value
}
func (a *aImpl) Add(n1, n2 int) int {
m, _ := a.Type.MethodByName("Add")
out := a.Handler(m, []reflect.Value{reflect.ValueOf(n1), reflect.ValueOf(n2)})
return int(out[0].Int())
}
func ImplementationOf(h func(m reflect.Method, in []reflect.Value) []reflect.Value, u reflect.Type) any {
var p *A
a := aImpl{Handler: h, Type: reflect.TypeOf(p).Elem()}
return &a
}