Some notes about Golang Generics and Abstraction based on my working experience.

WARNING

Under construction.

Use ‘generic interface’ when you need to self-reference the interface in the methods of your interface

TIP

For Example:

Before we have generics:

type MyInterface interface {
  DoSomething(MyInterface)
}
 
type MyConcreteType struct{}
 
func (m MyConcreteType) DoSomething(m2 MyInterface) {} // Here we have to use 'MyInterface' instead of 'MyConcreteType' !

Note that m2 MyInterface argument. We can’t use ‘concrete’ type here. The consequence is that we will have to do a dynamic type assertion to get the concrete type for every implementation.

With generics:

type MyInterface[T any] interface {
  DoSomething(T)
}
 
type MyConcreteType struct{}
func (m MyConcreteType) DoSomething(m2 MyConcreteType) {} // Here we can use 'MyConcreteType' directly !

NOTE

注意这个东西,it is used as the type constraint in the type parameter in the code of the generic interface user.

但是你肯定有疑问:DoSomething 如果不是 receiver 和 argument 不是同一个 type,不是还是有这个问题吗? 确实如此:

type GenericInterface[T any] interface {
	DoSomething(T)
}
type ConcreteType struct{}
func (m ConcreteType) DoSomething(m2 ConcreteType) {}
 
type AnotherType struct{}
func (a AnotherType) DoSomething(m2 ConcreteType) {}
 
var (
	_ GenericInterface[ConcreteType] = ConcreteType{}
  // The exact example of receiver type != argument type.
	_ GenericInterface[ConcreteType] = AnotherType{}
)

但是可惜,这方面只能是这样了。实际上只能在 user end 来让 compiler 知道 “对于这个 interface,receiver type 和 argument type 应该一样“

func FuncUserOfGenericInterface[T MyInterface[T]](arg T) {
  arg.DoSomething(arg) // This is OK.
}
 
type TypeUserOfGenericInterface[T MyInterface[T]] struct {
  value T
}
 
func (t TypeUserOfGenericInterface[T]) method() {
  t.value.DoSomething(t.value) // This is OK.
}

In the example above:

  • The T MyInterface[T] enforced the reciver type and the argument type must be the same.

Functor and single-method interface conversion

这两个虽然有很多相似的地方,而且如果 functor 可以直接当作 implementation for single-method interace,可能很多代码可以简化。

但是从 type system 的角度看,就是不一样的东西。两者之间也没有办法互相 cast。

Here is the not-planned proposal for Golang 2: https://github.com/golang/go/issues/21670

但是现实中我们可以看到很多地方用了 http.HandlerFunc 这个 functor type 来当作 http.Handler 的 implementation. 为什么呢?

Because: code

// Yes, this is a functor type.
type HandlerFunc func(ResponseWriter, *Request)
 
// But it also has a specific method to implement the handler interface 
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

So, if you do want to let a ‘functor’ type to implement a single-method interface, you still need to explicitly implement the method for the interface:

type yourFunctorType func(xxx) yyy
 
// This is needed to implement interface `YourInterface` which has a method: `YourInterfaceMethod(xxx) yyy`
func (f yourFunctorType) YourInterfaceMethod(xxx) yyy {
}