On Wed, Oct 18, 2023 at 5:24 PM Jon Watte <jwa...@gmail.com> wrote:
> I think "making all values comparable" is a worse change though (there's a > reason they aren't comparable!) > FTR the proposal was not to make all values comparable, but to make all values comparable to the predeclared identifier nil - similar to how, currently, a lot of non-comparable values are comparable to the predeclared identifier nil. > and making everything comparable to nil without type qualification is > better IMO. > > Sincerely, > > Jon Watte > > > -- > "I find that the harder I work, the more luck I seem to have." -- Thomas > Jefferson > > > On Tue, Oct 17, 2023 at 10:25 PM Axel Wagner < > axel.wagner...@googlemail.com> wrote: > >> On Wed, Oct 18, 2023 at 6:09 AM Jon Watte <jwa...@gmail.com> wrote: >> >>> Circling back to this, because it came up today again. >>> >>> Here's the generic function I want to write. It comes up in a lot of >>> function composition, which in turn comes up in a lot of interface adapters >>> and such: >>> >>> func maybeAssign[T any](dst *T, src T, name string) { >>> if *dst != nil { // any can't be compared with nil >>> panic(fmt.Errorf("too many %s arguments", name)) >>> } >>> *dst = src >>> } >>> >>> Using this function in each assignment, instead of inlining the >>> four-line construct with panic, can save a lot of space and make code a lot >>> more readable. >>> The above doesn't work, because not every type can be assigned nil. >>> The following also doesn't work: >>> >>> func maybeAssign[T comparable](dst *T, src T, name string) { >>> var zero T >>> if *dst != zero { // interface and other nillable types can't be >>> compared to zero >>> panic(fmt.Errorf("too many %s arguments", name)) >>> } >>> *dst = src >>> } >>> >>> Because interface values aren't comparable. (As aren't chans, maps, etc, >>> but THOSE can be jammed into various interface constructs, whereas "any >>> interface" cannot, because "interface{}" doesn't actually mean "any >>> interface") >>> >>> Let me try to answer: >>> >>> > Why is the *specific* split into (interfaces, pointers, slices, >>> functions, maps, channels) and (numbers, booleans, strings, structs, >>> arrays) a particularly important one? >>> >>> Because, while go tries very hard to make sure every storable type has a >>> "zero value," it somehow decides that you can't necessarily COMPARE to that >>> zero value. >>> But the whole point of zero values is that you can tell them from >>> non-zero values! >>> So, the language has introduced a work-around with the concept of "I can >>> compare to nil" for these reference types that aren't comparable to their >>> zero value. >>> But generics don't allow us to sense or make use of this, so generics >>> can't express what the regular language can express. Even a very simple >>> case like the above, can't currently be expressed, and this leads to more >>> verbose code that's harder to read and harder to work with. (Granted, this >>> is my opinion, but I'm not alone.) >>> >> >> That does not actually answer the question, though. Again, note that your >> problem would be solved both by #61372 >> <https://github.com/golang/go/issues/61372> (you could write `if *dst != >> zero`) and by #62487 <https://github.com/golang/go/issues/62487> (you >> could just write `if *dst != nil`), neither of which require you to make a >> distinction between "nilable" types and "non-nilable" types. In fact, it >> would make your `maybeAssign` function worse - it would be less general, >> because it could only be used with a subset of types and it's not clear why >> that subset is a good one. >> >> (also, nit: channels are comparable) >> >> If the language instead changes so that nil means "the zero value" in >>> general, and it so happens that these nil-comparable types can be compared >>> to nil without any particular qualification, that also solves the problem. >>> >> >> Right. That is what my question was getting at. >> >> >>> That might indeed be a good solution -- but if so, it'd be nice to know >>> what we can do to make that happen, and what the other opposition to that >>> change might be, because that change also feels much less narrow than a >>> "nil" type constraint. >>> >> >> Being "less narrow" can mean two things: It can mean "it is a more >> general solution" and it can mean "it is a bigger change". The two >> proposals above are a similarly big change, that are more general in the >> kinds of problems they solve. So they seem better. >> >> >>> >>> >>> Sincerely, >>> >>> Jon Watte >>> >>> >>> -- >>> "I find that the harder I work, the more luck I seem to have." -- >>> Thomas Jefferson >>> >>> >>> On Tue, Oct 3, 2023 at 10:41 PM Axel Wagner < >>> axel.wagner...@googlemail.com> wrote: >>> >>>> Oh (sorry, being forgetful) and re "it's less of a new mechanism than >>>> introducing a zero identifier": #62487 >>>> <https://github.com/golang/go/issues/62487> introduces *even less* new >>>> mechanism, by expanding comparison to (and assignment of) `nil` to all >>>> types inside a generic function. It's not a new class of constraint, it >>>> just special-cases `nil` a bit more. So it is still a far more general >>>> mechanism, that solves more problems than `nilable` constraint, while >>>> requiring fewer (or at worst the same number of) new concepts. >>>> >>>> On Wed, Oct 4, 2023 at 7:36 AM Axel Wagner < >>>> axel.wagner...@googlemail.com> wrote: >>>> >>>>> (correction: It should be Convert[J isinterface, T J]. I changed the >>>>> name from I to J to be more readable and then missed one occurrence) >>>>> >>>>> On Wed, Oct 4, 2023 at 7:33 AM Axel Wagner < >>>>> axel.wagner...@googlemail.com> wrote: >>>>> >>>>>> On Wed, Oct 4, 2023 at 6:54 AM Jon Watte <jwa...@gmail.com> wrote: >>>>>> >>>>>>> > where it is important to permit only type arguments that can be >>>>>>> compared to nil >>>>>>> >>>>>>> I see! As in, if we somehow got a "equalszero" constraint, then that >>>>>>> constraint would solve the problem I illustrate. >>>>>>> I believe that assertion is correct, but I also believe that is a >>>>>>> stronger assertion, and also that it introduces more of a new concept >>>>>>> than >>>>>>> a simple "nil" constraint. (Unless you're looking for some way to make >>>>>>> "any" work, and introduce a zero keyword or something...) >>>>>>> >>>>>> >>>>>> Yes, that is what #61372 <https://go.dev/issue/61372> proposes: >>>>>> Introduce a `zero` predeclared identifier (!) that is assignable to any >>>>>> type and comparable to any type. With some discussion about whether it >>>>>> should only apply inside generic code or not. There is no proposal (as >>>>>> far >>>>>> as I know) for anything like an "equalszero" constraint, as every type >>>>>> can >>>>>> be assigned a meaningful comparison to its zero value, so it seems we >>>>>> should just allow it for all types. >>>>>> >>>>>> To be clear, the criticism of a `nilable` constraint is >>>>>> 1. It only solves a subset of the problem we are seeing. You gave >>>>>> examples from that subset. I gave some examples of problems we are seeing >>>>>> that are *not* in that subset. >>>>>> 2. It is not really clear this particular subset is particularly >>>>>> important. Why is the *specific* split into (interfaces, pointers, >>>>>> slices, functions, maps, channels) and (numbers, booleans, strings, >>>>>> structs, arrays) a particularly important one? >>>>>> 3. As long as that is not clear, it seems more prudent to focus on >>>>>> mechanisms that solve more of the problems we are seeing. >>>>>> >>>>>> FWIW I could, personally, get more (though still not fully) on board >>>>>> with an `isinterface` constraint, that would allow *only* >>>>>> interfaces. It would still allow assignment and comparison to `nil`. But >>>>>> it >>>>>> seems far clearer to me, that interfaces can be singled out. While a >>>>>> `nil` >>>>>> interface is categorically an invalid value, the same is not true for >>>>>> `nil` >>>>>> pointers/maps/channels/funcs *in general*. Any of those kinds of >>>>>> types could still have methods callable on them that work perfectly fine >>>>>> (by doing an `if receiver == nil` check in the method). You categorically >>>>>> can't call a method on a `nil` interface. >>>>>> >>>>>> And an `isinterface` constraint could still conceivable be useful for >>>>>> many of the examples you mentioned. Or it would allow >>>>>> >>>>>> func Convert[J isinterface, T I](s []T) []J { >>>>>> out := make([]I, len(T)) >>>>>> for i, v := range s { >>>>>> out[i] = J(v) >>>>>> } >>>>>> return out >>>>>> } >>>>>> >>>>>> I'd still not be convinced this is really worth it, but at least it >>>>>> seems clearer why that particular subset of types deserves to be singled >>>>>> out. In fact, many people have argued that the interface zero value >>>>>> really >>>>>> shouldn't have been spelled `nil`, because interfaces have so little in >>>>>> common, conceptually, to other "nilable" types. >>>>>> >>>>>> >>>>>>> >>>>>>> Also, there's the ergonomics of having to make a zero value >>>>>>> instance. Maybe we can rely on the compiler to optimize it away, but at >>>>>>> a >>>>>>> minimum it adds another required line of code in the implementation. E >>>>>>> g: >>>>>>> >>>>>>> func MaybeNuke[T nil](b bool, val T) T { >>>>>>> if b { >>>>>>> return nil >>>>>>> } >>>>>>> return val >>>>>>> } >>>>>>> >>>>>>> func MaybeNuke(T zero](b bool, val T) T { >>>>>>> if b { >>>>>>> var nope T // an extra line! >>>>>>> return nope >>>>>>> } >>>>>>> return val >>>>>>> } >>>>>>> >>>>>>> func MaybeNuke(T any](b bool, val T) T { >>>>>>> if b { >>>>>>> return zero[T]{} // maybe? seems weird >>>>>>> } >>>>>>> return val >>>>>>> } >>>>>>> >>>>>>> This is because not all zero values can be instantiated inline with >>>>>>> simply T{}. >>>>>>> >>>>>>> Sincerely, >>>>>>> >>>>>>> Jon Watte >>>>>>> >>>>>>> >>>>>>> -- >>>>>>> "I find that the harder I work, the more luck I seem to have." -- >>>>>>> Thomas Jefferson >>>>>>> >>>>>> -- 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/CAEkBMfF6b1uL3SsjbDTPnHj%2BEzKeCAFFS8pe%2Bo_X_NvUkeUgHA%40mail.gmail.com.