Home > Back-end >  Generics type inference when cascaded calls of generic functions
Generics type inference when cascaded calls of generic functions

Time:09-03

While building a (my first) generics heavy library, and I'm bumping on some apparent limitations on the generics type checking implementation -- more likely my lack of knowledge.

Any ideas how to get something like below to work ?

package main

import (
    "fmt"
    "reflect"
)

type Number interface {
    int | float32
}

type MultiDimensionSlice interface {
    int | float32 | []int | []float32 | [][]int | [][]float32
}

func dimensions[S MultiDimensionSlice](s S) int {
    dims := 0
    t := reflect.TypeOf(s)
    for t.Kind() == reflect.Slice {
        dims  = 1
        t = t.Elem()
    }
    return dims
}

func indirection[T Number](v T) int {
    var slice2D [][]T
    return dimensions(slice2D)
}

func main() {
    x := [][]float32{{1}, {2}, {3}}
    fmt.Printf("x=%v, dims=%d\n", x, dimensions(x))
    fmt.Printf("indirection should return 2, got %d\n", indirection(0))
}

This fails to compile with the message [][]T does not implement MultiDimensionSlice ([][]T missing in int | float32 | []int | []float32 | [][]int | [][]float32)

But within the function indirection() all the allowed values of T will have an implementation in dimensions().

Any help or pointers would be mostly appreciated!

(Playground link)

ps.: My problem is a bit more complex than that, but the issue is that one generic function (indirection() in this example) is not able to invoke the other (dimensions() here) because (apparently) Go compiler is not able to resolve the type parameter constraints (the information is there in compile time...).

CodePudding user response:

As mentioned in the comments go has some restrictions in its generics perspective. You can achieve what you require with a workaround.

First, you need to change the interface you define. (Make it generic too)

type Number interface {
    int | float32
}

type MultiDimensionSlice[T Number] interface {
    Number | []T | [][]T
}

Then we need to change dimension methods type arguments. It would be much cleaner if go would let us define dimensions method like this

func  dimensions[S Number](s MultiDimensionSlice[S]) int {

But all we can do is :

func dimensions[S Number, K MultiDimensionSlice[S]](s K) int {
    dims := 0
    t := reflect.TypeOf(s)
    for t.Kind() == reflect.Slice {
        dims  = 1
        t = t.Elem()
    }
    return dims
}

And then we need to change how we call dimensions method. We need to provide an extra type argument in order to go can infer type argument S

func indirection[T Number](v T) int {
    var slice2D [][]T
    return dimensions[T](slice2D)
}

func main() {
    x := [][]float32{{1}, {2}, {3}}
    fmt.Printf("x=%v, dims=%d\n", x, dimensions[float32](x))
    fmt.Printf("indirection should return 2, got %d\n", indirection(0))
}

Playground

  •  Tags:  
  • go
  • Related