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
<https://github.com/golang/go/issues/809> 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
<https://github.com/golang/go/issues/809#issuecomment-66051363>", 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.

Reply via email to