On Fri, 21 Aug 2020 at 23:12, jimmy frasche <soapboxcic...@gmail.com> wrote:
> I don't want a generic min unless it looks like this: > > func Min[T constraints.Ordered](a, b T) T { > switch T { > case float32: > return T(math.Min(float32(a), float32(b))) > case float64: > return T(math.Min(float64(a), float64(b))) > } > if a < b { > return a > } > return b > } > I'd really like to be able to write that as: func Min[T constraints.Ordered](a, b T) T { switch T { case float32: return math.Min(float64(a), float64(b)) case float64: return math.Min(a, b) } if a < b { return a } return b } But then there is the difficulty that the original T is no longer the same as the T inside the type switch. So this code would print false not true, which would be... somewhat unexpected: https://go2goplay.golang.org/p/d0cJBfYObAY Perhaps the type switch* should *allow declaring a new name for the switched type, making it clear that a new type was being declared inside the switch. Something like: switch T1 := T { } but the ":=" operator doesn't seem quite right there, and that still doesn't solve the original problem, because T1 is still incompatible with all the argument values. I guess that one could allow conversion or assignment compatibility between types involving T and T1, but that might complicate the spec too much. On Fri, 21 Aug 2020 at 23:12, jimmy frasche <soapboxcic...@gmail.com> wrote: > I don't want a generic min unless it looks like this: > > func Min[T constraints.Ordered](a, b T) T { > switch T { > case float32: > return T(math.Min(float32(a), float32(b))) > case float64: > return T(math.Min(float64(a), float64(b))) > } > if a < b { > return a > } > return b > } > > On Fri, Aug 21, 2020 at 3:03 PM Axel Wagner > <axel.wagner...@googlemail.com> wrote: > > > > On Fri, Aug 21, 2020 at 11:46 PM Ian Lance Taylor <i...@golang.org> > wrote: > >> > >> Yes, there are various such possibilities. > >> > >> What jimmy frasche said above is correct: nothing changes in the case > >> of a type switch of a type parameter. The code now knows the type > >> list element that the type argument matched, but it can't do anything > >> that it couldn't do anyhow. > > > > > > I think there are two reasonable things that it could be allowed to do > in the case, that aren't allowed outside: > > 1. Convert to the matched type. We have a guarantee that the matched > type is either identical or has the same underlying type, both of which > would allow a conversion in the language as-is. I feel allowing this > conversion would be sufficiently useful (e.g. passing things to > `strconv.Itoa` or functions from `math` can be very useful). > > 2. If the type is not a predeclared type, we could even take this a step > further, as the types must be identical - so we might allow treating them > as such. This feels natural when viewed from the "type lists are > essentially sum types" POV. However, it would treat predeclared types more > special than other declared types and so it may be too elaborate to put > into the spec. It would also allow what rog suggest - but only in certain > corner cases, which feels weird. > > > > The more I think about it, the less I understand the intention behind > the type-switch construct introduced. I tend to agree that 1. at least > should be possible to make it useful. But even then, it seems like kind of > a big change for relatively limited use. What was the motivation behind > that change? Is there discussion somewhere, of interesting use-cases this > enables? > > > > > >> > >> > >> Ian > >> > >> On Fri, Aug 21, 2020 at 2:43 PM Axel Wagner > >> <axel.wagner...@googlemail.com> wrote: > >> > > >> > also, of course, you could still use operators with them, while now > also knowing the exact semantics of those operators (e.g. in regards to > overflow), which might also be useful. > >> > > >> > On Fri, Aug 21, 2020 at 11:42 PM Axel Wagner < > axel.wagner...@googlemail.com> wrote: > >> >> > >> >> > >> >> > >> >> On Fri, Aug 21, 2020 at 11:30 PM roger peppe <rogpe...@gmail.com> > wrote: > >> >>> > >> >>> On Fri, 21 Aug 2020 at 22:10, jimmy frasche < > soapboxcic...@gmail.com> wrote: > >> >>>> > >> >>>> I'd assume that would fail to compile as you're returning a []T > not a []int > >> >>> > >> >>> > >> >>> If that's the case, then I'm not sure that such a type switch would > be very useful. It would tell you what type the values are, but you can't > do anything with them because all the values would still be of the original > type. > >> >> > >> >> > >> >> You can reasonably convert them to their underlying type and *then* > use them as such. > >> >> That would make it useful while not allowing what you posit. > >> >> > >> >>> I had assumed that the intention was that within the arm of the > type switch, the switched type would take on the specified type. > >> >>> That would allow (for example) specialising to use underlying > machine operations on []T when T is a known type such as byte. > >> >> > >> >> > >> >> It would, however, prevent you from calling methods on the type or > pass it to a function taking an interface compatible with the constraint. > >> >> Also, I shudder to even imagine how this could be put into a spec. > >> >> > >> >>> > >> >>> > >> >>> > >> >>>> On Fri, Aug 21, 2020 at 2:07 PM roger peppe <rogpe...@gmail.com> > wrote: > >> >>>> > > >> >>>> > > >> >>>> > On Fri, 21 Aug 2020 at 01:28, Ian Lance Taylor <i...@golang.org> > wrote: > >> >>>> >> > >> >>>> >> After many discussions and reading many comments, we plan to > move > >> >>>> >> forward with some changes and clarifications to the generics > design > >> >>>> >> draft. > >> >>>> >> > >> >>>> >> 1. > >> >>>> >> > >> >>>> >> We’re going to settle on square brackets for the generics > syntax. > >> >>>> >> We’re going to drop the “type” keyword before type parameters, > as > >> >>>> >> using square brackets is sufficient to distinguish the type > parameter > >> >>>> >> list from the ordinary parameter list. To avoid the ambiguity > with > >> >>>> >> array declarations, we will require that all type parameters > provide a > >> >>>> >> constraint. This has the advantage of giving type parameter > lists the > >> >>>> >> exact same syntax as ordinary parameter lists (other than using > square > >> >>>> >> brackets). To simplify the common case of a type parameter > that has > >> >>>> >> no constraints, we will introduce a new predeclared identifier > “any” > >> >>>> >> as an alias for “interface{}”. > >> >>>> >> > >> >>>> >> The result is declarations that look like this: > >> >>>> >> > >> >>>> >> type Vector[T any] []T > >> >>>> >> func Print[T any](s []T) { … } > >> >>>> >> func Index[T comparable](s []T, e T) { … } > >> >>>> >> > >> >>>> >> We feel that the cost of the new predeclared identifier “any” is > >> >>>> >> outweighed by the simplification achieved by making all > parameter > >> >>>> >> lists syntactically the same: as each regular parameter always > has a > >> >>>> >> type, each type parameter always has a constraint (its > meta-type). > >> >>>> >> > >> >>>> >> Changing “[type T]” to “[T any]” seems about equally readable > and > >> >>>> >> saves one character. We’ll be able to streamline a lot of > existing > >> >>>> >> code in the standard library and elsewhere by replacing > “interface{}” > >> >>>> >> with “any”. > >> >>>> >> > >> >>>> >> 2. > >> >>>> >> > >> >>>> >> We’re going to simplify the rule for type list satisfaction. > The type > >> >>>> >> argument will satisfy the constraint if the type argument is > identical > >> >>>> >> to any type in the type list, or if the underlying type of the > type > >> >>>> >> argument is identical to any type in the type list. What we are > >> >>>> >> removing here is any use of the underlying types of the types > in the > >> >>>> >> type list. This tweaked rule means that the type list can > decide > >> >>>> >> whether to accept an exact defined type, other than a > predeclared > >> >>>> >> type, or whether to accept any type with a matching underlying > type. > >> >>>> >> > >> >>>> >> This is a subtle change that we don’t expect to affect any > existing > >> >>>> >> experimental code. > >> >>>> >> > >> >>>> >> We think that this definition might work if we permit interface > types > >> >>>> >> with type lists to be used outside of type constraints. Such > >> >>>> >> interfaces would effectively act like sum types. That is not > part of > >> >>>> >> this design draft, but it’s an obvious thing to consider for the > >> >>>> >> future. > >> >>>> >> > >> >>>> >> Note that a type list can mention type parameters (that is, > other type > >> >>>> >> parameters in the same type parameter list). These will be > checked by > >> >>>> >> first replacing the type parameter(s) with the corresponding > type > >> >>>> >> argument(s), and then using the rule described above. > >> >>>> >> > >> >>>> >> 3. > >> >>>> >> > >> >>>> >> We’re going to clarify that when considering the operations > permitted > >> >>>> >> for a value whose type is a type parameter, we will ignore the > methods > >> >>>> >> of any types in the type list. The general rule is that the > generic > >> >>>> >> function can use any operation permitted by every type in the > type > >> >>>> >> list. However, this will only apply to operators and > predeclared > >> >>>> >> functions (such as "len" and "cap"). It won’t apply to > methods, for > >> >>>> >> the case where the type list includes a list of types that all > define > >> >>>> >> some method. Any methods must be listed separately in the > interface > >> >>>> >> type, not inherited from the type list. > >> >>>> >> > >> >>>> >> This rule seems generally clear, and avoids some complex > reasoning > >> >>>> >> involving type lists that include structs with embedded type > >> >>>> >> parameters. > >> >>>> >> > >> >>>> >> 4. > >> >>>> >> > >> >>>> >> We’re going to permit type switches on type parameters that > have type > >> >>>> >> lists, without the “.(type)” syntax. The “(.type)” syntax > exists to > >> >>>> >> clarify code like “switch v := x.(type)”. A type switch on a > type > >> >>>> >> parameter won’t be able to use the “:=” syntax anyhow, so there > is no > >> >>>> >> reason to require “.(type)”. In a type switch on a type > parameter > >> >>>> >> with a type list, every case listed must be a type that appears > in the > >> >>>> >> type list (“default” is also permitted, of course). A case > will be > >> >>>> >> chosen if it is the type matched by the type argument, although > as > >> >>>> >> discussed above it may not be the exact type argument: it may > be the > >> >>>> >> underlying type of the type argument. > >> >>>> > > >> >>>> > > >> >>>> > Here's one interesting implication of this: it allows us to do > type conversions that were not previously possible. > >> >>>> > > >> >>>> > For example, if we have "type I int", we can use a type switch > to convert some type []I to type []int: > >> >>>> > https://go2goplay.golang.org/p/-860Zlz7-cn > >> >>>> > > >> >>>> > func F[type T intlike](ts []T) []int { > >> >>>> > switch T { > >> >>>> > case int: > >> >>>> > return ts > >> >>>> > } > >> >>>> > return nil > >> >>>> > } > >> >>>> > > >> >>>> > It seems to me that this kind of thing will allow us to perform > a similar conversion (convert some part of the type to its underlying type) > on any type. > >> >>>> > > >> >>>> > In the early days of Go, the spec allowed this kind of > conversion as a normal type conversion. I wonder if it might be reasonable > to revert to those more relaxed semantics. I think they're potentially > useful, for example, when dealing with named types obtained from modules > with two different major versions without incurring copies. > >> >>>> > > >> >>>> > Although in the above-linked issue Robert talks about runtime > costs such as "possibly re-mapping method tables", I don't see that this > would necessarily be the case. Thoughts? > >> >>>> > > >> >>>> > -- > >> >>>> > 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+unsubscr...@googlegroups.com. > >> >>>> > To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/CAJhgacjL7p7qck%3DSO0Nz9f%2BKZw6MNcgkD5REXwSNK7_fCTXYQg%40mail.gmail.com > . > >> >>> > >> >>> -- > >> >>> 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+unsubscr...@googlegroups.com. > >> >>> To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/CAJhgacjTm%3DC-6f%2B4%2BA0HCTDT0_U7pQZOmRjShuzigdocDzAcww%40mail.gmail.com > . > -- 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+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAJhgacgBbaTyw965AHNSsyFtZ%3DnsTtWRtrVqioy1X9Ouy%3D2G%3DQ%40mail.gmail.com.