Skip to content

proposal: spec: add a mechanism to allow generic de- and re-structuring of complex values #45049

Open
@kortschak

Description

@kortschak

Background

The Type Parameters Generics proposal 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, here and here.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    LanguageChangeSuggested changes to the Go languageLanguageChangeReviewDiscussed by language change review committeeProposalgenericsIssue is related to generics

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions