Generics type interference on "T | []T" to get T?

292 views
Skip to first unread message

TheDiveO

unread,
Aug 12, 2022, 9:07:15 AM8/12/22
to golang-nuts
I would like to provide a generics-based type-safe API that accepts either a scalar or slice of some type T (not a type parameter), that is, the type constraint is (if I'm not mistaken) "T | []T".

While I understand that this constraint would be declared as an interface (simplified example)...

type interface ScalarOrSlice { int | []int }

...the straightforward generics-powered func definition...

func New[E ScalarOrSlice](e E) { ... }

either gives me "int" or "[]int". Is it somehow possible to derive the "int" type at compile time so that I have an, say, "F" that is always the scalar type of E?

Thanks, TheDiveO

Axel Wagner

unread,
Aug 12, 2022, 9:18:05 AM8/12/22
to TheDiveO, golang-nuts
type ScalarOrSlice[E any] interface {
    E | ~[]E
}

func New[E any, T ScalarOrSlice[E]](v T) { … }

Though I haven't tested this.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/0ae02f54-67d6-4070-a11f-36c31a961598n%40googlegroups.com.

TheDiveO

unread,
Aug 12, 2022, 9:49:18 AM8/12/22
to golang-nuts
Gave it a try on playground, but this unfortunately raises the error "term cannot be a type parameter" for the E | ~[]E.

Axel Wagner

unread,
Aug 12, 2022, 10:00:48 AM8/12/22
to TheDiveO, golang-nuts
Indeed. In hindsight, I should've predicted that.
I think you are SOOL then, in general. I'd suggest using ~[]E directly and do the "scalar" case as a 1-element slice then.

For a function, you can write `func F[E any](e ...E)`, which makes it callable with any number of arguments. Or `func F[E any](e E, es ...E)`, which guarantees that there is at least one argument.

It's probably doesn't seem ideal, but it's the only thing that works right now.

Jan

unread,
Sep 3, 2022, 6:38:24 AM9/3/22
to golang-nuts
If you don't mind me extending the topic a bit: I bumped into a similar issue, which I described here:


One of the solutions for the original question would be to use something like:

type MultiDimensionSlices[T any] interface{
  T | []T | [][]T | [][][]T ...
}

But it also fails with `term cannot be a type parameter`.

Is this a temporary limitation (to be fixed in some future Go compiler), or fundamentally it doesn't work for some reason ? 

Also if I remove the "scalar" version (so remove T from the constraint types list), and define:

type MultiDimensionSlices[T any] interface{
  []T | [][]T | [][][]T ...
}
type Number interface { int | float32 }
func rank[S MultiDimensionSlices[T], T Number](value MultiDimensionSlices) int {...}

I cannot simply call:

  rank([][]float32{{1}})  // == 2

because it's not able to infer the type. Instead I'm forced to do:

  rank[[][]float32, float32]([][]float32{{1}})

Any way to have the automatic type inference work in this case ?

Thanks!!

Reply all
Reply to author
Forward
0 new messages