Re: [go-nuts] Generic "nillable" constraint

2023-10-18 Thread 'Axel Wagner' via golang-nuts
On Wed, Oct 18, 2023 at 5:24 PM Jon Watte  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  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
>>  (you could write `if *dst !=
>> zero`) and by #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
  introduces *even less* new
 mechanism, by expanding comparison 

Re: [go-nuts] Generic "nillable" constraint

2023-10-18 Thread Jon Watte
> The two proposals above are a similarly big change, that are more general
in the kinds of problems they solve. So they seem better.

Well, if that would happen, my complaint would go away, so I'd be happy
with that.

I think "making all values comparable" is a worse change though (there's a
reason they aren't comparable!) 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 
wrote:

> On Wed, Oct 18, 2023 at 6:09 AM Jon Watte  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
>  (you could write `if *dst !=
> zero`) and by #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
>>>  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 

Re: [go-nuts] Generic "nillable" constraint

2023-10-17 Thread 'Axel Wagner' via golang-nuts
On Wed, Oct 18, 2023 at 6:09 AM Jon Watte  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
 (you could write `if *dst !=
zero`) and by #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 
> wrote:
>
>> Oh (sorry, being forgetful) and re "it's less of a new mechanism than
>> introducing a zero identifier": #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 
>> 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  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 

Re: [go-nuts] Generic "nillable" constraint

2023-10-17 Thread Bakul Shah
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  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  > wrote:
>> Oh (sorry, being forgetful) and re "it's less of a new mechanism than 
>> introducing a zero identifier": #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 > > 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 >> > wrote:
 On Wed, Oct 4, 2023 at 6:54 AM Jon Watte >>> > 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  proposes: Introduce 
 a `zero` predeclared 

Re: [go-nuts] Generic "nillable" constraint

2023-10-17 Thread Jon Watte
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 
wrote:

> Oh (sorry, being forgetful) and re "it's less of a new mechanism than
> introducing a zero identifier": #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 
> 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 
>> wrote:
>>
>>> On Wed, Oct 4, 2023 at 6:54 AM Jon Watte  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  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 

Re: [go-nuts] Generic "nillable" constraint

2023-10-03 Thread 'Axel Wagner' via golang-nuts
Oh (sorry, being forgetful) and re "it's less of a new mechanism than
introducing a zero identifier": #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 
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 
> wrote:
>
>> On Wed, Oct 4, 2023 at 6:54 AM Jon Watte  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  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 

Re: [go-nuts] Generic "nillable" constraint

2023-10-03 Thread 'Axel Wagner' via golang-nuts
(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 
wrote:

> On Wed, Oct 4, 2023 at 6:54 AM Jon Watte  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  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/CAEkBMfH0BdLrSjeBvV1sYoaPA13cJkpD-kyd5WOn8M32a2SnNw%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-03 Thread 'Axel Wagner' via golang-nuts
On Wed, Oct 4, 2023 at 6:54 AM Jon Watte  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  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/CAEkBMfHAW%3DjDmkvDs8VS373c%3DYFVTn2%3D2d0mLXdbAUnMM3wT%2BA%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-03 Thread Jon Watte
> 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...)

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/CAJgyHGNnRhoHr_C_Db-hZ1FiHqsMoNkbkLMfwVPdqSYijgq2ew%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-03 Thread Ian Lance Taylor
On Tue, Oct 3, 2023 at 5:57 PM Jon Watte  wrote:
>
> > Can you give us an example?
>
> I already gave examples! They're in the thread! In reality, we have the 
> insides of a compiler where this would be helpful in more complex cases, and 
> we have the insides of a graphql and JSON web interface where this would be 
> helpful in a more complex case, but I'm not about to post the hundreds of 
> lines of code that would be needed to set those up.

Thanks, but I'm looking for examples where it is important to permit
only type arguments that can be compared to nil.  Your examples seem
to me to be cases that would be addressed if we had a standard way to
compare a type to its zero value, such as https://go.dev/issue/61372
or https://go.dev/issue/62487  I'm asking whether there are cases
where we want the former but not the latter.  Thanks.

Ian

-- 
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/CAOyqgcUyWo-NLfBd3fXkyW8za-DPpDiqBboZeyFtO23gg5po%2BQ%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-03 Thread Jon Watte
> Can you give us an example?

I already gave examples! They're in the thread! In reality, we have the
insides of a compiler where this would be helpful in more complex cases,
and we have the insides of a graphql and JSON web interface where this
would be helpful in a more complex case, but I'm not about to post the
hundreds of lines of code that would be needed to set those up.

For another example, I have a slice of chans, some of which are nil. I'd
like to compact that to only the non-nil chans.
I can make a chan-specific slice compactor that's generic on the chan type.
But why shouldn't I be able to also use that for errors, or maps, or
pointers?

Why do I need:
func CompactChan[T any](ch []chan T) ...
func CompactMap[K comparable, V any](m []map[K, V]) ...
func CompactPointer[T any; U *T](sl []U) ...
...
and this still can't be used on `[]error` (other than with explicit types,
or using the more-powerful comparable constraint)

The main problem here is that the language has a set of "comparable values"
and a set of "comparable to nil" values and they only partially overlap.
Because the language has a type semantic of "can be compared or assigned to
nil," then that clearly should be expressible in the generic type system,
unless you for some reason want the the generic type system to be
deliberately less expressive than the full type system, and deliberately
want to exclude use cases like these.

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 5:32 PM Ian Lance Taylor  wrote:

> On Tue, Oct 3, 2023 at 4:33 PM Jon Watte  wrote:
> >
> >
> >
> > > It's not obvious that that is an interesting set of types.  How often
> > > do people want to write a generic function that permits any type that
> > > can be compared to nil but does not permit any numeric or string type?
> >
> > Well I've run into this with some frequency, and a bunch of other
> developers here have similarly moaned that this is a big gaping hole in the
> constraint system.
> > So, at least from our corner of the world, this is a legitimate desire.
> > There's a few other questions like this on the internet, too, so I don't
> think "we" are particularly special.
>
> Can you give us an example?  There are clear reasons for being able to
> compare a type to the zero value of that type.  But it's not clear why
> it's useful to focus on the set of types for which the zero value is
> written as "nil".  For example, why wouldn't I want to call your
> LogJSON example with a string?  Thanks.
>
> Ian
>

-- 
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/CAJgyHGPjiKRLYp6OJ9%3DYXKkVzt2wipp2y4tpZjNgC1JGPD5r7A%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-03 Thread Jon Watte
> It's not obvious that that is an interesting set of types.  How often
> do people want to write a generic function that permits any type that
> can be compared to nil but does not permit any numeric or string type?

Well I've run into this with some frequency, and a bunch of other
developers here have similarly moaned that this is a big gaping hole in the
constraint system.
So, at least from our corner of the world, this is a legitimate desire.
There's a few other questions like this on the internet, too, so I don't
think "we" are particularly special.

> Unless I misunderstand, you seem to have shifted the goal a bit.
> There is currently no way to restrict a type argument to be an
> interface type, with or without your suggestion of a nil constraint.

I have not shifted the goal. This very function has been my particular goal
in this thread from the start, because it clearly illustrates the need for
this constraint.
The only reason any other possible solution (like "zero values") was
suggested in this thread, was as a possible work-around for the lack of a
`nil` constraint.
(Note that this particular function could usefully be applied to slices of
maps, slices of chans, and so on -- none of which are comparable.)

"assign to nil" is also pretty useful as a generic constraint, because that
also includes "return a nil literal."
This can combo nicely with some other interfaces, such as JSON or YAML or
protobuf marshaling/demarshaling.

So, for illustration:

func LogJSON[T nil](name string, arg T) {
if arg == nil {
Log(name, "is empty")
} else {
data := Infallible(json.Marshal(arg))
Log(name, string(data))
}
}

(The actual, production uses of these kinds of affordances are typically a
little more involved, but we would save a lot of boilerplate/repeated code
if this constraint were available.)
(Also, Infallible() could be a good standard library function, because it's
common enough, but that's a totally different discussion that's not related
here.)

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 12:32 PM Ian Lance Taylor  wrote:

> On Tue, Oct 3, 2023 at 12:06 PM Jon Watte  wrote:
> >
> > I don't want to include int or struct{} in this case. I care
> specifically about "can be compared to nil"
> > The only thing I can do with a type parameter that is only constrained
> as "nil" is compare it to nil, or assign nil to it.
> > This means approximately "any reference type" -- interfaces, pointers,
> maps, slices, chans...
>
> It's not obvious that that is an interesting set of types.  How often
> do people want to write a generic function that permits any type that
> can be compared to nil but does not permit any numeric or string type?
>
>
> > But, let's put this out there:
> > How would you, today, write a function that compacted a slice of
> interface, such that any "nil" interface value would be removed from the
> slice?
>
> Unless I misunderstand, you seem to have shifted the goal a bit.
> There is currently no way to restrict a type argument to be an
> interface type, with or without your suggestion of a nil constraint.
>
>
> > func Compact[T ?](sl []T) []T {
> > j := 0
> > for i := 0; i < len(sl); i++ {
> > if el := sl[i]; el != nil {
> > sl[j] = el
> > j++
> > }
> > }
> > return sl[:j]
> > }
> >
> > There's nothing today I can put in place of the "?" to make this
> perfectly-reasonable generic function work.
> > I propose I should be able to put "nil" in place of the "?" to make this
> work, and it would work for any reference type (that can be compared or
> assigned to nil.)
> >
> > Unless I'm missing something?
>
> Well, here is a version that works for a slice of any type, including
> an interface type.  Arguably, though, using reflect is cheating.
>
> func Compact[T any](s []T) []T {
> j := 0
> for _, v := range s {
> vv := reflect.ValueOf(v)
> if vv.IsValid() && !vv.IsZero() {
> s[j] = v
> j++
> }
> }
> return s[:j]
> }
>
> Ian
>

-- 
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/CAJgyHGPBPnVaXoJT%2BYD0XYA9CS7TxqyOZJ_bjoz_9w59v-FhEA%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-03 Thread 'Axel Wagner' via golang-nuts
On Tue, Oct 3, 2023 at 9:06 PM Jon Watte  wrote:

> I don't want to include int or struct{} in this case. I care specifically
> about "can be compared to nil"
> The only thing I can do with a type parameter that is only constrained as
> "nil" is compare it to nil, or assign nil to it.
> This means approximately "any reference type" -- interfaces, pointers,
> maps, slices, chans...
>

Exactly. That is the problem. It means your suggestion does not allow a
slew of problems that #61372  would solve. It
means your suggestion is less powerful.


> But, let's put this out there:
> How would you, today, write a function that compacted a slice of
> interface, such that any "nil" interface value would be removed from the
> slice?
>
> func Compact[T ?](sl []T) []T {
> j := 0
> for i := 0; i < len(sl); i++ {
> if el := sl[i]; el != nil {
> sl[j] = el
> j++
> }
> }
> return sl[:j]
> }
>
> There's nothing today I can put in place of the "?" to make this
> perfectly-reasonable generic function work.
> I propose I should be able to put "nil" in place of the "?" to make this
> work, and it would work for any reference type (that can be compared or
> assigned to nil.)
>

With #61372, you would put `any` and write `el != zero`. And it would work
for all types, including the ones you mention.


>
> Unless I'm missing something?
>
> Sincerely,
>
> Jon Watte
>
>
> --
> "I find that the harder I work, the more luck I seem to have." -- Thomas
> Jefferson
>
>
> On Mon, Oct 2, 2023 at 9:26 PM Axel Wagner 
> wrote:
>
>> It doesn't solve the problem. That function signature you wrote could not
>> be instantiated by `int`, for example. You can't write `comparable | nil`,
>> as `comparable` is not allowed in a union. And if we allowed it, there
>> would be no way to write the body of the function. It doesn't help with
>> types like `struct{ F func() }`. And it doesn't make it easier to write the
>> zero value for returns etc. And it doesn't address the same kind of issue
>> for generated code.
>>
>> On Tue, Oct 3, 2023 at 3:51 AM Jon Watte  wrote:
>>
>>> What's the concern with "a constant interface that is inhabited exactly
>>> by the types that can compare to nil?"
>>>
>>> This is clearly something the language has as a concept -- it knows
>>> whether "foo == nil" is an error or not.
>>>
>>> "nil" could be the ergonomic name for this constraint:
>>>
>>> func Compact[T nil](s []T) ...
>>>
>>> Sincerely,
>>>
>>> Jon
>>>
>>>
>>> On Mon, Oct 2, 2023, 16:53 Ian Lance Taylor  wrote:
>>>
 There is a lot more on this topic at https://go.dev/issue/61372.
 We're not sure how best to handle it.

 Ian

>>> --
>>> 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/CAJgyHGPRdv%3DjOPs34V1Q%3DNh9RVpD8c-qagr%3DdEz0OX-oDEaa7Q%40mail.gmail.com
>>> 
>>> .
>>>
>>

-- 
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/CAEkBMfEuVW5C1zR6%3DCd-vxSyPOxg3MxERkcRTrgt10SS0_0ssw%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-03 Thread Ian Lance Taylor
On Tue, Oct 3, 2023 at 12:06 PM Jon Watte  wrote:
>
> I don't want to include int or struct{} in this case. I care specifically 
> about "can be compared to nil"
> The only thing I can do with a type parameter that is only constrained as 
> "nil" is compare it to nil, or assign nil to it.
> This means approximately "any reference type" -- interfaces, pointers, maps, 
> slices, chans...

It's not obvious that that is an interesting set of types.  How often
do people want to write a generic function that permits any type that
can be compared to nil but does not permit any numeric or string type?


> But, let's put this out there:
> How would you, today, write a function that compacted a slice of interface, 
> such that any "nil" interface value would be removed from the slice?

Unless I misunderstand, you seem to have shifted the goal a bit.
There is currently no way to restrict a type argument to be an
interface type, with or without your suggestion of a nil constraint.


> func Compact[T ?](sl []T) []T {
> j := 0
> for i := 0; i < len(sl); i++ {
> if el := sl[i]; el != nil {
> sl[j] = el
> j++
> }
> }
> return sl[:j]
> }
>
> There's nothing today I can put in place of the "?" to make this 
> perfectly-reasonable generic function work.
> I propose I should be able to put "nil" in place of the "?" to make this 
> work, and it would work for any reference type (that can be compared or 
> assigned to nil.)
>
> Unless I'm missing something?

Well, here is a version that works for a slice of any type, including
an interface type.  Arguably, though, using reflect is cheating.

func Compact[T any](s []T) []T {
j := 0
for _, v := range s {
vv := reflect.ValueOf(v)
if vv.IsValid() && !vv.IsZero() {
s[j] = v
j++
}
}
return s[:j]
}

Ian

-- 
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/CAOyqgcUaTdPE5-FGqqBRtr5kf-kM3b4rfsvwyFUBYu75gVZ8Ow%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-03 Thread Jon Watte
I don't want to include int or struct{} in this case. I care specifically
about "can be compared to nil"
The only thing I can do with a type parameter that is only constrained as
"nil" is compare it to nil, or assign nil to it.
This means approximately "any reference type" -- interfaces, pointers,
maps, slices, chans...

But, let's put this out there:
How would you, today, write a function that compacted a slice of interface,
such that any "nil" interface value would be removed from the slice?

func Compact[T ?](sl []T) []T {
j := 0
for i := 0; i < len(sl); i++ {
if el := sl[i]; el != nil {
sl[j] = el
j++
}
}
return sl[:j]
}

There's nothing today I can put in place of the "?" to make this
perfectly-reasonable generic function work.
I propose I should be able to put "nil" in place of the "?" to make this
work, and it would work for any reference type (that can be compared or
assigned to nil.)

Unless I'm missing something?

Sincerely,

Jon Watte


--
"I find that the harder I work, the more luck I seem to have." -- Thomas
Jefferson


On Mon, Oct 2, 2023 at 9:26 PM Axel Wagner 
wrote:

> It doesn't solve the problem. That function signature you wrote could not
> be instantiated by `int`, for example. You can't write `comparable | nil`,
> as `comparable` is not allowed in a union. And if we allowed it, there
> would be no way to write the body of the function. It doesn't help with
> types like `struct{ F func() }`. And it doesn't make it easier to write the
> zero value for returns etc. And it doesn't address the same kind of issue
> for generated code.
>
> On Tue, Oct 3, 2023 at 3:51 AM Jon Watte  wrote:
>
>> What's the concern with "a constant interface that is inhabited exactly
>> by the types that can compare to nil?"
>>
>> This is clearly something the language has as a concept -- it knows
>> whether "foo == nil" is an error or not.
>>
>> "nil" could be the ergonomic name for this constraint:
>>
>> func Compact[T nil](s []T) ...
>>
>> Sincerely,
>>
>> Jon
>>
>>
>> On Mon, Oct 2, 2023, 16:53 Ian Lance Taylor  wrote:
>>
>>> There is a lot more on this topic at https://go.dev/issue/61372.
>>> We're not sure how best to handle it.
>>>
>>> Ian
>>>
>> --
>> 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/CAJgyHGPRdv%3DjOPs34V1Q%3DNh9RVpD8c-qagr%3DdEz0OX-oDEaa7Q%40mail.gmail.com
>> 
>> .
>>
>

-- 
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/CAJgyHGMV%2BhF%3Dvfm7SLigHa8ZAh5tK28eKkBzQ9iu-FVw9J4MAQ%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-02 Thread 'Axel Wagner' via golang-nuts
It doesn't solve the problem. That function signature you wrote could not
be instantiated by `int`, for example. You can't write `comparable | nil`,
as `comparable` is not allowed in a union. And if we allowed it, there
would be no way to write the body of the function. It doesn't help with
types like `struct{ F func() }`. And it doesn't make it easier to write the
zero value for returns etc. And it doesn't address the same kind of issue
for generated code.

On Tue, Oct 3, 2023 at 3:51 AM Jon Watte  wrote:

> What's the concern with "a constant interface that is inhabited exactly by
> the types that can compare to nil?"
>
> This is clearly something the language has as a concept -- it knows
> whether "foo == nil" is an error or not.
>
> "nil" could be the ergonomic name for this constraint:
>
> func Compact[T nil](s []T) ...
>
> Sincerely,
>
> Jon
>
>
> On Mon, Oct 2, 2023, 16:53 Ian Lance Taylor  wrote:
>
>> There is a lot more on this topic at https://go.dev/issue/61372.
>> We're not sure how best to handle it.
>>
>> Ian
>>
> --
> 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/CAJgyHGPRdv%3DjOPs34V1Q%3DNh9RVpD8c-qagr%3DdEz0OX-oDEaa7Q%40mail.gmail.com
> 
> .
>

-- 
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/CAEkBMfE_j3LvJ7H_QBag4u4nvJ9W6iAyeqXV%2BgB0cqv-z_ZYXQ%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-02 Thread Jon Watte
What's the concern with "a constant interface that is inhabited exactly by
the types that can compare to nil?"

This is clearly something the language has as a concept -- it knows whether
"foo == nil" is an error or not.

"nil" could be the ergonomic name for this constraint:

func Compact[T nil](s []T) ...

Sincerely,

Jon


On Mon, Oct 2, 2023, 16:53 Ian Lance Taylor  wrote:

> There is a lot more on this topic at https://go.dev/issue/61372.
> We're not sure how best to handle it.
>
> Ian
>

-- 
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/CAJgyHGPRdv%3DjOPs34V1Q%3DNh9RVpD8c-qagr%3DdEz0OX-oDEaa7Q%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-02 Thread Ian Lance Taylor
There is a lot more on this topic at https://go.dev/issue/61372.
We're not sure how best to handle it.

Ian

-- 
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/CAOyqgcVA%2BqNC8CshKonaSZ1zheZyEw6P%3D2pEYe4XqHyqmb4%3D%3Dg%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-01 Thread Patrick Smith
A quick correction to my own comment:

On Sun, Oct 1, 2023 at 8:35 PM Patrick Smith  wrote:

> The problem with your code is that "T comparable" guarantees that two
> values of type T can be compared,
>

... but then I remembered the comparison can still panic at runtime, as per
https://go.dev/doc/go1.20#language

-- 
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/CAADvV_ttdboLt0zvp%2BMKFDezbc4o4pNTX-1HuO6hK7uvToHxsA%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-01 Thread Patrick Smith
On Sun, Oct 1, 2023 at 7:30 PM Jon Watte  wrote:

> I want to implement a generic function to "remove nil values from a
> slice." This should be able to remove nil instances of error, for example.
> So, let's say I have:
>
> var arg = []error{nil, errors.New("oh noes"), nil}
>
> Unfortunately, this doesn't work:
>
> func Compact[T comparable](s []T) T {
>...
>if s[i] == nil {
>
> because "error" is not comparable.
>

Actually, error is comparable. See https://go.dev/play/p/ymBOc5JQyJT

The problem with your code is that "T comparable" guarantees that two
values of type T can be compared, but does not guarantee you can compare a
value of type T to nil. For example, int is a comparable type, but you
can't compare an int to nil. See https://go.dev/play/p/SETrraZ7vkR, which
produces the error message: ./prog.go:11:11: invalid operation: t != nil
(mismatched types T and untyped nil)

This also doesn't work:
>
> func Compact[T any](s []T) T {
>   var zero T
>   ...
>   if s[i] == zero {
>
> because T is not a comparable constraint.
>

You can fix this by making T comparable instead of any. But then the
function will also remove 0 from int slices, "" from string slices, etc.
https://go.dev/play/p/NigMasu1aL1

-- 
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/CAADvV_stLn9%3DCXnydGWQgmP1CEFq_10bj96FpezTb6%3DqVBFSbQ%40mail.gmail.com.


Re: [go-nuts] Generic "nillable" constraint

2023-10-01 Thread Jon Watte
>  It seems to me `== nil` is a better way to spell that, than a function 
call. And a generic function can always do `v == *new(T)` to check if a 
value is the zero value.

Bringing this up because I found it from a thread on Google.

I want to implement a generic function to "remove nil values from a slice." 
This should be able to remove nil instances of error, for example.
So, let's say I have:

var arg = []error{nil, errors.New("oh noes"), nil}

Unfortunately, this doesn't work:

func Compact[T comparable](s []T) T {
   ...
   if s[i] == nil {

because "error" is not comparable.
This also doesn't work:

func Compact[T any](s []T) T {
  var zero T
  ...
  if s[i] == zero {

because T is not a comparable constraint.


It may be that "error" is somehow special, and is "the only thing" that 
doesn't fit neatly into "zero values" or "comparables," but it's a pretty 
basic fundament of the runtime, so having to special-case everything about 
it is quite frustrating and cumbersome.


Sincerely

Jon Watte



On Thursday, December 22, 2022 at 5:39:29 AM UTC-8 Axel Wagner wrote:

> On Thu, Dec 22, 2022 at 2:23 PM Pierre Durand  wrote:
>
>> Is there a way to declare a "nillable" constraint that matches all types 
>> that can be nil (pointer, slice, map, etc.) ?
>
>
> There is currently no way to do that. In particular, any interface type 
> can be `nil`, therefore any such constraint would have to be satisfied by 
> all interface types.
>  
>
>> I would like to write a function, that receives a parameter (with a 
>> generic type), and check if this parameter is nil.
>>
>
> I don't understand the point, to be honest. It seems to me `== nil` is a 
> better way to spell that, than a function call. And a generic function can 
> always do `v == *new(T)` to check if a value is the zero value.
>  
>
>> I found a very ugly way to write it: https://go.dev/play/p/0g0SoTlBEgs
>> The problem: the map type needs more than 1 type, so I need to provide 
>> the 3 types when I call the `IsNil()` function/
>> Is there a better way ?
>>
>> Yes I know I could use `any`, and check with reflect if the value inside 
>> the interface is nil.
>> But I want to know if it's possible to do it with generics.
>>
>> Thank you.
>>
>> -- 
>> 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/2043afe5-3d18-445a-90a9-75b48d3ec078n%40googlegroups.com
>>  
>> 
>> .
>>
>

-- 
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/01f1280f-029e-4b37-ae7e-324a62279fedn%40googlegroups.com.


Re: [go-nuts] Generic "nillable" constraint

2022-12-22 Thread 'Axel Wagner' via golang-nuts
On Thu, Dec 22, 2022 at 2:23 PM Pierre Durand 
wrote:

> Is there a way to declare a "nillable" constraint that matches all types
> that can be nil (pointer, slice, map, etc.) ?


There is currently no way to do that. In particular, any interface type can
be `nil`, therefore any such constraint would have to be satisfied by all
interface types.


> I would like to write a function, that receives a parameter (with a
> generic type), and check if this parameter is nil.
>

I don't understand the point, to be honest. It seems to me `== nil` is a
better way to spell that, than a function call. And a generic function can
always do `v == *new(T)` to check if a value is the zero value.


> I found a very ugly way to write it: https://go.dev/play/p/0g0SoTlBEgs
> The problem: the map type needs more than 1 type, so I need to provide the
> 3 types when I call the `IsNil()` function/
> Is there a better way ?
>
> Yes I know I could use `any`, and check with reflect if the value inside
> the interface is nil.
> But I want to know if it's possible to do it with generics.
>
> Thank you.
>
> --
> 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/2043afe5-3d18-445a-90a9-75b48d3ec078n%40googlegroups.com
> 
> .
>

-- 
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/CAEkBMfH-4aRxrGY2VrfWs52PecpjWR-s0yGOO7m-3%3Dpg37H9BQ%40mail.gmail.com.


[go-nuts] Generic "nillable" constraint

2022-12-22 Thread Pierre Durand
Is there a way to declare a "nillable" constraint that matches all types 
that can be nil (pointer, slice, map, etc.) ?

I would like to write a function, that receives a parameter (with a generic 
type), and check if this parameter is nil.
I found a very ugly way to write it: https://go.dev/play/p/0g0SoTlBEgs
The problem: the map type needs more than 1 type, so I need to provide the 
3 types when I call the `IsNil()` function/
Is there a better way ?

Yes I know I could use `any`, and check with reflect if the value inside 
the interface is nil.
But I want to know if it's possible to do it with generics.

Thank you.

-- 
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/2043afe5-3d18-445a-90a9-75b48d3ec078n%40googlegroups.com.