I'd happy with

        if val { ... }

and

        if !val { ... }

Instead of comparing val with an explicit zero but don't feel strongly about 
this.

> On Oct 17, 2023, at 9:09 PM, 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.)
> 
> If the language instead changes such that chans and interfaces and maps 
> become comparable, then that's fine -- that solves the problem! But that 
> seems like a much bigger change than a constraint that senses exactly the 
> "can compare to nil" semantic.
> 
> 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. 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.
> 
> 
> 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 
> <mailto: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 
>> <mailto: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 
>>> <mailto:axel.wagner...@googlemail.com>> wrote:
>>>> On Wed, Oct 4, 2023 at 6:54 AM Jon Watte <jwa...@gmail.com 
>>>> <mailto: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 
> <mailto:golang-nuts+unsubscr...@googlegroups.com>.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/golang-nuts/CAJgyHGPYEqrDTyRE-gMvSpiyUobHWKuL35nAAtDa1MmGd8uqzA%40mail.gmail.com
>  
> <https://groups.google.com/d/msgid/golang-nuts/CAJgyHGPYEqrDTyRE-gMvSpiyUobHWKuL35nAAtDa1MmGd8uqzA%40mail.gmail.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/FADB0BAB-675C-45A1-8B31-70C0EAFCA4F8%40iitbombay.org.

Reply via email to