-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: spec: add a mechanism to allow generic de- and re-structuring of complex values #45049
Comments
Note that CC @griesemer |
Thanks, @ianlancetaylor. Yes, that was something I was considering in terms of backwards compatibility. I was trying to see if there was something that the syntax choice here was cause problems with but was unable to find any (of course they may exist). Other non-function based syntax is obviously also possible. |
I quite like the view of complex types as generic types. Suppose that we'd already had generics in the language and wanted to add support for complex values.
Perhaps if we think of the existing complex types as a kind of sugar over the above, then it might make sense to allow One issue with that is that this would make the type It also opens up a question: given that type lists allow any type whose underlying types are mentioned, this would also admit |
FWIW I always felt that the inclusion of complex numbers into the language was a bit strange. IMO, adding more strangeness on top to make them work with generics is bringing the sunk cost fallacy to mind. I think it might well be worthy of consideration to deprecate the builtin complex types in favor of a library-implementation of them - which can actually be built with generics. The generic type could live in |
In my opinion, this example might show one of the limits of the current form of the proposal where constraints are derived from types. The opposite should happen, i.e. types should be derived from constraints. (with a way to extract the original constraints of a type so that the general user can constrain type parameters) Then we could have functions of constraints object argument that returns constraints (Propositional functions). This is different from using types as constraints because not all constraints are necessarily types. I will reference here again the little warning from "A gentle introduction to semantic subtyping" on page 200 as this seem to remotely apply here. Their issue is about subtyping relations but I think the more general issue is about type constraint propagation... i.e. whenever there is a relation between types. In the present case for instance or notably when we deal with interfaces, composite types such as structs etc. The constraint package would eventually just be the partial constraint store of the default constraint system induced into the type system. Just a note. |
I don't know what the downvote is for since I see no explication but said in some more simpler terms, types should be built from constraint sets. An interface would be like a regular type with some structural and the nominal constraints lifted. Effectively allowing subtyping (but not parametricity here because interfaces are themselves types) . This is going to be important for any other composite types and especially if we want typelists are regular interfaces. Propositional functions would allow to define precisely the constraint that form a complex type depending on the constraints of the argument float type. Funnily enough, this is the goal of semantic subtyping: allowing parametricity in the presence of structural subtyping. |
@atdiar I gave a thumbs-down because it sounds to me like you are suggesting a relatively fundamental change to the generics design - not just an amendment to address OPs concerns. I felt that this is neither the time nor place for that discussion. I also didn't want to drag the issue further off-topic, so I tried leaving it with an emoji reaction. |
Yes but OP concerns are to me a part of a more general problem with the design. I think it fits here. It explains the idea of a complex higher-order function that takes a float type as input for instance. Thank you for the clarification. |
@Merovius Regarding the removal of built-in complex types, there are two parts that need to be addressed. The first is likely trivial, it would need a rewrite tool to convert the existing infix notation to function calls (we have an extensive collection of functions that work on complex values and a manual rewrite is a non-starter). The second is a judgement call; reading conventional infix notation is much easier for most people. We have other rings (quaternion, dual, hyperdual, dual quaternion and noncom dual complex) implemented and using those systems is harder than for the complex types. If a rewrite tool for thing 1 existed I guess it could be coopted to make a code generation tool to convert from infix to function call forms though. |
Yeah, my comment was originally prompted by this sentence from your mail to golang-nuts:
I think the same readability concern also transfers to other types, like Personally, it seems more elegant to me to address the readability issue separately, if we are worried about it. With a solution that can be applied to other library-types as well. If Go had operator overloading, that would solve the readability issue of representing ℂ as structs while also helping Quaternions, In any case, I just wanted to make the option explicit :) I'm content to just wait and see where the rest of the discussion is going :) |
I'll just comment that instead of
you could also use
if we had typeof (and that could apply to much more than complex and float problems). |
@rsc can I clarify the proposal status of this issue? It has been removed from Incoming, but not placed in Active. Was this intentional? |
It's now labeled as a Go2 proposal.
- gri
…On Wed, Apr 7, 2021, 12:40 PM Dan Kortschak ***@***.***> wrote:
@rsc <https://github.com/rsc> can I clarify the proposal status of this
issue? It has been removed from Incoming, but not placed in Active. Was
this intentional?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#45049 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACBCIT46UXRRWHMOXL3H5IDTHSYJRANCNFSM4ZHUISPQ>
.
|
Maybe how that fits in with https://github.com/golang/proposal#readme could be clarified? This otherwise feels kind of arbitrary. |
We have in the past liberally moved proposals between "regular" and "Go2" proposals, so I don't think this is arbitrary. In the Go2 proposal reviews we tend to spend more time on language design issues, sometimes discussing just one or two proposals at length. It seems that this proposal fits better in that process for now. It might move back once we have decided on the best way forward. So if anything, this relabelling should free up space for a more in-depth discussion. |
OK. Thanks for clarifying that. Perhaps some text in the proposal#readme could be added to note this progress path? |
Is there interest in extending such a proposal to a broader syntax for destructuring uncommon types in general? Something like the following example: type Person struct {
Firstname, Lastname string
NumberOfCats,NumberOfDogs int
}
type Name struct {
FName, LName string
}
type Pets struct {
Cats, Dogs int
}
func DestructuringExample(person Person) bool {
name, pets := person.{Name, Pets}
name.FName = "Ted"
return person.Firstname == name.FName
}
func DestructuringExample2(person Person) bool {
name, pets := (*Name)(unsafe.Pointer(&person)), (*Name)(add(unsafe.Pointer(&person),sizeof(Name))
name.FName = "Ted"
return person.Firstname == name.FName
} Specific concerns would be whether it would be an issue that this exposes otherwise unexported types, and that this now creates a strong coupling between the involved named types. Nonetheless, this type of destructuring can be currently done with raw pointers, though it is considerable more brittle and is not statically checkable. P.S. I want this. Omigosh, I want this. If it's tangental to the topic of complex number expansion, regardless of whether this proposal is acceptable, I will absolutely write up a proposal. |
Rather than using We don't need anything else, because we can rely on type inference. type Float interface { ~float32 | ~float64 }
func ToComplex[F Float, C ~complex(F)](f F) C { return complex(f, 0) }
func RealPart[C ~complex(F), F Float](c C) F { return real(c) } In these examples, the function ordinary argument determines the first type argument, and the second type argument is inferred from the first type argument. |
A slightly different syntax would be to use |
To follow-up on @ianlancetaylor's comment, another way of stating this is to say that complex types are (special, built-in) composite types of which there exist two types: And for historic reasons we also have the aliases type complex64 = complex[float32]
type complex128 = complex[float64] The reason why these complex[T] types are not defined as structs is because they allow operations (such as the usual arithmetic operations) which are not available on structs. The one anomaly we'd have is the fact that |
@ianlancetaylor @griesemer variously @ianlancetaylor I think probably this
is
Is this correct? If so, I think we could live with this approach. |
Yes, your understanding is correct. Sorry for the typos. I think I've fixed them now. |
If we were to have: type complex64 = complex[float32]
type complex128 = complex[float64] Then could we write these? type MyFloat float64
type MyComplex complex[MyFloat]
type ConstraintComplex[T float32 | float64] complex[T]
type AnyComplex128[T ~float64] complex[T] |
@zephyrtronium Yes, I believe all these should be valid but I may be missing something. |
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
andcomplex128
as their underlying type. Under the current proposal it is not possible to write something like this: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.
where
?
indicates some type specification that would be thefloat
type corresponding to thecomplex
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.
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
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
andreal
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:The choice of
complex
is obvious, thoughreal
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 ofT
. Iffloat
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 ascomplex(T)
rather thancomplex(T, T)
.A potential alternative syntax instead of
complex(T)
andreal(T)
iscomplex[T]
andreal[T]
, though this would be ambiguous with indexing in the code body.The text was updated successfully, but these errors were encountered: