On Saturday, August 22, 2020 at 12:49:21 AM UTC+2 rog wrote:

> On Fri, 21 Aug 2020 at 23:12, jimmy frasche <soapbo...@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
> }
>

Kotlin calls such a feature smart-casting, though it works for any type 
rather than such generic types with type lists. I'd love if Go had such a 
feature, even if it only applied to switch statements over generic types 
with type lists in them. This wouldn't be conversion, since the type is the 
actual type that is matched with the case.

 

>
> 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 <soapbo...@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.wa...@googlemail.com> wrote:
>> >
>> > On Fri, Aug 21, 2020 at 11:46 PM Ian Lance Taylor <ia...@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.wa...@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.wa...@googlemail.com> wrote:
>> >> >>
>> >> >>
>> >> >>
>> >> >> On Fri, Aug 21, 2020 at 11:30 PM roger peppe <rogp...@gmail.com> 
>> wrote:
>> >> >>>
>> >> >>> On Fri, 21 Aug 2020 at 22:10, jimmy frasche <soapbo...@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 <rogp...@gmail.com> 
>> wrote:
>> >> >>>> >
>> >> >>>> >
>> >> >>>> > On Fri, 21 Aug 2020 at 01:28, Ian Lance Taylor <
>> ia...@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...@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...@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/ce1090e5-82b8-4734-a2b6-7915b007b49an%40googlegroups.com.

Reply via email to