invalid operation: cannot index T when modeling slices of arbitrary dimensions - go

I am try to do a matrix subtraction with unknow size of matrix. here is the code:
type ArrayLike interface {
[]interface{} | [][]interface{} | [][][]interface{} | [][][][]interface{}
}
func subMatrix[T ArrayLike](A, B T, shape []int) interface{} {
dim := shape[0]
if len(shape) == 1 {
retObj := make([]interface{}, dim)
for i := 0; i < dim; i++ {
Av := A[i].(float64)
Bv := B[i].(float64)
retObj[i] = Av - Bv
}
return retObj
} else {
retObj := make([]interface{}, dim)
for i := 0; i < dim; i++ {
retObj[i] = subMatrix(Av[i], Bv[i], shape[1:])
}
return retObj
}
}
It complains about
invalid operation: cannot index A (variable of type T constrained by []interface{}|[][]interface{}|[][][]interface{}|[][][][]interface{})compilerNonIndexableOperand
Does anyone know how to do this job?

You can't do this, not with generics and not with interface{}/any alone. The main issue is that you can't statically model slices with arbitrary dimensions — i.e. arbitrary types. Let's go in order:
The error message you got is because you can't index a type parameter with a union constraint like that. Specs, Index expressions:
For a of type parameter type P:
[...]
The element types of all types in P's type set must be
identical. [...]
The element types of ArrayLike's type set are not identical. The element type of []interface{} is interface{}, the one of [][]interface{} is []interface{} and so on.
There is a placebo solution to the indexing error, outlined here, basically it involves changing the type of the arguments A and B to a slice []T. Then you can index A[i] and B[i].
However this isn't enough. At this point, there's no way to pass correct arguments to the recursive call. The type of the expression A[i] is now T, but subMatrix wants []T.
Even dropping type parameters and declaring the args A and B as any doesn't work, because on the recursive call you still want to index them. In order to index, you have to assert any to something that's indexable, but what would that be? At each recursion the types would have one less dimension, so a statically typed assertion would eventually panic.
The only way to solve this is (probably?) with reflection, but honestly I don't see a practical utility to do that.
You can, and should, write a generic function for each matrix dimension:
import "golang.org/x/exp/constraints"
type Number interface {
constraints.Integer | constraints.Float
}
func Sub2DMatrix[T Number](a, b [][]T) {
for i := range a {
for j := range a[i] {
a[i][j] -= b[i][j]
}
}
}

Related

Go dependent type contraints [duplicate]

I am trying to write the following function:
func Fill[X any](slice []*X){
for i := range slice {
slice[i] = new(X)
}
}
xs := make([]*int, 10) // fill with nils
Fill(xs) // now fill with new(int)
That works fine but… if I want to use a slice of interfaces and provide a concrete type?
func Fill[X, Y any](slice []X){
for i := range slice {
slice[i] = new(Y) // not work!
}
}
xs := make([]sync.Locker, 10) // fill with nils
Fill[sync.Locker,sync.Mutex](xs) // ouch
I try some combinations without success, is there a way or go1.18 does not support such relations?
When you constrain both X and Y to any, you lose all interface-implementor relationship. The only thing that is known at compile time is that X and Y are different types, and you can't assign one to the another within the function body.
A way to make it compile is to use an explicit assertion:
func Fill[X, Y any](slice []X) {
for i := range slice {
slice[i] = any(*new(Y)).(X)
}
}
But this panics if Y doesn't really implement X, as in your case, since it is *sync.Mutex (pointer type) that implements sync.Locker.
Moreover, when Y is instantiated with a pointer type, you lose information about the base type, and therefore the zero value, including *new(Y) would be nil, so you don't really have a baseline improvement over make (just typed nils vs. nil interfaces).
What you would like to do is to constrain Y to X, like Fill[X any, Y X](slice []X) but this is not possible because 1) a type parameter can't be used as a constraint; and/or 2) a constraint can't embed a type parameter directly. It also initializes nils as the above.
A better solution is to use a constructor function instead of a second type parameter:
func main() {
xs := make([]sync.Locker, 10)
Fill(xs, func() sync.Locker { return &sync.Mutex{} })
}
func Fill[X any](slice []X, f func() X) {
for i := range slice {
slice[i] = f()
}
}

Golang generics with interface and implementation at same time

I am trying to write the following function:
func Fill[X any](slice []*X){
for i := range slice {
slice[i] = new(X)
}
}
xs := make([]*int, 10) // fill with nils
Fill(xs) // now fill with new(int)
That works fine but… if I want to use a slice of interfaces and provide a concrete type?
func Fill[X, Y any](slice []X){
for i := range slice {
slice[i] = new(Y) // not work!
}
}
xs := make([]sync.Locker, 10) // fill with nils
Fill[sync.Locker,sync.Mutex](xs) // ouch
I try some combinations without success, is there a way or go1.18 does not support such relations?
When you constrain both X and Y to any, you lose all interface-implementor relationship. The only thing that is known at compile time is that X and Y are different types, and you can't assign one to the another within the function body.
A way to make it compile is to use an explicit assertion:
func Fill[X, Y any](slice []X) {
for i := range slice {
slice[i] = any(*new(Y)).(X)
}
}
But this panics if Y doesn't really implement X, as in your case, since it is *sync.Mutex (pointer type) that implements sync.Locker.
Moreover, when Y is instantiated with a pointer type, you lose information about the base type, and therefore the zero value, including *new(Y) would be nil, so you don't really have a baseline improvement over make (just typed nils vs. nil interfaces).
What you would like to do is to constrain Y to X, like Fill[X any, Y X](slice []X) but this is not possible because 1) a type parameter can't be used as a constraint; and/or 2) a constraint can't embed a type parameter directly. It also initializes nils as the above.
A better solution is to use a constructor function instead of a second type parameter:
func main() {
xs := make([]sync.Locker, 10)
Fill(xs, func() sync.Locker { return &sync.Mutex{} })
}
func Fill[X any](slice []X, f func() X) {
for i := range slice {
slice[i] = f()
}
}

What's the reason for having methods outside the definition of the struct?

Why do we have the methods declared outside the type definition of the struct? E.g.:
type antenna struct {
name string
length float32
girth float32
bloodtype string
}
func (p *antenna) extend() {
p.length += 10
}
It seems to me that the method could be part of the struct? (Let's ignore for now that structs are supposed to be value types)
type antenna struct {
name string
length float32
girth float32
bloodtype string
func extend() {
length += 10
}
}
This would be more similar to traditional OOP. I didn't find any good explanations of why it is done the way it is besides "structs are value-types and classes are reference-types". I know the difference, but it's not a satisfactory answer to me. In any way the method has to be called like this:
var x = antenna()
x.extend()
So what's the point of separating the the struct and methods? Having them visually grouped together in the code - as in typical OOP languages - seems useful to me?
TLR: Code reuse, and Consistency.
1 - This enables to reuse methods:
This is the key design principle of the interface type in Go - let me make it more clear with an example: Consider you need to sort an slice of int (try it here):
a := []int{1, 3, 2, 5, 4}
sort.Ints(a) // sort.Sort(sort.IntSlice(a))
fmt.Println(a) // [1 2 3 4 5]
You simply call sort.Ints(a) which then calls Sort(IntSlice(a)) inside the standard library:
type IntSlice []int
func (x IntSlice) Len() int { return len(x) }
func (x IntSlice) Less(i, j int) bool { return x[i] < x[j] }
func (x IntSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
sort.IntSlice attaches the 3 methods of sort.Interface: Len, Less, and Swap to the type []int, to call:
// Sort sorts data in ascending order as determined by the Less method.
// It makes one call to data.Len to determine n and O(n*log(n)) calls to
// data.Less and data.Swap. The sort is not guaranteed to be stable.
func Sort(data Interface) {
n := data.Len()
quickSort(data, 0, n, maxDepth(n))
}
So you are able to reuse methods from the standard library, and you don't need to reimplement it again.
2- You may define your own types, See this example - There is no inside here for this named type - so methods must be outside of this type:
package main
import "fmt"
type num int32
func (p *num) inc() {
*p++
}
func main() {
p := num(100)
p.inc()
fmt.Println(p) // 101
}
The above named type num versus this user defined type: By design this makes the Go language consistent for both types:
type Animal struct {
Name string
moves []move.Direction
}
func (p *Animal) Walk(dir move.Direction) {
p.moves = append(p.moves, dir)
}
See also:
In Go is naming the receiver variable 'self' misleading or good practice?

Go variadic function argument passing

I am trying to understand, What is difference between 1st and 2nd passing argument in function. In both case methods are functional and compiles.
1)
generateReport(capacities...)
func generateReport(capacities ...float64) {
for i, cap := range capacities {
fmt.Printf("Plant %d capacity %.0f\n", i, cap)
}
}
2)
generateReport(plantCapacities)
func generateReport(capacities []float64) {
for i, cap := range capacities {
fmt.Printf("Plant %d capacity %.0f\n", i, cap)
}
}
Have found few good samples
1) GolangBot - Variadic Function
2) Golang.org - Passing arguments as #Himanshu mentioned.
According to Golang language specification
If f is variadic with a final parameter p of type ...T, then within f
the type of p is equivalent to type []T. If f is invoked with no
actual arguments for p, the value passed to p is nil. Otherwise, the
value passed is a new slice of type []T with a new underlying array
whose successive elements are the actual arguments, which all must be
assignable to T. The length and capacity of the slice is therefore the
number of arguments bound to p and may differ for each call site.
variadic functions are used to handle multiple trailing arguments. It can be used to pass slice arguments.
func main(){
capacities := []float64{1, 2, 3, 4}
generateReport(capacities...)
}
func generateReport(capacities ...float64) {
for i, cap := range capacities {
fmt.Printf("Plant %d capacity %.0f\n", i, cap)
}
}
Variadic functions can also be called in usual way with individual arguments. It works like spread operator in java script which can take multiple arguments. For eg:-
func main(){
generateReport(1,2,3,4)
}
func generateReport(capacities ...float64) {
for i, cap := range capacities {
fmt.Printf("Plant %d capacity %.0f\n", i, cap)
}
}

Go Golang - embedding types and "len/range"

package m
type M map[int]int
// have methods on M
// can use len and range on M
package n
// need methods of M
type N struct { M }
// methods available
// BUT cannot use len or range on N
// if " type N M " - I lose the methods on M
Need the methods of M and len/range functionality in a different package. How can this be done ?
Ignoring packages (they don't matter in this scenario), you need to specify a valid type for the builtins len and range:
type M map[int]int
func (m *M) SayHi() {
fmt.Println("Hi!")
}
type N struct{ M }
func main() {
var foo N
fmt.Println(len(foo.M))
for k, v := range foo.M {
fmt.Printf("%d: %d\n", k, v)
}
foo.SayHi()
}
foo.SayHi() works because SayHi is promoted to struct N.
However, len and range are not methods on M, they are builtins that expect specific types. Embedding does not change the type, it promotes methods from the embedded field to the container struct.
You can read more about the details in the Go spec and Effective Go.

Resources