In golang 1.18 I would like to define a function like follows:
func Pipe[A, T1, T2 any](left func(A) T1, right func(T1) T2) func(A) T2 {
return func(a A) T2 {
return right(left(a))
}
}
e.g. the output of the left
function should be the input of the right
function, repesented as generics.
I am noticing that this fails to work as expected for the following example:
func OpenFile(name string) *os.File {
...
}
func ReadAll(rdr io.Reader) []byte {
...
}
var OpenRead = Pipe(OpenFile, ReadAll)
This fails to compile because the compiler considers T1
to be *os.File
and although it's compatible to io.Reader
it's not identical.
If I were to invoke the chain without templates like so:
var result = ReadAll(OpenFile("test"))
then the compiler identifies the compatible types.
Questions:
- is there a way in golang 1.18 generics to fix the signature of
Pipe
to allow for the desired behaviour? - is the golang 1.18 behaviour by design or is this a bug?
CodePudding user response:
- No.
- No, not a bug. See the FAQ.
Given that Go does not support covariant result types you'd need to convert the result of the left
to the type accepted by the right
. However there's currently no way to express convertibility using type parameters.
If you want you could adapt your code based on the example in that link and you'll get something like this, but keep in mind that it's not "compile-time type-safe".
func Pipe[A, T1, T2, T3 any](left func(A) T1, right func(T2) T3) func(A) T3 {
return func(a A) T3 {
return right((interface{})(left(a)).(T2))
}
}