Re: [go-nuts] Nillable basic types?

2024-06-10 Thread Daniel Lepage
>
>  But I think this is not really worth arguing. If you change fundamental
> things like this in the language, then you'll suggesting turning Go into
> something that looks like Rust. In which case, you may as well just use
> Rust.

I don't think I'm proposing a fundamental change, or to turn Go into Rust -
there are good reasons why Go (and the majority of other languages) don't
force us to null-check every single pointer access, because there are lots
of use cases for pointers besides "this can be null" (e.g. for mutability
or performance reasons). I'm proposing a fairly specific syntax to solve a
fairly specific (but, at least in projects I've worked on, incredibly
common) problem. That said, I do agree that #57644
 subsumes this problem (as do a
number of other proposals for union or variant types), and if this feature
were to be implemented it would definitely be preferable to do so en route
to a fuller variant type solution.

-- 
Dan

On Thu, Mar 21, 2024 at 11:04 PM Mike Schinkel  wrote:

> Hi Axel,
>
> Thank you for that link. I had not seen it before, but it is rather
> insightful.
>
> -Mike
>
> On Wednesday, March 20, 2024 at 10:29:20 AM UTC-4 Axel Wagner wrote:
>
> FWIW I believe (as Brian sort of points out) this proposal is fully
> subsumed under #57644 . Under
> that proposal, the proposed type `int | nil` would be spelled `interface{
> int }`. The other syntactical constructs are, as far as I can tell,
> identical - you'd have to use a type-assertion to use an `interface{ int }`
> as an integer (e.g. to do arithmetic), you can use it with type-switches,
> and any `int` as well as `nil` would be assignable to it.
>
> I think the one difference would be that `x == 42` would work on
> `interface{ int }`, but would (presumably) not work on `int | nil`. I
> personally doubt that this difference would justify an extra construction,
> but I'm mentioning it for completeness sake.
>
> The "nice to have" of type guards is, I think, an easy idea to mention,
> but not an easy idea to add to Go. Note that the other languages mentioned,
> that do that, use a function-scoped type-inference (as far as I know) -
> that is, they look at an identifiers use over the entire function and then
> infer the most general type it would have.
> Go has so far tried to avoid doing anything like that, limiting any
> inference to the statement (or expression) a value appears in. And this
> idea effectively means an identifier would change its type over its
> lifetime (from `interface{ int }` - does not allow arithmetic - to `int` -
> does allow arithmetic), which would create numerous problems for existing
> tooling, as it violates assumptions made by the `go/*` packages.
>
> On Wed, Mar 20, 2024 at 11:26 AM Mike Schinkel wrote:
>
> On Wednesday, March 20, 2024 at 5:47:00 AM UTC-4 Brian Candler wrote:
>
> If you change fundamental things like this in the language, then you'll
> suggesting turning Go into something that looks like Rust. In which case,
> you may as well just use Rust.
>
>
> Agreed.  Which is why I was asking if using interfaces as type constraints
> would address the concern.
>
> And as discussed, probably not.
>
> But it is an interesting thought exercise. If an interface-based solution
> could be found, it would address the concern without turning us effectively
> into Rust programmers. ¯\_(ツ)_/¯
>
> -Mike
>
> --
>
> 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/b3cf8686-b0fb-4cdd-938e-deee4a6af273n%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/b13c0f3c-2f87-4270-bc57-a22c577473d9n%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/CANtoCQ_ATPoPxbDvCbj30yN0RvS_W8c4qKfQYRpBbTcuEdObiw%40mail.gmail.com.


Re: [go-nuts] Nillable basic types?

2024-03-21 Thread Mike Schinkel
Hi Axel,

Thank you for that link. I had not seen it before, but it is rather 
insightful.

-Mike

On Wednesday, March 20, 2024 at 10:29:20 AM UTC-4 Axel Wagner wrote:

FWIW I believe (as Brian sort of points out) this proposal is fully 
subsumed under #57644 . Under 
that proposal, the proposed type `int | nil` would be spelled `interface{ 
int }`. The other syntactical constructs are, as far as I can tell, 
identical - you'd have to use a type-assertion to use an `interface{ int }` 
as an integer (e.g. to do arithmetic), you can use it with type-switches, 
and any `int` as well as `nil` would be assignable to it.

I think the one difference would be that `x == 42` would work on 
`interface{ int }`, but would (presumably) not work on `int | nil`. I 
personally doubt that this difference would justify an extra construction, 
but I'm mentioning it for completeness sake.

The "nice to have" of type guards is, I think, an easy idea to mention, but 
not an easy idea to add to Go. Note that the other languages mentioned, 
that do that, use a function-scoped type-inference (as far as I know) - 
that is, they look at an identifiers use over the entire function and then 
infer the most general type it would have.
Go has so far tried to avoid doing anything like that, limiting any 
inference to the statement (or expression) a value appears in. And this 
idea effectively means an identifier would change its type over its 
lifetime (from `interface{ int }` - does not allow arithmetic - to `int` - 
does allow arithmetic), which would create numerous problems for existing 
tooling, as it violates assumptions made by the `go/*` packages.

On Wed, Mar 20, 2024 at 11:26 AM Mike Schinkel wrote:

On Wednesday, March 20, 2024 at 5:47:00 AM UTC-4 Brian Candler wrote:

If you change fundamental things like this in the language, then you'll 
suggesting turning Go into something that looks like Rust. In which case, 
you may as well just use Rust.


Agreed.  Which is why I was asking if using interfaces as type constraints 
would address the concern.

And as discussed, probably not.  

But it is an interesting thought exercise. If an interface-based solution 
could be found, it would address the concern without turning us effectively 
into Rust programmers. ¯\_(ツ)_/¯ 

-Mike

-- 

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/b3cf8686-b0fb-4cdd-938e-deee4a6af273n%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/b13c0f3c-2f87-4270-bc57-a22c577473d9n%40googlegroups.com.


Re: [go-nuts] Nillable basic types?

2024-03-20 Thread 'Axel Wagner' via golang-nuts
FWIW I believe (as Brian sort of points out) this proposal is fully
subsumed under #57644 . Under
that proposal, the proposed type `int | nil` would be spelled `interface{
int }`. The other syntactical constructs are, as far as I can tell,
identical - you'd have to use a type-assertion to use an `interface{ int }`
as an integer (e.g. to do arithmetic), you can use it with type-switches,
and any `int` as well as `nil` would be assignable to it.

I think the one difference would be that `x == 42` would work on
`interface{ int }`, but would (presumably) not work on `int | nil`. I
personally doubt that this difference would justify an extra construction,
but I'm mentioning it for completeness sake.

The "nice to have" of type guards is, I think, an easy idea to mention, but
not an easy idea to add to Go. Note that the other languages mentioned,
that do that, use a function-scoped type-inference (as far as I know) -
that is, they look at an identifiers use over the entire function and then
infer the most general type it would have.
Go has so far tried to avoid doing anything like that, limiting any
inference to the statement (or expression) a value appears in. And this
idea effectively means an identifier would change its type over its
lifetime (from `interface{ int }` - does not allow arithmetic - to `int` -
does allow arithmetic), which would create numerous problems for existing
tooling, as it violates assumptions made by the `go/*` packages.

On Wed, Mar 20, 2024 at 11:26 AM Mike Schinkel  wrote:

> On Wednesday, March 20, 2024 at 5:47:00 AM UTC-4 Brian Candler wrote:
>
> If you change fundamental things like this in the language, then you'll
> suggesting turning Go into something that looks like Rust. In which case,
> you may as well just use Rust.
>
>
> Agreed.  Which is why I was asking if using interfaces as type constraints
> would address the concern.
>
> And as discussed, probably not.
>
> But it is an interesting thought exercise. If an interface-based solution
> could be found, it would address the concern without turning us effectively
> into Rust programmers. ¯\_(ツ)_/¯
>
> -Mike
>
> --
> 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/b3cf8686-b0fb-4cdd-938e-deee4a6af273n%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/CAEkBMfHkUwS_XyNAkwM79Hy4F_%2BQxy%3DcaZRvmYkCc-MDi3O9Ww%40mail.gmail.com.


Re: [go-nuts] Nillable basic types?

2024-03-20 Thread Mike Schinkel
On Wednesday, March 20, 2024 at 5:47:00 AM UTC-4 Brian Candler wrote:

If you change fundamental things like this in the language, then you'll 
suggesting turning Go into something that looks like Rust. In which case, 
you may as well just use Rust.


Agreed.  Which is why I was asking if using interfaces as type constraints 
would address the concern.

And as discussed, probably not.  

But it is an interesting thought exercise. If an interface-based solution 
could be found, it would address the concern without turning us effectively 
into Rust programmers. ¯\_(ツ)_/¯ 

-Mike

-- 
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/b3cf8686-b0fb-4cdd-938e-deee4a6af273n%40googlegroups.com.


Re: [go-nuts] Nillable basic types?

2024-03-20 Thread Mike Schinkel
On Wednesday, March 20, 2024 at 5:31:08 AM UTC-4 Brian Candler wrote:


It's in the very first post that opened this thread, under the heading "## 
Summary".


I did in-fact miss it. Thank you for pointing to it.

-Mike 
 

-- 
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/7e807f38-97a2-4890-9a9b-bd0e535e481bn%40googlegroups.com.


Re: [go-nuts] Nillable basic types?

2024-03-20 Thread 'Brian Candler' via golang-nuts


I got the impression the proposal was motivated by scalars that did not 
currently allow `nil` values


Under the heading "Alternatives and why they're bad" he describes some ways 
this is currently dealt with - such as the common idiom of returning or 
passing a pointer to a value, instead of a plain value.  (He didn't mention 
using an interface, incidentally - which doesn't have some of the downsides 
he described. For example, a plain numeric value wrapped in an interface 
can't be mutated)

As has already been observed, if you're going to allow the full range of 
values of some type T (i.e. all possible bit patterns), *plus* the sentinel 
value of "unset", then you need an extra bit - which can be done using a 
struct, or by indirecting through a pointer or an interface. 

The problem that the OP has is that none of those options will force you to 
check the state of the extra bit, or that the pointer/interface is not nil 
- which could be argued is a problem with pointers/interfaces in general.

But I think this is not really worth arguing. If you change fundamental 
things like this in the language, then you'll suggesting turning Go into 
something that looks like Rust. In which case, you may as well just use 
Rust.

-- 
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/b409e7bb-ef94-40db-986f-268643e7cb39n%40googlegroups.com.


Re: [go-nuts] Nillable basic types?

2024-03-20 Thread 'Brian Candler' via golang-nuts
On Wednesday 20 March 2024 at 09:01:43 UTC Mike Schinkel wrote:

Your comments made me go back and read the whole thing, but I was unable to 
find a list of enumerated objectives, and I did not find the text you 
quoted.  Did I miss it somehow?


It's in the very first post that opened this thread, under the heading "## 
Summary". This link should take you to it:
https://groups.google.com/g/golang-nuts/c/pN0AO7a_S2k/m/3osyz_NyBgAJ

Quoting directly:

# Proposal: Nillable types

## Summary

I propose that for any type T, the type `T | nil` should also be a valid 
type with the following properties:
1. The zero value of a `T | nil` is `nil`.
2. Any `T` or `nil` may be used as a value of `T | nil`
3. It is a compile-time error to use a `T | nil` as a `T` without first 
checking that it is non-nil.

-- 
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/51fec468-faa5-4a42-b10a-31fa68f1a9c3n%40googlegroups.com.


Re: [go-nuts] Nillable basic types?

2024-03-20 Thread Mike Schinkel

On Wednesday, March 20, 2024 at 4:14:27 AM UTC-4 Brian Candler wrote:

When you say "var x NillableUint8" then you've just declared a variable of 
an interface type, and interface types are already nilable, so there's no 
need for "| nil" in the type!


Well, I was thinking  of 1.) explicitness of intent, and 2.) potentially a 
signal to the compiler for the use-case.  

But you probably have a point.
 

However, I don't think this meets the OP's  objective number 3:

> 3. It is a compile-time error to use a `T | nil` as a `T` without first 
checking that it is non-nil.


Hmm.  While I did not read the whole proposal at first, my initial takeaway 
was that his intent was more about allowing a way for a scalar value to 
have an invalid value so it would be possible to determine if a scalar had 
been set or not rather than being unsure if zero means *"It was set to 0"* 
vs *"It was never set and defaulted to 0."*

Your comments made me go back and read the whole thing, but I was unable to 
find a list of enumerated objectives, and I did not find the text you 
quoted.  Did I miss it somehow?

What I did see is his *"Nice to have"* section which talked about checking 
for non-nil.  Since he titled it *"nice to have"* I presume he did not 
consider that a primary objective of his proposal?

OTOH, even if it is an explicit call-out of `nil` in a type constraint 
*could* be the signal for the compiler to enforce that, if that was 
something the Go team agreed with that. Still, it is probably too obscure 
to be an appropriate signal. ¯\_(ツ)_/¯

Therefore I think the underlying request a completely different one: that 
you should never be able to use an interface (or a pointer or a channel, or 
insert into a map), without first checking that it's not nil, in a way that 
can be statically validated by the compiler. I'm sure that suggestion has 
come up before and been discussed to death - e.g. you end up with static 
types like "a pointer which can never be nil".


I definitely see there is an argument one could make for having the 
compiler guarantee against incorrectly using `nil`.  But I got the 
impression the proposal was motivated by scalars that did not currently 
allow `nil` values and not by reference types like pointers, channels and 
maps. Although he did not state that explicitly, his examples implied that 
to me.

For the compiler to guarantee that an `int` is properly set it needs as a 
prerequisite the potential for what it effectively a `nil` state. But 
ensuring against a misused `nil` feels to me to be orthogonal to first 
allowing an *"unset"* state for scalars.  

Or maybe I misread?  Maybe the best thing to do is let him tell us what he 
was thinking?
 
-Mike


On Wednesday 20 March 2024 at 07:34:10 UTC Mike Schinkel wrote:

On Mar 19, 2024, at 2:43 PM, Daniel Lepage wrote:

I'm not proposing that *any* value be made nillable, I'm proposing the 
explicit syntax

var x uint8 | nil

that would create a nillable uint8. A variable of type `byte` would still 
only take up one byte; a variable of type `byte | nil` would be larger. 
Existing code, which obviously doesn't use the `| nil` syntax because it 
doesn't exist yet, would be completely unaffected by this change.


Focusing the proposal like that was helpful, at least for me.  The original 
proposal? tldr; 

Question: Assuming the following was currently possible with type 
constraints, how would your proposal differ from the following?

type NillableUInt8 interface {

   uint8 | nil 

}

var x NillableUInt8


Also, if the above were possible in future Go, would that achieve the same 
objectives you are seeking, or not?  And if not, why not?

Finally, for the Go team, if that would be meet his objectives, would 
extending type constraints in this manner be a viable potential?


-Mike

-- 
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/66a9f1f6-a05d-4fed-b41d-5cfec262fb5bn%40googlegroups.com.


Re: [go-nuts] Nillable basic types?

2024-03-20 Thread 'Brian Candler' via golang-nuts
When you say "var x NillableUint8" then you've just declared a variable of 
an interface type, and interface types are already nilable, so there's no 
need for "| nil" in the type!

https://go.dev/play/p/f54akG65qJ3
https://go.dev/play/p/Jmtlta0h9m9   // generic version

It's a perfectly valid way of specifying an optional value ("error" works 
this way, after all). However, I don't think this meets the OP's  objective 
number 3:

> 3. It is a compile-time error to use a `T | nil` as a `T` without first 
checking that it is non-nil.

Therefore I think the underlying request a completely different one: that 
you should never be able to use an interface (or a pointer or a channel, or 
insert into a map), without first checking that it's not nil, in a way that 
can be statically validated by the compiler. I'm sure that suggestion has 
come up before and been discussed to death - e.g. you end up with static 
types like "a pointer which can never be nil".

On Wednesday 20 March 2024 at 07:34:10 UTC Mike Schinkel wrote:

> On Mar 19, 2024, at 2:43 PM, Daniel Lepage  wrote:
>
> I'm not proposing that *any* value be made nillable, I'm proposing the 
> explicit syntax
>
> var x uint8 | nil
>
> that would create a nillable uint8. A variable of type `byte` would still 
> only take up one byte; a variable of type `byte | nil` would be larger. 
> Existing code, which obviously doesn't use the `| nil` syntax because it 
> doesn't exist yet, would be completely unaffected by this change.
>
>
> Focusing the proposal like that was helpful, at least for me.  The 
> original proposal? tldr; 
>
> Question: Assuming the following was currently possible with type 
> constraints, how would your proposal differ from the following?
>
> type NillableUInt8 interface {
>
>uint8 | nil 
>
> }
>
> var x NillableUInt8
>
>
> Also, if the above were possible in future Go, would that achieve the same 
> objectives you are seeking, or not?  And if not, why not?
>
> Finally, for the Go team, if that would be meet his objectives, would 
> extending type constraints in this manner be a viable potential?
>
>
> -Mike
>

-- 
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/9e78e0c1-81cc-46c0-87ac-5bfd183093aen%40googlegroups.com.


Re: [go-nuts] Nillable basic types?

2024-03-20 Thread Mike Schinkel
> On Mar 19, 2024, at 2:43 PM, Daniel Lepage  wrote:
> 
> I'm not proposing that *any* value be made nillable, I'm proposing the 
> explicit syntax
> 
> var x uint8 | nil
> 
> that would create a nillable uint8. A variable of type `byte` would still 
> only take up one byte; a variable of type `byte | nil` would be larger. 
> Existing code, which obviously doesn't use the `| nil` syntax because it 
> doesn't exist yet, would be completely unaffected by this change.

Focusing the proposal like that was helpful, at least for me.  The original 
proposal? tldr; 

Question: Assuming the following was currently possible with type constraints, 
how would your proposal differ from the following?

type NillableUInt8 interface {
   uint8 | nil 
}
var x NillableUInt8

Also, if the above were possible in future Go, would that achieve the same 
objectives you are seeking, or not?  And if not, why not?

Finally, for the Go team, if that would be meet his objectives, would extending 
type constraints in this manner be a viable potential?

-Mike

-- 
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/3CC3FD1B-D591-4B21-AE7B-0D9E5C821F31%40newclarity.net.


Re: [go-nuts] Nillable basic types?

2024-03-19 Thread Patrick Smith
On Tue, Mar 19, 2024 at 11:44 AM Daniel Lepage  wrote:

> I'm not proposing that *any* value be made nillable, I'm proposing the
> explicit syntax
>
> var x uint8 | nil
>
> that would create a nillable uint8. A variable of type `byte` would still
> only take up one byte; a variable of type `byte | nil` would be larger.
> Existing code, which obviously doesn't use the `| nil` syntax because it
> doesn't exist yet, would be completely unaffected by this change.
>

Any such proposal would have to specify how this interacts with types that
are already nillable. Consider

var p *int | nil
p = nil

Does this make p "unset"? Does it set p to *int(nil)? How can subsequent
code distinguish between the cases where p is unset and p has the value
*int(nil)?

Also, given

var a any | nil

are there  _three_ different ways in which a can be nil (a is unset,
any(nil), or, say, *int(nil))? People are already confused by having two
different ways for interface values to be nil.

-- 
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_vWZD6cmNY%2BFX4aU-_XRYNBHBgnRCoNyn_21DOz-9zVnQ%40mail.gmail.com.


Re: [go-nuts] Nillable basic types?

2024-03-19 Thread Daniel Lepage
*From Jan Mercl: *

> To represent all those values and the new possibility
> of the value being nil, we need 257 distinct values. But that does not
> fit 8 in bits anymore. However, changing the size and/or the bit
> representation of such a variable is observable, making it not
> backwards-compatible.


I'm not proposing that *any* value be made nillable, I'm proposing the
explicit syntax

var x uint8 | nil

that would create a nillable uint8. A variable of type `byte` would still
only take up one byte; a variable of type `byte | nil` would be larger.
Existing code, which obviously doesn't use the `| nil` syntax because it
doesn't exist yet, would be completely unaffected by this change.

*From Brian Candler:*

> I also think that you need to find multiple compelling use cases (of which
> that example is not one), before fundamentally adding complexity to the
> language. Remember that "nil" is already overloaded in Go, so adding
> another (similar but different) meaning increases complexity for the reader.


Back when I worked at google, this was the number one cause (at least in
code that I worked with) of runtime errors in go (mostly in code using
protobuf v2 or ygot). I've also seen a lot of random github discussions
that boiled down to "how do we differentiate between 'x is unset' and 'x is
explicitly 0'; if I have some time next weekend I'll see if I can make a
big list of those to support the motivation for this.

*From Jeremy French:*

> I understand the problem you are trying to solve, and it's valid, I
> think.  But this solutions "feels" bad and very un-Go-like.  Usable zero
> values is a feature in Go. Whether you agree with that or not, it's a
> selling feature of the language and clearly something that was/is important
> to the Go Authors - as is explicit vs implicit logic.


This would make zero values *more* usable - it provides a way to ensure
that the zero value of a struct is a valid and consistent empty value of
that type, without requiring that users remember to set extra bools or do
nil checks. `Certificate` is a good example of this - you would expect a
"zero" certificate to not have a path length specified, but since 0 is a
possible value for MaxPathLen in this context, the type needs the extra
bool because otherwise `Certificate{}` would give you a certificate that
asserts that its subject is a CA and that this certificate cannot be
followed by any other intermediate certificates.

And, to be clear, the developers of that library could have left the bool
out - they already have the logic that -1 indicates an unset length, so
they could've just insisted that anyone who created a Certificate must
remember to explicitly set its MaxPathLen to -1. They added this extra
boolean solely because they wanted the zero value `Certificate{}` to
actually be an empty certificate, and the fact that they added a bunch of
extra logic (and new ways to make a logically inconsistent certificate) in
order to this is pretty strong evidence that this would be a useful thing
for the language to support.

It would not be an extremely common thing to do: for most types, the zero
value really is usable! But in cases where the zero value of a field has an
actual important meaning, it would be really great if there were a standard
way to make zero values of structs still be useful.

 So having a zero value that implicitly means "don't use this" a a feature
> that is built into the language seems like it could lead to confusion or at
> least a very muddled message about the nature and purpose of Go.


As far as I know, nobody has this issue with pointers, which very
definitely have a zero value that means "don't use this"; I don't see how
using 'nil' this way would confuse anyone about the nature of Go.

Indeed, the most common way I've seen to solve this problem is just to use
pointers, and live with the fact that if a junior dev forgets a nil check
then your code will compile just fine but panic at runtime; this seems
antithetical to the nature of Go.

I think maybe where your opinion diverges from the "hive mind" here is that
> you want the compiler to enforce this notion.  You can already achieve your
> basic objective with your "Additional Bool" example, and the confusion you
> complain about there can be easily mitigated with a better named variable
> "IsSet" rather than "IsZero" like Brian Candler suggested.


This is a minor point, but in the example I gave, IsZero is only checked if
the value is zero, so that users can write `cert.MaxPathLen = 5` without
remembering to set the bool except in the less common case where you set it
to 0. It obviously wouldn't be hard to have an IsSet, but both cases add
operations or checks that users must remember to do to maintain
consistency, with no compile-time mechanism to validate that they actually
are doing this, and make it possible to construct inconsistent data (e.g.
setting Value to something non-zero but also setting IsSet to false).


> But you still 

Re: [go-nuts] Nillable basic types?

2024-03-19 Thread Jeremy French
I understand the problem you are trying to solve, and it's valid, I think.  
But this solutions "feels" bad and very un-Go-like.  Usable zero values is 
a feature in Go. Whether you agree with that or not, it's a selling feature 
of the language and clearly something that was/is important to the Go 
Authors - as is explicit vs implicit logic.  So having a zero value that 
implicitly means "don't use this" a a feature that is built into the 
language seems like it could lead to confusion or at least a very muddled 
message about the nature and purpose of Go.

I think maybe where your opinion diverges from the "hive mind" here is that 
you want the compiler to enforce this notion.  You can already achieve your 
basic objective with your "Additional Bool" example, and the confusion you 
complain about there can be easily mitigated with a better named variable 
"IsSet" rather than "IsZero" like Brian Candler suggested.  But you still 
wouldn't have compile-time enforcement.  And that I think is where Go is 
going to disappoint you.  Go is a strongly typed language, at least on the 
scale of QBasic to Pascal, but the compiler does allow you to do a lot of 
things that you shouldn't, in the interest of letting you do things that 
might be a little crazy but that still work.  It seems to me that Go likes 
to leave "should" rules to the linter rather than the compiler, so maybe 
you could use a custom linter to achieve what you're looking for?  It's not 
a perfect solution, but it feels like it gets you 90% there.

On Monday, March 18, 2024 at 4:42:32 AM UTC-4 Brian Candler wrote:

> I like Go because it's relatively simple and low-level, like C, except 
> with things like garbage collection and channels integrated.  If Go were to 
> have "| nil" types then the internal representation of such variables would 
> have to be something like this:
>
> type Maybe[T any] struct {
> Value T
> IsSet bool
> }
>
> In which case, why not just use that form explicitly? The code you write 
> is then clear and obvious. Taking one of your examples:
>
> func foo3(x Maybe[int]) {
>   if x.IsSet {
> fmt.Println("x squared is", x.Value*x.Value)
>
>   } else {
> fmt.Println("No value for x")
>   }
> }
>
> // https://go.dev/play/p/ci10fhU1zqL
>
> I also think that you need to find multiple compelling use cases (of which 
> that example is not one), before fundamentally adding complexity to the 
> language. Remember that "nil" is already overloaded in Go, so adding 
> another (similar but different) meaning increases complexity for the reader.
>
> Full union types are a different thing again, and I can see the attraction 
> of those - except at that point, the language is no longer Go. Having 
> "int32 | int64" as a union type, but also as a generic type constraint, 
> would also be somewhat mind-boggling. How would you write a type constraint 
> that allows a union type?
>
> On Monday 18 March 2024 at 07:01:25 UTC Jan Mercl wrote:
>
>> On Mon, Mar 18, 2024 at 4:41 AM Daniel Lepage  wrote: 
>>
>> > This change would be entirely backward-compatible ... 
>>
>> Let's consider, for example, the type uint8, aka byte. A variable of 
>> type byte is specified* to occupy 8 bits of memory and has 256 
>> possible values. To represent all those values and the new possibility 
>> of the value being nil, we need 257 distinct values. But that does not 
>> fit 8 in bits anymore. However, changing the size and/or the bit 
>> representation of such a variable is observable, making it not 
>> backwards-compatible. 
>>
>>  
>> *: From https://go.dev/ref/spec#Numeric_types: 
>>
>>  
>> The value of an n-bit integer is n bits wide and represented using 
>> two's complement arithmetic. 
>>  
>>
>

-- 
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/e45d298f-154d-49c1-9316-73df4f250543n%40googlegroups.com.


Re: [go-nuts] Nillable basic types?

2024-03-18 Thread 'Brian Candler' via golang-nuts
I like Go because it's relatively simple and low-level, like C, except with 
things like garbage collection and channels integrated.  If Go were to have 
"| nil" types then the internal representation of such variables would have 
to be something like this:

type Maybe[T any] struct {
Value T
IsSet bool
}

In which case, why not just use that form explicitly? The code you write is 
then clear and obvious. Taking one of your examples:

func foo3(x Maybe[int]) {
  if x.IsSet {
fmt.Println("x squared is", x.Value*x.Value)
  } else {
fmt.Println("No value for x")
  }
}

// https://go.dev/play/p/ci10fhU1zqL

I also think that you need to find multiple compelling use cases (of which 
that example is not one), before fundamentally adding complexity to the 
language. Remember that "nil" is already overloaded in Go, so adding 
another (similar but different) meaning increases complexity for the reader.

Full union types are a different thing again, and I can see the attraction 
of those - except at that point, the language is no longer Go. Having 
"int32 | int64" as a union type, but also as a generic type constraint, 
would also be somewhat mind-boggling. How would you write a type constraint 
that allows a union type?

On Monday 18 March 2024 at 07:01:25 UTC Jan Mercl wrote:

> On Mon, Mar 18, 2024 at 4:41 AM Daniel Lepage  wrote:
>
> > This change would be entirely backward-compatible ...
>
> Let's consider, for example, the type uint8, aka byte. A variable of
> type byte is specified* to occupy 8 bits of memory and has 256
> possible values. To represent all those values and the new possibility
> of the value being nil, we need 257 distinct values. But that does not
> fit 8 in bits anymore. However, changing the size and/or the bit
> representation of such a variable is observable, making it not
> backwards-compatible.
>
> 
> *: From https://go.dev/ref/spec#Numeric_types:
>
> 
> The value of an n-bit integer is n bits wide and represented using
> two's complement arithmetic.
> 
>

-- 
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/a1e89e97-e879-4bc8-9110-50b8432767bdn%40googlegroups.com.


Re: [go-nuts] Nillable basic types?

2024-03-18 Thread Jan Mercl
On Mon, Mar 18, 2024 at 4:41 AM Daniel Lepage  wrote:

> This change would be entirely backward-compatible ...

Let's consider, for example, the type uint8, aka byte. A variable of
type byte is specified* to occupy 8 bits of memory and has 256
possible values. To represent all those values and the new possibility
of the value being nil, we need 257 distinct values. But that does not
fit 8 in bits anymore. However, changing the size and/or the bit
representation of such a variable is observable, making it not
backwards-compatible.


*: From https://go.dev/ref/spec#Numeric_types:


The value of an n-bit integer is n bits wide and represented using
two's complement arithmetic.


-- 
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/CAA40n-XCMSmhEDGL04NfhOVpexG3GCDA0Vj2dq138pWJVSBnwA%40mail.gmail.com.


[go-nuts] Nillable basic types?

2024-03-17 Thread Daniel Lepage
Hey everyone, I've been thinking for a while now about the advantages of 
nillable types, especially for basic types, and I'm interested in making an 
official language proposal. I'd love to know what the rest of the community 
thinks about it, and if this discussion has been had before (I found some 
vaguely similar past proposals but they were all specifically about making 
pointers easier to use, which is not the same thing).

Thanks,
Dan



# Proposal: Nillable types

## Summary

I propose that for any type T, the type `T | nil` should also be a valid 
type with the following properties:
1. The zero value of a `T | nil` is `nil`.
2. Any `T` or `nil` may be used as a value of `T | nil`
3. It is a compile-time error to use a `T | nil` as a `T` without first 
checking that it is non-nil.

So basically, you should be able to write code like

```go

func Foo(x int32 | nil) {
  if x != nil {
fmt.Println("X squared is", x*x)
  } else {
fmt.Println("X is undefined")
  }
}
```

## Motivation

There are loads of examples out there (I can dig up a bunch if it would be 
helpful) where various go programs need values that can take some "unset" 
value that is distinct from the zero value of that type. Examples include 
configuration objects, command line flags, and quite a lot of large 
structured data types (protobuf used to have every field be a pointer for 
this reason, though in v3 they got rid of this in their go implementation 
because it was so frustrating to do, and now go programs just can't 
distinguish unset values from zero values in protobufs).

### Alternatives and why they're bad

There are three common ways I've seen of dealing with this in Go, and they 
all have serious limitations:

 Pointers

The variable `var x *int32` allows you to distinguish between `x == nil` 
and `*x == 0`. However, using a pointer has three major drawbacks:

1. No compile-time protection

Go does not force developers to put nil guards on pointers, so nothing will 
stop you from dereferencing `x` without checking if it's nil. I've lost 
count of the number of times I've seen code that panics in corner cases 
because someone assumed that some pointer would always be set.

2. Passed values are mutable

If I want a function that can take an integer or an unset marker, and I 
define it as `func foo(x *int32)`, then users of my library just have to 
trust me that calling `foo(_var)` won't change the value of 
`some_var`; this breaks an important encapsulation boundary.

3. No literals

You can't take the address of a literal, so instead of simply writing e.g. 
`x = 3`, you have to either use a temporary variable:
```
tmp := 3
x = 
```
or write a helper function that does this for you. Numerous libraries have 
implemented these helper functions - for example, both 
https://pkg.go.dev/github.com/openconfig/ygot/ygot and 
https://pkg.go.dev/github.com/golang/protobuf/proto define helpers named 
`Bool`, `Float32`, `Float64`, `Int`, `Int32`, etc. just so that you can 
write
```
x = proto.Int32(3)
```
But this makes code a lot more cumbersome to read, and also requires the 
developer to restate the type of `x` every time (as compared to `x = 3`, 
where Go will infer that `3` means `int32(3)` based on the type of `x`).

[Sourcegraph finds more than 10k results for functions that just take an 
int and return a pointer to 
it](https://sourcegraph.com/search?q=context:global+/func+Int%5C%28%5Cw%2B+int%5C%29+%5C*int/+lang:go=keyword=0)
 
so this is coming up A LOT.

 Sentinel values

Some code uses explicit "impossible" values to indicate unset. For example, 
a nonegative value might have type `int` and be set to -1 to indicate that 
it is unset. However, this fails the criteria that the zero value for the 
type should be unset. It also requires that every function using this value 
check for -1 before using the value, and the compiler cannot enforce that 
this check has been made.

Furthermore, this requires you to use a broader type than the type you 
actually care about, which may be impossible (e.g. if the value can be any 
float) or extremely unwieldy (e.g. if you have to use an integer to 
represent a bool).

 An additional bool

You can also approximate this by using a struct like

```go
struct {
  X int32
  IsZero bool
}
```

The zero value for this struct has `IsZero=false`, so you can use that to 
determine that `X` is not explicitly 0, but is in fact unset. However, this 
is confusing (what does it mean if IsZero is true but X is not 0?) and 
awkward (you have to remember to set IsZero any time you set X to 0, and to 
check IsZero any time you want to read X), and again, the compiler will not 
complain if you fail to do these.

An example of using BOTH a sentinel value AND an additional bool in golang 
itself is: 
https://github.com/golang/go/blob/68d3a9e417344c11426f158c7a6f3197a0890ff1/src/crypto/x509/x509.go#L724
 
. The `MaxPathLen` value is considered "unset" if it's set to -1 OR if it's 
set to 0 and