Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics type inference when cascaded calls of generic functions

Tags:

go

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...).

like image 978
Jan Avatar asked Dec 21 '25 19:12

Jan


1 Answers

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

like image 122
Eldar Avatar answered Dec 23 '25 16:12

Eldar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!