On Wed, Jan 20, 2021 at 12:04 PM Brian Candler <b.cand...@pobox.com> wrote:
> What do you make of this? > https://go2goplay.golang.org/p/gN-FK2kbYK5 > > Using interface values, it seems possible to bypass a declared constraint > that two arguments have the same type. > I understand if people are confused by it. I find it straight forward though - type-parameters express constraints on static types. From that perspective, the constraint isn't "bypassed". It's fulfilled - both arguments have the same static type. Personally, I just don't see this cause a lot of practical problems. I can't imagine a lot of cases where you'd need two interface values to have the same dynamic value - except if you want to type-assert them, maybe to do some operation with them. Generics specifically exist to eliminate the need to do that. In other words: If you want two values to have the same dynamic type, you probably want to do an operation to them, so you'll probably add a type-list to the constraint for the operation you want. At that point, the caller can no longer pass interface-values. Can you come up with a realistic reason to have that constraint that doesn't end up needing reflect anyway (where you can implement any checks you want)? 1. If the function signature says two types should be the same (via type > parameters), but this can't be determined at compile time, then a runtime > check should be inserted at the call point - and panic if fail. > > 2. Since a nil interface value has "no type", then nil interface values > should be disallowed for arguments defined with type parameters. Again, if > this can't be determined statically, then add a runtime check and panic. > > Incidentally, point (2) also sidesteps the OP's original problem, since it > guarantees the value can never be nil. > > On Wednesday, 20 January 2021 at 10:43:42 UTC axel.wa...@googlemail.com > wrote: > >> On Wed, Jan 20, 2021 at 11:08 AM Brian Candler <b.ca...@pobox.com> wrote: >> >>> The end result is there's a crucial but subtle difference between: >>> >>> type Foo interface { ... } >>> func f(v Foo) ... >>> >>> and >>> >>> type Foo interface { ... } >>> func f[T Foo](v T) ... >>> >>> Given that the second case supports both concrete types *and* interface >>> types, then it seems to me that comparing a value with nil is a >>> semantically valid thing to do. >>> >> >> FWIW, it's also possible to write a constraint that can *only* be >> satisfied by concrete types - namely one containing a type-list. >> >> IMO it is confusing to allow comparing generic values to nil, in general. >> If we could, I would either assume I can compare any *non*-generic value to >> nil. I would assume we can always go from a generic function to an >> instantiated one by substituting all occurrences of the type-parameters. >> But IMO, nil is already confusing enough as it is. >> >> I think the solutions Patrick and Ian provided for checking if an >> interface is nil, or a value is zero, are enough. Because, to be clear: >> Calling a method on an interface can panic *whether or not that interface >> is nil*. Fundamentally, the case where a caller passes you a nil-interface >> is not different from the case where a caller passes you an interface with >> a method that dereferences a nil-pointer or accesses a slice out of bounds >> - or an interface that just calls panic("foo") in one of its methods. It's >> a bug in the caller and panicing in such a case is the correct thing to do. >> You will never be able to statically catch all bugs or statically prevent >> all panics. I don't think this particular panic is special enough to give >> it extra attention - because, again, you *can* do the check, if you want >> to, it's just not super convenient. >> >> >> If the function specialisation is a concrete type, then that comparison >>> would always be false and the code path optimised out. >>> >>> Alternatively, generics could exclude interface values. But then you >>> get the weird situation that a generic function declaration that *looks* >>> like an interface type, explicitly disallows interface type values! >>> >>> ---- 8< ---- >>> >>> With my wild hat on: it makes me wonder what would happen if the >>> generics proposal became nothing more than interfaces with linked >>> constraints - so that you could say "this function takes a function of type >>> T (interface C) and returns *the same type* T", or "this function takes a >>> []T and returns a value of *the same type* T". >>> >>> What I mean is, the difference between >>> func f(a, b fmt.Stringer) c fmt.Stringer { ... } >>> and >>> func f[T fmt.Stringer](a, b T) c T { ... } >>> would simply be that a, b and c had to be of the *same* concrete type - >>> but were otherwise still interface values (and specialisation, if >>> implemented, would just be a hidden optimisation). >>> >>> The obvious problem is that if you actually pass interface values >>> around, then many type-mismatch violations couldn't be detected until >>> runtime. >>> >>> However: I find the same problem occurs with the current generics >>> implementation, *if* you pass interface variables. Check this out: >>> https://go2goplay.golang.org/p/F08gh5gotsO >>> Print2() expects that both arguments are of the same type - but in the >>> final call, they are not! There is neither compile-time nor run-time error. >>> >>> ISTM that constrained interface types *could* also be checked at >>> compile time, in the common case where the caller passes concrete types. >>> Rewriting to use plain interfaces instead of generics: >>> https://go2goplay.golang.org/p/CfkvAcgr8mC >>> I think that an interface type constraint system *could* statically >>> check that both args of the first Print2() call were (or were not) the same >>> type. >>> >>> What you might end up with is linked type constraints being a natural >>> extension of interfaces. Generic specialisation would just be an >>> optimisation on top of that (if the call site knows that a particular set >>> of concrete types is being used). Type lists could also become an >>> extension of interfaces. >>> >>> However, that's just off the top of my head. Whilst I've been following >>> generics intermittently, no doubt this has been considered (and discarded) >>> before. >>> >>> Regards, Brian. >>> >>> -- >>> 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/34bdb40e-ad0b-47ce-8588-d014d0886470n%40googlegroups.com >>> <https://groups.google.com/d/msgid/golang-nuts/34bdb40e-ad0b-47ce-8588-d014d0886470n%40googlegroups.com?utm_medium=email&utm_source=footer> >>> . >>> >> -- > 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/e4e446de-9be4-4e31-8df7-ed48998022een%40googlegroups.com > <https://groups.google.com/d/msgid/golang-nuts/e4e446de-9be4-4e31-8df7-ed48998022een%40googlegroups.com?utm_medium=email&utm_source=footer> > . > -- 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/CAEkBMfHaC%2Byr4B-8N1iwv%2B6o9Z-CZe3iHTv%3DY_8vORHFPBLRyw%40mail.gmail.com.