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.

Reply via email to