On Wed, 8 Jul 2020 at 00:33, Steven Blenkinsop <steven...@gmail.com> wrote:
> On Tue, Jul 7, 2020 at 10:44 AM, roger peppe <rogpe...@gmail.com> wrote: > >> >> In my description, there's no assumption that doing it over the type >> parameter implies a refinement of the previously set type constraints. In >> fact it definitely implies otherwise because (for example) if you know that >> a generic type parameter has the known type "string", then it's a loosening >> rather than a tightening of the constraint - you can now do more things >> with values of that type than you could previously. >> > > I think this is speaking at cross purposes. The identity of the type is > more constrained (it must be string as opposed to any other type > satisfying a particular interface), but you can do more with it in your > generic code. You're refining the constraint on the identity of the type > while at the same time expanding its capabilities. An unconstrained type > parameter can be any type but supports the fewest operations. > Fair point. > > I don't think it's sufficient. Consider this program: >> https://go2goplay.golang.org/p/wO0JHIuHH2l. If "string" matches both >> "myString" and "string" itself, then the type checker would allow the >> assignment of a function of type "func(string) uint64" to a function of >> type "func(myString) uint64" with no explicit type conversion, which breaks >> an important safety constraint in the language. Worse, if we let an >> interface type match any concrete type that happens to implement that >> interface, then there's a serious issue because those types probably don't >> have the same shape, so can't be used interchangeably. For example, this >> program would be valid, but isn't possible to compile because io.Stringer >> and string have different representations: >> https://go2goplay.golang.org/p/_uLjQxb-z-b >> > > I would think that in the second case, the assignment to xx wouldn't type > check because T is only known to implement io.Stringer rather than being > identical to io.Stringer. > That depends on the semantics of the type switch. In my suggestion, it would type check because the case would only be chosen if the argument type is *exactly* that type (but the xx slice wouldn't be assigned to). You'd need to add a "type" qualifier to match on any type that implements io.Stringer. Perhaps this would be more clear if it was written as > > type switch { > case T io.Stringer: > // Code here can treat the type parameter T > // as though the generic type list defining > // it was `(type T io.Stringer)` > } > That's another interesting syntax. I'm not sure whether there's any particular advantage in mentioning the type parameter in each case, although I guess it does mean that the syntax for multiple matches is straightforward and can allow an "any" match. type switch { case T1 string, T2 string: case T1 []byte, T2 string: case T1 string: } If my syntax were to support multiple type parameters, that might look like switch T1, T2 { case (string, string): case ([]byte, string): case (string, type interface{}): } One could define _ to be a synonym for "type interface{}", I guess. Of course, this wouldn't allow you to write either of your examples. But > then, your stringHash example could be written using: > > func stringHash(type S interface{type string})(s S) uint64 { > return 1 > } > That doesn't seem ideal to me. There are many potentially useful non-generic functions out there and I think it would be nice to be able to use them in this way. > > https://go2goplay.golang.org/p/FAaqnFalYCL > > I think the key tension with allowing you to match on the specific type > passed in is that, if you actually need to do this, then that means your > function can't handle certain type parameters which are permitted by its > signature, i.e. it's not total over its type parameter domain. > ISTM that technically the function is still total over the type parameter domain (assuming you don't panic in the default case) - it just has different behaviour depending on the type parameters, which is exactly what the type switch is for. Also, you can *already* do this in a slightly more limited way, by converting to interface{} and type switching on that, although that's restrictive because it doesn't allow you to assign back to generic values - the types aren't unified - which is why we're talking about this feature here. > If interfaces allowed you to restrict the type parameters passed in to the > specific ones you can handle, then you would necessarily also be able to > handle each specific case by matching on interface satisfaction as well. > I'd hope that most people using type switches for specialisation would include a generic fallback for unknown types. The main use cases I see for it are optimisation and preserving backward compatibility. cheers, rog. -- 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/CAJhgachtriny-g5%2BHGD2SqhopSxYcrsZw90AJvTh7%3DU4KxNsTA%40mail.gmail.com.