Repository: go
Issue: proposal: spec: add a mechanism to allow generic de- and re-structuring of complex values
Labels: LanguageChange,Proposal,generics,LanguageChangeReview

# Background

The [Type Parameters Generics proposal](https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md) has been accepted so type-parametric generic functions will be part of the language in the not wildly distant future. Currently there is a gap in the proposal that has been raised in golang-nuts, [here](https://groups.google.com/g/golang-nuts/c/vJfdrHfbCfI/m/iItrQxIGAgAJ), [here](https://groups.google.com/g/golang-nuts/c/dZIdMJTO5ws/m/nQbBk7NrBgAJ) and [here](https://groups.google.com/g/golang-dev/c/nug_hZnGQog/m/et4aKG6_DAAJ).

The issue in those discussions is that the type parameterisation approach does not currently allow any kind of correlated structured types unless the structured type is a composite type (structs, arrays, slices, and maps); the current approach covers the majority of structured types but misses types that have `complex64` and `complex128` as their underlying type. Under the current proposal it is not possible to write something like this:

```
type Complex interface {
	type complex64, complex128
}

func Abs[T Complex](v T) ? {
	return ?(math.Hypot(real(v), imag(v))) 
}
```

Another example in the opposite direction, showing a use that would useful in generic DSP code is to allow the conversion of a real value to the same value represented as a complex number.
```
type Real interface {
	type float32, float64
}

func AsComplex[T Real](v T) ? {
	return ?(complex(v, 0))
}
```
where `?` indicates some type specification that would be the `float` type corresponding to the `complex` `T`.

The examples shown here are trivial (though relevant to a generic "math/cmplx" package), but more significant issues exist for example the example given in the first golang-nuts link above, a generic Eigen Decomposition function which takes a real m×n matrix and returns a pair of complex matrices and a complex vector.

```
type Real interface {
	type float32, float64
}

func Eigen[T Real](m, n int, data []T) (left, values, right []?, ok bool) { ...
```

If complex values are represented as structs, these issues do not exist, for example, [here](https://go2goplay.golang.org/p/lhJEIK3XD9Q). However, this shuts out any use of the language's complex types, and it requires that complex types be referred to by their real coefficients' types, rather than by their actual complex type.

Alternatively, the function could be implemented as follows

```
type Real interface {
	type float32, float64
}

func Eigen[T Real](m, n int, data []T) (leftRI, valuesRI, rightRI []T, ok bool) { ...
```
where the returned slices hold alternating real and imaginary parts, but the 70s called and they want their language back, and this does not address the issue with non-slice types.

# Proposal

I propose that the `complex` and `real` keywords be adopted to allow obtaining the correlated type for real and complex values respectively. This would allow the examples above to be written as follows:
```
type Real interface {
	type float32, float64
}

type Complex interface {
	type complex64, complex128
}

func AsComplex[T Real](v T) complex(T) {
	return complex(T)(complex(v, 0))
}

func Abs[T Complex](v T) real(T) {
	return real(T)(math.Hypot(real(v), imag(v)))
}

func Eigen[T Real](m, n int, data []T) (left, values, right []complex(T), ok bool) { ...
```

The choice of `complex` is obvious, though `real` is perhaps less so, it can be read as the type of the real coefficients of the complex type, rather than the real part. For this reason, `imag(T)` is not appropriate to obtain the float correspondent of `T`. If `float` were still part of the language it could have been chosen.

Since complex values' parts must match, the complex corresponding to a float `T`, is specified as `complex(T)` rather than `complex(T, T)`.

A potential alternative syntax instead of `complex(T)` and `real(T)` is `complex[T]` and `real[T]`, though this would be ambiguous with indexing in the code body.