Re: [go-nuts] Add comparisons to all types

2022-05-03 Thread Ian Lance Taylor
On Mon, May 2, 2022 at 11:32 PM Will Faught  wrote:
>>
>> There are cases involving closures, generated trampolines, late
>> binding and other details that mean that doing this will either
>> eliminate many optimization possibilities or restrict the compiler too
>> much or cause surprising results. We disabled function comparison for
>> just these reasons. It used to work this way, but made closures
>> surprising, so we backed out and allow comparison only to nil.
>
>
> That's interesting. I didn't know that. :)
>
> When I run:
>
> ```
> func f() {
> x := func() {}
> y := func() {}
> fmt.Printf("%#v %#v %#v %#v\n", x, y, func() {}, func() {})
> }
>
> func g() {}
>
> func main() {
> fmt.Printf("%#v %#v %#v %#v\n", f, g, func() {}, func() {})
> f()
> }
> ```
>
> I get:
>
> ```
> (func())(0x108ac80) (func())(0x108ad40) (func())(0x108ad60) 
> (func())(0x108ad80)
> (func())(0x108ac00) (func())(0x108ac20) (func())(0x108ac40) 
> (func())(0x108ac60)
> ```
>
> I don't know where those integer values are coming from, but those are what I 
> meant by memory addresses. They seem to be unique per function value. Can't 
> the runtime calculate those same values for comparisons?

Yes, it can.  That's not the issue.  The issue is that whether those
pointer values are equal for two func values is not intuitive at the
language level.  When using shared libraries (-buildmode=shared) you
can get two different pointer values for references to the same
function.  When using method values you can get the same pointer value
for references to method values of different expressions of the same
type.  When using closures you will sometimes get the same value,
sometimes different values, depending on the implementation and the
escape analysis done by the compiler.



> The point isn't to provide equivalence operations; it's to provide useful 
> comparison operations that are consistent with the other types' comparison 
> operations, to make all types consistent and simplify the language. We could 
> provide a separate equivalence operation, perhaps something like `===` that 
> behaves like `reflect.DeepEquals`, but that's a separate issue. Shallow slice 
> comparisons do allow you to conclude that elements are equal if slices 
> compare equal, and we can still iterate slices manually to compare elements.

It's important that Go operators be intuitive for programmers.  For
example, many new Java programmers find that the == operator for
strings is not intuitive in Java.  What is people's intuition for
slice equality?  I think that different people make different
assumptions.  Not supporting the == operators ensures that nobody gets
confused.

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


[go-nuts] Refactor to use generics to have a single Make

2022-05-03 Thread sbezverk
Hello,

 

I am starting to learn generics and I was wondering if the following code could 
be refactored to use genercis, in order to avoid using per type Make() method.

 

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

 

I have 3 identical structs, they are defined with different types and used in 
other types by their unique types. The function to create all 3 types can be 
consolidated as the actual structure of is the same.

 

I am curious it would be possible to leverage generics for this. I have made an 
attempt but I still need to use per type Make method.

 

Thank you and appreciate a lot your help.

 

Serguei

-- 
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/D41C14EF-35D1-434F-A719-1E8C88D90C0F%40gmail.com.


[go-nuts] Is introducing ... to parameter list a breaking change? How is it considered in the proposal process?

2022-05-03 Thread Changkun Ou
Hi gophers,

I wonder how the Go project defines a breaking change, specifically for the 
case where we have an existing API but want to add ... to its parameter 
list for customizations.

For instance, we have an API:

package foo
func Bar() {}

And the current code uses this function as:

foo.Bar()

Now, we added a ... to the Bar() and having a new function signature:

func Bar(args ...any) {}

Technically, the language allows the existing users to continue to work:

foo.Bar() // still OK.

As long as we don't change how Bar() behaves by default, it seems that the 
code that previously used foo.Bar() is still considered valid and not 
breaking. Is introducing this type of API signature change considered a 
breaking change in the standard library?
What if we propose API changes like this to the existing standard library, 
will it be considered differently compared to an actual breaking change?

Thanks!
Changkun

-- 
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/9e9c58f0-7a79-4c43-9389-72f8d2954080n%40googlegroups.com.


Re: [go-nuts] Is introducing ... to parameter list a breaking change? How is it considered in the proposal process?

2022-05-03 Thread 'Axel Wagner' via golang-nuts
Basically every change to any exported API is a breaking change
,
strictly speaking. For this case, a user might do
var f func() = pkg.Bar
Which would fail to compile if `pkg.Bar` gains variadic arguments. Similar
constructs can be done for any change to the type of any identifier.

That's why the Go 1 compatibility promise (and any notion of compatibility)
is essentially reduced to explicitly list the changes it considers allowed.
Best I know, it's

   - Adding a method to a type
   - Adding a field to a struct
   - Adding a new exported identifier

However, by extension, you can technically apply whatever compatibility
promises you like. It is not entirely uncommon to document that you reserve
the right to make certain changes - you could very well document that you
would not consider adding variadic parameters a breaking change, so users
of your package should not assume it doesn't happen and avoid constructs as
above (basically, storing `pkg.Bar` in any variable, unless it uses a short
variable declaration and the type of that variable doesn't matter).

On Tue, May 3, 2022 at 9:28 PM Changkun Ou  wrote:

> Hi gophers,
>
> I wonder how the Go project defines a breaking change, specifically for
> the case where we have an existing API but want to add ... to its parameter
> list for customizations.
>
> For instance, we have an API:
>
> package foo
> func Bar() {}
>
> And the current code uses this function as:
>
> foo.Bar()
>
> Now, we added a ... to the Bar() and having a new function signature:
>
> func Bar(args ...any) {}
>
> Technically, the language allows the existing users to continue to work:
>
> foo.Bar() // still OK.
>
> As long as we don't change how Bar() behaves by default, it seems that the
> code that previously used foo.Bar() is still considered valid and not
> breaking. Is introducing this type of API signature change considered a
> breaking change in the standard library?
> What if we propose API changes like this to the existing standard library,
> will it be considered differently compared to an actual breaking change?
>
> Thanks!
> Changkun
>
> --
> 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/9e9c58f0-7a79-4c43-9389-72f8d2954080n%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/CAEkBMfG4jbbvmRMoEOAs0VN5tAhJUEQeWXR68kgeuzsYCJSVoQ%40mail.gmail.com.


Re: [go-nuts] Is introducing ... to parameter list a breaking change? How is it considered in the proposal process?

2022-05-03 Thread Ian Lance Taylor
On Tue, May 3, 2022 at 12:28 PM Changkun Ou  wrote:
>
> I wonder how the Go project defines a breaking change, specifically for the 
> case where we have an existing API but want to add ... to its parameter list 
> for customizations.
>
> For instance, we have an API:
>
> package foo
> func Bar() {}
>
> And the current code uses this function as:
>
> foo.Bar()
>
> Now, we added a ... to the Bar() and having a new function signature:
>
> func Bar(args ...any) {}
>
> Technically, the language allows the existing users to continue to work:
>
> foo.Bar() // still OK.
>
> As long as we don't change how Bar() behaves by default, it seems that the 
> code that previously used foo.Bar() is still considered valid and not 
> breaking. Is introducing this type of API signature change considered a 
> breaking change in the standard library?
> What if we propose API changes like this to the existing standard library, 
> will it be considered differently compared to an actual breaking change?

In the standard library we would consider that a breaking change,
because it would break code like

var f = []func() { Bar }

You might be interested in Jonathan Amsterdam's Gophercon talk:
https://www.youtube.com/watch?v=JhdL5AkH-AQ

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/CAOyqgcW_AnnZbRoUVBH5tK_NzN4Paq%3D_p-BUXcb-Q33mYDamYw%40mail.gmail.com.


Re: [go-nuts] Refactor to use generics to have a single Make

2022-05-03 Thread Ian Lance Taylor
On Tue, May 3, 2022 at 12:06 PM sbezverk  wrote:
>

>
> I am starting to learn generics and I was wondering if the following code 
> could be refactored to use genercis, in order to avoid using per type Make() 
> method.
>
>
>
> https://go.dev/play/p/gE4Z6Zj19Is
>
>
>
> I have 3 identical structs, they are defined with different types and used in 
> other types by their unique types. The function to create all 3 types can be 
> consolidated as the actual structure of is the same.
>
>
>
> I am curious it would be possible to leverage generics for this. I have made 
> an attempt but I still need to use per type Make method.

There is no simple way to do this at present.  This is related to
https://go.dev/issue/48522 and https://go.dev/issue/51259.

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/CAOyqgcW-Wxdp_MyvjgqHfMLcGP_-XUwE-B6wenS3V5NuaY97hg%40mail.gmail.com.


Re: [go-nuts] Add comparisons to all types

2022-05-03 Thread Will Faught
I'm not sure we're on the same page in terminology. I meant shallow as
opposed to deep. E.g. pointer equality in terms of `==` vs.
`reflect.DeepEqual`. Unequal pointers can reference values that are
equivalent.

On Mon, May 2, 2022 at 11:58 PM Jan Mercl <0xj...@gmail.com> wrote:

> On Tue, May 3, 2022 at 8:32 AM Will Faught  wrote:
>
> > Just as pointer comparisons are shallow, so too are comparisons for
> types that contain pointers.
>
> Pointer comparisons are not shallow. Comparing two pointers compares
> the entire values. a == b and *a == *b compare different values but in
> both cases, and always, the entire values. (What concerns the
> semantics of the Go '==' operator, equality per se can be defined in
> many other ways.)
>

-- 
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/CAKbcuKhtQp1iSPP8BDYa3Zguw4rCgGTd6nCcV%3DTRK6%3DBunF3fg%40mail.gmail.com.


Re: [go-nuts] Refactor to use generics to have a single Make

2022-05-03 Thread 'Sean Liao' via golang-nuts
right now it only works if the structs are actually identical:
https://go.dev/play/p/tMG3SYG5fLN

- sean


On Tue, May 3, 2022 at 8:06 PM sbezverk  wrote:

> Hello,
>
>
>
> I am starting to learn generics and I was wondering if the following code
> could be refactored to use genercis, in order to avoid using per type
> Make() method.
>
>
>
> https://go.dev/play/p/gE4Z6Zj19Is
>
>
>
> I have 3 identical structs, they are defined with different types and used
> in other types by their unique types. The function to create all 3 types
> can be consolidated as the actual structure of is the same.
>
>
>
> I am curious it would be possible to leverage generics for this. I have
> made an attempt but I still need to use per type Make method.
>
>
>
> Thank you and appreciate a lot your help.
>
>
>
> Serguei
>
> --
> 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/D41C14EF-35D1-434F-A719-1E8C88D90C0F%40gmail.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/CAGabyPp9hirawRPWXNZ96psNaKrg8x0HPXNTM9DsH7cq1NNXDw%40mail.gmail.com.


Re: [go-nuts] Add comparisons to all types

2022-05-03 Thread will....@gmail.com


On Tuesday, May 3, 2022 at 12:30:47 AM UTC-7 axel.wa...@googlemail.com 
wrote:

> On Tue, May 3, 2022 at 8:32 AM Will Faught  wrote:
>
>> Can't the same argument be made for pointer comparisons?
>>
>
> I think what it comes down to is: Yes, this argument can be made for 
> pointers as well. But it would be more controversial. There is no 
> absolutely more/less confusing semantic. But, at least that's the argument, 
> it's less controversial for pointers to be compared as they are, than it 
> would be for slices.
>
>
I don't think controversy is a good counterargument. It's vague, 
unquantifiable, and subjective. I could easily claim the opposite, while 
also giving no proof. I would find the underlying reasons for the supposed 
controversy far more useful and productive. I understand that some people 
might expect a deep comparison, which is why I gave the Slice1000 example 
to explain the reasoning behind choosing a shallow comparison.

Just because there are two ways to do something, and people tend to lean 
different ways, doesn't mean we shouldn't pick a default way, and make the 
other way still possible. For example, the range operation can produce per 
iteration an element index and an element value for slices, but a byte 
index and a rune value for strings. Personally, I found the byte index 
counterintuitive, as I expected the value to count runes like slice 
elements, but upon reflection, it makes sense, because you can easily count 
iterations yourself to have both byte indexes and rune counts, but you 
can't so trivially do the opposite. Should we omit ranging over strings 
entirely just because someone, somewhere, somehow might have a minority 
intuition, or if something is generally counterintuitive, but still the 
best approach?

The best path is to pick the best way for the common case, and make the 
other way possible. If slices are compared shallowly, we can still compare 
them deeply ourselves, or with `reflect.DeepEqual`; but if slices are 
compared deeply, then we lose the ability to compare them shallowly when 
that's appropriate.
 

> For pointers there are essentially two ways to define comparisons: 1. You 
> compare the pointees, which can lead to bad results, because you can easily 
> build circular pointer structures, or 2. you compare the pointers, which is 
> easy to specify and well-defined. Note that even that leads to ambiguities 
> which sometimes (but relatively rarely) come up - for example, the spec 
> doesn't say if new(struct{}) == new(struct{}). But apart from this rarely 
> important edge-case, it is easy to specify an unsurprising pointer 
> comparison.
>
>
As a tangent, I don't understand why this wasn't made unambiguous in the 
language spec. Why not have `new(struct{})` always allocate a new pointer? 
Who's allocating all these empty structs on the heap where this is 
something that needs to be optimized for? Is that really worth complicating 
the language? 🤔

I would argue this isn't really a deficiency with pointer comparisons, but 
rather with `new`. If `new(struct{}) == new(struct{})` is true, then they 
point to the same value in memory; that's all it means. Pointer comparisons 
are still valid in that case, it's just that the behavior of `new` can vary.
 

> For slices, even with your definition, there are questions. For example, 
> should s[0:0] == s[0:0:0], for non-empty slices? That is, should capacity 
> matter? Should make([]T, 0) == make([]T, 0)? That is, what if the "pointer" 
> in the slice header doesn't actually mean anything, as the slice has 
> capacity 0?
>
>
I specified slice comparisons like this:

> Slices: Compare the corresponding `runtime.slice` (non-pointer struct) 
values. The time complexity is constant.

For those unfamiliar, `runtime.slice` is implemented 

 
like this:

```
type slice struct {
array unsafe.Pointer
len   int
cap   int
}
```

It contains the array pointer, the length, and the capacity. Doing a 
shallow comparison of `runtime.slice` will do a comparison of only 
`unsafe.Pointer`, `int`, and `int`. If any of those three fields are 
different, the slices won't compare as equal. This is explicitly 
demonstrated in the examples:

```
var S1 = make([]int, 2)
var S2 = make([]int, 2)

var _ = S1 == S1 // True
var _ = S1 != S2 // True

var _ = S1 == S1[:] // True because the lengths, capacities, and pointers 
are equal
var _ = S1 != S1[:1] // True because the lengths aren't equal
var _ = S1[:1] != S1[:1:1] // True because the capacities aren't equal
var _ = S1 != append(S1, 0)[:2:2] // True because the pointers aren't equal
```

The last four lines relate to the fields of `runtime.slice`.

The reason to include capacity in comparisons, aside from it being 
convenient when doing comparisons, is that the capacity is an observable 
attribute of slices in regular code. Programmers are *encouraged* to reason 
about sl

Re: [go-nuts] Refactor to use generics to have a single Make

2022-05-03 Thread tapi...@gmail.com


On Wednesday, May 4, 2022 at 7:01:48 AM UTC+8 se...@liao.dev wrote:

> right now it only works if the structs are actually identical:
> https://go.dev/play/p/tMG3SYG5fLN
>
> - sean
>
>
Though accessing fields is still not allowed.
 

>
> On Tue, May 3, 2022 at 8:06 PM sbezverk  wrote:
>
>> Hello,
>>
>>  
>>
>> I am starting to learn generics and I was wondering if the following code 
>> could be refactored to use genercis, in order to avoid using per type 
>> Make() method.
>>
>>  
>>
>> https://go.dev/play/p/gE4Z6Zj19Is
>>
>>  
>>
>> I have 3 identical structs, they are defined with different types and 
>> used in other types by their unique types. The function to create all 3 
>> types can be consolidated as the actual structure of is the same.
>>
>>  
>>
>> I am curious it would be possible to leverage generics for this. I have 
>> made an attempt but I still need to use per type Make method.
>>
>>  
>>
>> Thank you and appreciate a lot your help.
>>
>>  
>>
>> Serguei
>>
>> -- 
>> 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/D41C14EF-35D1-434F-A719-1E8C88D90C0F%40gmail.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/4a99ffe9-8a38-45b7-ae6d-9c12ef6cf058n%40googlegroups.com.


Re: [go-nuts] Add comparisons to all types

2022-05-03 Thread Ian Lance Taylor
On Tue, May 3, 2022 at 6:08 PM Will Faught  wrote:
>
> My apologies, it seems that "reply all" in the Google Groups UI doesn't send 
> email to individuals like "reply all" in Gmail does, just the group. Response 
> copied below.
>
>> Yes, it can. That's not the issue. The issue is that whether those
>> pointer values are equal for two func values is not intuitive at the
>> language level. When using shared libraries (-buildmode=shared) you
>> can get two different pointer values for references to the same
>> function. When using method values you can get the same pointer value
>> for references to method values of different expressions of the same
>> type. When using closures you will sometimes get the same value,
>> sometimes different values, depending on the implementation and the
>> escape analysis done by the compiler.
>
>
> Interesting. This certainly differs from how I pictured functions working 
> (more like C function pointers with extra steps). I'd be curious to know more 
> about the details. Do you know if that's documented somewhere?

I'm not aware of any specific documentation on the topic, sorry.

The fact that C function pointers are guaranteed to compare equal can
actually be a performance hit at program startup for dynamically
linked C programs.  I wrote up the details of the worst case several
years ago at https://www.airs.com/blog/archives/307.  There are other
lesser issues.


> Just curious, what would be the cost if things were rejiggered under the hood 
> to make function comparisons work? Would any language features be impossible, 
> or would it be worse compiler/runtime complexity/performance, or both?

Regardless of compiler/runtime issues, this would introduce language
complexity, similar to the issues with slices.  We would have to
precisely specify when two func values are equal and when they are
not.  There is no intuitive answer to that.

Does a program like this print true or false?

func F() func() int { return func() int { return 0 } }
func G() { fmt.Println(F() == F()) }

What about a program like this:

func H(i int) func() *int { return func() *int { return &i } }
func J() { fmt.Println(H(0) == H(1)) }

Whatever we define for cases like this some people will be ready to
argue for a different choice.  The costs of forcing a decision exceed
the benefits.


> Regarding expectations, many new Java programmers came from JavaScript (like 
> myself), so the confusion is understandable, but it's not something that 
> necessarily needs to be considered. Arguably, old Java programmers would find 
> `==` confusing for structs, since it doesn't compare references. Bad 
> assumptions are best prevented by proper education and training, not by 
> omitting language features. Wrong expectations aren't the same as foot-guns.

I don't agree.  Unexpected behavior is a footgun.  Go is intended to
be a simple language.  When special explanation is required, something
has gone wrong.

In saying this I don't at all claim that Go is perfect.  There are
places where we made mistakes.  But I don't think that our decision to
not define == on slices or functions is one of them.


> >Just because there are two ways to do something, and people tend to lean 
> >different ways, doesn't mean we shouldn't pick a default way, and make the 
> >other way still possible. For example, the range operation can produce per 
> >iteration an element index and an element value for slices, but a byte index 
> >and a rune value for strings. Personally, I found the byte index 
> >counterintuitive, as I expected the value to count runes like slice 
> >elements, but upon reflection, it makes sense, because you can easily count 
> >iterations yourself to have both byte indexes and rune counts, but you can't 
> >so trivially do the opposite. Should we omit ranging over strings entirely 
> >just because someone, somewhere, somehow might have a minority intuition, or 
> >if something is generally counterintuitive, but still the best approach?

The fact that range over []byte and string are different may well have
been a mistake.  It can certainly be convenient, but it trips people
up.  (Certainly others may disagree with me on this.)


> >The best path is to pick the best way for the common case, and make the 
> >other way possible

I do not agree.  The best path is to make no choice, and force the
program writer to be explicit about what they want.

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/CAOyqgcV3PCVT11Vk0tzgVoRGTv%3DYUqDekHeY4taFyAx5%3DCD1bQ%40mail.gmail.com.


Re: [go-nuts] Add comparisons to all types

2022-05-03 Thread Bakul Shah
On May 3, 2022, at 7:27 PM, Ian Lance Taylor  wrote:
> 
> Does a program like this print true or false?
> 
> func F() func() int { return func() int { return 0 } }
> func G() { fmt.Println(F() == F()) }
> 
> What about a program like this:
> 
> func H(i int) func() *int { return func() *int { return &i } }
> func J() { fmt.Println(H(0) == H(1)) }

Note that in Scheme eq? works for functions as one would expect.

> (define (f (lambda (x) x))
> (eq? f f)) => #t
> (define g f)
> (eq? f g) => #t

But
> (eq? f (lambda (x) x)) => #f
> (define g (lambda () (lambda (x) x)))
> (eq? (g) (g)) => #f

One can make the case that each closure would be a fresh instance.
This is more clear with a slightly more complex version:

(define (counter m) (let ((n m) (lambda () (set! n (+ n 1)) n))

And equal? is unspecified in the Scheme RnRS but would typically implemented
to return #f.

> (equal? (lambda (x) x) (lambda (x) x)) => #f

Technically (lambda (x) x) & (lambda (y) y) behave identically but
proving the more general case of this in even an interpreter would
be hard to impossible.

Go pretty much has the same considerations (but for a more complex
data model).


-- 
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/2E7C1835-40C5-4FF3-BB7B-D32A71EE89EC%40iitbombay.org.


Re: [go-nuts] Add comparisons to all types

2022-05-03 Thread 'Axel Wagner' via golang-nuts
On Wed, May 4, 2022 at 1:40 AM will@gmail.com 
wrote:

> I don't think controversy is a good counterargument. It's vague,
> unquantifiable, and subjective. I could easily claim the opposite, while
> also giving no proof.
>

Sure. It was not intended to be an argument. It was intended to be an
explanation.
I can't proof to you whether or not it is a good idea to design the
language as it is. I can only try to explain why it was.


> Just because there are two ways to do something, and people tend to lean
> different ways, doesn't mean we shouldn't pick a default way, and make the
> other way still possible. For example, the range operation can produce per
> iteration an element index and an element value for slices, but a byte
> index and a rune value for strings. Personally, I found the byte index
> counterintuitive, as I expected the value to count runes like slice
> elements, but upon reflection, it makes sense, because you can easily count
> iterations yourself to have both byte indexes and rune counts, but you
> can't so trivially do the opposite. Should we omit ranging over strings
> entirely just because someone, somewhere, somehow might have a minority
> intuition, or if something is generally counterintuitive, but still the
> best approach?
>

I think this is an interesting example, in that I've thought a couple of
times in the past that it might have been a mistake to introduce the
current semantics for strings. I think there are good arguments to make
ranging over strings behave as ranging over []byte and consequently, there
are arguments that we maybe should not have allowed either.

But the language is, at is is and we can't break compatibility.


> The best path is to pick the best way for the common case, and make the
> other way possible. If slices are compared shallowly, we can still compare
> them deeply ourselves, or with `reflect.DeepEqual`
>

FTR, I don't think I ever found reflect.DeepEqual to give the semantics I
want, when it comes to slices. In particular, it considers nil and empty
slices to be different. Which is the right decision to make, probably, but
it is almost never the semantics I want. Which is why I don't use
reflect.DeepEqual, but use go-cmp, which gives me the option to configure
that.

Note that it is totally possible to make comparable versions of slices as a
library now . So at least the "make
other ways possible" part is now done, with whatever semantics you want.

As a tangent, I don't understand why this wasn't made unambiguous in the
> language spec. Why not have `new(struct{})` always allocate a new pointer?
> Who's allocating all these empty structs on the heap where this is
> something that needs to be optimized for? Is that really worth complicating
> the language? 🤔
>

I don't know why that decision was made. I do believe there are some less
obvious cases, where you at least have to add special casing in the
implementation (e.g. make([]T, x) would have to check at runtime if x is
0). But I agree that it would probably be okay to change the spec here.


> I would argue this isn't really a deficiency with pointer comparisons, but
> rather with `new`. If `new(struct{}) == new(struct{})` is true, then they
> point to the same value in memory; that's all it means. Pointer comparisons
> are still valid in that case, it's just that the behavior of `new` can vary.
>

Sure. That seems to be a distinction without a difference to me. Note that
I didn't say it's a deficiency, quite the opposite. I said that
pointer-comparisons work just fine, as they have (mostly) unambiguous and
intuitive semantics. But the same is not true for slices and maps.


>
>
>> For slices, even with your definition, there are questions. For example,
>> should s[0:0] == s[0:0:0], for non-empty slices? That is, should capacity
>> matter? Should make([]T, 0) == make([]T, 0)? That is, what if the "pointer"
>> in the slice header doesn't actually mean anything, as the slice has
>> capacity 0?
>>
>>
> I specified slice comparisons like this:
>
> > Slices: Compare the corresponding `runtime.slice` (non-pointer struct)
> values. The time complexity is constant.
>

Yes. I understand what you suggested and I understood how it *would* work,
if implemented that way. But why is that the best way to compare them?
Doing it that way has a bunch of semantic implications, some of which are
perhaps counterintuitive, which I tried to mention.

Note that the language doesn't mention "runtime.slice", BTW. So, even if we
did it that way, we would have to phrase it as "two slices are equal, if
they point to the same underlying array and have the same length and
capacity", or somesuch. This would still not define whether make([]T, 0) ==
make([]T, 0), though.

So, even if we accepted that this was the "right" way to do it, it would
still leave at least one question open.


> I assume `make([]T, 0)` sets the array pointer to nil, because
> `reflect.DeepEqual` says two of those exp

Re: [go-nuts] Add comparisons to all types

2022-05-03 Thread Will Faught
On Tue, May 3, 2022 at 7:27 PM Ian Lance Taylor  wrote:

> On Tue, May 3, 2022 at 6:08 PM Will Faught  wrote:
> >
> > My apologies, it seems that "reply all" in the Google Groups UI doesn't
> send email to individuals like "reply all" in Gmail does, just the group.
> Response copied below.
> >
> >> Yes, it can. That's not the issue. The issue is that whether those
> >> pointer values are equal for two func values is not intuitive at the
> >> language level. When using shared libraries (-buildmode=shared) you
> >> can get two different pointer values for references to the same
> >> function. When using method values you can get the same pointer value
> >> for references to method values of different expressions of the same
> >> type. When using closures you will sometimes get the same value,
> >> sometimes different values, depending on the implementation and the
> >> escape analysis done by the compiler.
> >
> >
> > Interesting. This certainly differs from how I pictured functions
> working (more like C function pointers with extra steps). I'd be curious to
> know more about the details. Do you know if that's documented somewhere?
>
> I'm not aware of any specific documentation on the topic, sorry.
>
> The fact that C function pointers are guaranteed to compare equal can
> actually be a performance hit at program startup for dynamically
> linked C programs.  I wrote up the details of the worst case several
> years ago at https://www.airs.com/blog/archives/307.  There are other
> lesser issues.
>
>
Thanks! :)


>
> > Just curious, what would be the cost if things were rejiggered under the
> hood to make function comparisons work? Would any language features be
> impossible, or would it be worse compiler/runtime complexity/performance,
> or both?
>
> Regardless of compiler/runtime issues, this would introduce language
> complexity, similar to the issues with slices.  We would have to
> precisely specify when two func values are equal and when they are
> not.  There is no intuitive answer to that.
>
> Does a program like this print true or false?
>
> func F() func() int { return func() int { return 0 } }
> func G() { fmt.Println(F() == F()) }
>
>
It would print false, because the function literal creates a new allocation
(according to the rule I sketched out). I can see the desire to optimize
that, but personally when I write code like that, I'm thinking, "and then
return a new function." Causing an allocation isn't surprising behavior
here, and so neither is uniqueness in terms of comparisons.


> What about a program like this:
>
> func H(i int) func() *int { return func() *int { return &i } }
> func J() { fmt.Println(H(0) == H(1)) }
>
>
It would print false for the same reason.


> Whatever we define for cases like this some people will be ready to
> argue for a different choice.  The costs of forcing a decision exceed
> the benefits.
>

So on the balance, the cost of making a decision is worth it for something
big like dependencies or generics, but not function equality. Well, I guess
that's fair enough, but it seems like one could use that kind of argument
to undermine any language change, though, including dependencies and
generics. It doesn't seem like the function equality rule I sketched out
would add much, if any, language complexity. It's only one sentence:
"Function values are equal if they were created by the same function
literal or declaration."

> Regarding expectations, many new Java programmers came from JavaScript
> (like myself), so the confusion is understandable, but it's not something
> that necessarily needs to be considered. Arguably, old Java programmers
> would find `==` confusing for structs, since it doesn't compare references.
> Bad assumptions are best prevented by proper education and training, not by
> omitting language features. Wrong expectations aren't the same as foot-guns.
>
> I don't agree.  Unexpected behavior is a footgun.


I wrote that wrong expectations aren't foot-guns, not that unexpected
behaviors aren't foot-guns. Wrong expectations, as in "I can call this
pointer-receiver method on this unaddressable value," or "since zero values
are useful, I can set keys and values in this zero-value map." Downloading
a compiler for some new language I haven't bothered to learn, typing in
Java-like stuff, and then being upset when it doesn't work isn't a problem
of unexpected behavior, it's a problem of wrong expectations (usually
misunderstandings or ignorance).


> Go is intended to
> be a simple language.  When special explanation is required, something
> has gone wrong.
>
>
The Go language spec is *full* of special explanations. The section on
comparisons  is quite
detailed and complicated. I recently had to ask here why the range
operation doesn't work for type set unions of slices and maps, which you
very kindly answered, if I remember correctly. How is slice equality
different in terms of special explanation?

I've argued t

Re: [go-nuts] Add comparisons to all types

2022-05-03 Thread 'Axel Wagner' via golang-nuts
As for documentation for how `func` works BTW: This design doc for Go 1.1
is a good description

and AFAIK mostly up to date. It doesn't mention how inlining decisions and
dynamic linking affect the pointer values, though. That you would have to
derive from first principles.

On Wed, May 4, 2022 at 7:58 AM Axel Wagner 
wrote:

> On Wed, May 4, 2022 at 1:40 AM will@gmail.com 
> wrote:
>
>> I don't think controversy is a good counterargument. It's vague,
>> unquantifiable, and subjective. I could easily claim the opposite, while
>> also giving no proof.
>>
>
> Sure. It was not intended to be an argument. It was intended to be an
> explanation.
> I can't proof to you whether or not it is a good idea to design the
> language as it is. I can only try to explain why it was.
>
>
>> Just because there are two ways to do something, and people tend to lean
>> different ways, doesn't mean we shouldn't pick a default way, and make the
>> other way still possible. For example, the range operation can produce per
>> iteration an element index and an element value for slices, but a byte
>> index and a rune value for strings. Personally, I found the byte index
>> counterintuitive, as I expected the value to count runes like slice
>> elements, but upon reflection, it makes sense, because you can easily count
>> iterations yourself to have both byte indexes and rune counts, but you
>> can't so trivially do the opposite. Should we omit ranging over strings
>> entirely just because someone, somewhere, somehow might have a minority
>> intuition, or if something is generally counterintuitive, but still the
>> best approach?
>>
>
> I think this is an interesting example, in that I've thought a couple of
> times in the past that it might have been a mistake to introduce the
> current semantics for strings. I think there are good arguments to make
> ranging over strings behave as ranging over []byte and consequently, there
> are arguments that we maybe should not have allowed either.
>
> But the language is, at is is and we can't break compatibility.
>
>
>> The best path is to pick the best way for the common case, and make the
>> other way possible. If slices are compared shallowly, we can still compare
>> them deeply ourselves, or with `reflect.DeepEqual`
>>
>
> FTR, I don't think I ever found reflect.DeepEqual to give the semantics I
> want, when it comes to slices. In particular, it considers nil and empty
> slices to be different. Which is the right decision to make, probably, but
> it is almost never the semantics I want. Which is why I don't use
> reflect.DeepEqual, but use go-cmp, which gives me the option to configure
> that.
>
> Note that it is totally possible to make comparable versions of slices as
> a library now . So at least the "make
> other ways possible" part is now done, with whatever semantics you want.
>
> As a tangent, I don't understand why this wasn't made unambiguous in the
>> language spec. Why not have `new(struct{})` always allocate a new pointer?
>> Who's allocating all these empty structs on the heap where this is
>> something that needs to be optimized for? Is that really worth complicating
>> the language? 🤔
>>
>
> I don't know why that decision was made. I do believe there are some less
> obvious cases, where you at least have to add special casing in the
> implementation (e.g. make([]T, x) would have to check at runtime if x is
> 0). But I agree that it would probably be okay to change the spec here.
>
>
>> I would argue this isn't really a deficiency with pointer comparisons,
>> but rather with `new`. If `new(struct{}) == new(struct{})` is true, then
>> they point to the same value in memory; that's all it means. Pointer
>> comparisons are still valid in that case, it's just that the behavior of
>> `new` can vary.
>>
>
> Sure. That seems to be a distinction without a difference to me. Note that
> I didn't say it's a deficiency, quite the opposite. I said that
> pointer-comparisons work just fine, as they have (mostly) unambiguous and
> intuitive semantics. But the same is not true for slices and maps.
>
>
>>
>>
>>> For slices, even with your definition, there are questions. For example,
>>> should s[0:0] == s[0:0:0], for non-empty slices? That is, should capacity
>>> matter? Should make([]T, 0) == make([]T, 0)? That is, what if the "pointer"
>>> in the slice header doesn't actually mean anything, as the slice has
>>> capacity 0?
>>>
>>>
>> I specified slice comparisons like this:
>>
>> > Slices: Compare the corresponding `runtime.slice` (non-pointer struct)
>> values. The time complexity is constant.
>>
>
> Yes. I understand what you suggested and I understood how it *would* work,
> if implemented that way. But why is that the best way to compare them?
> Doing it that way has a bunch of semantic implications, some of which are
> perhaps counterintuitive, which I tried to me

Re: [go-nuts] Is introducing ... to parameter list a breaking change? How is it considered in the proposal process?

2022-05-03 Thread Changkun Ou
Hi Axel and Ian,

Thanks for the elaborative answer! I was too naive while considering the 
case and didn't observe the existing practices well. Assignability seems to 
create a lot more complicated compatibility issues.

Regarding the compatibility promises to users, it always seems the 
interpretation of developers and users is less effective in this area. I 
wonder what might be a possible way to involve a successful impact here. Is 
there any old practice that exists? Especially in the Go project itself :)

Also, the fantastic talk by Jonathan illustrates and saves me potentially a 
lot of time from a very early attempt to tackle the problem once again.

Changkun

On Tuesday, May 3, 2022 at 9:38:35 PM UTC+2 Ian Lance Taylor wrote:

> On Tue, May 3, 2022 at 12:28 PM Changkun Ou  wrote:
> >
> > I wonder how the Go project defines a breaking change, specifically for 
> the case where we have an existing API but want to add ... to its parameter 
> list for customizations.
> >
> > For instance, we have an API:
> >
> > package foo
> > func Bar() {}
> >
> > And the current code uses this function as:
> >
> > foo.Bar()
> >
> > Now, we added a ... to the Bar() and having a new function signature:
> >
> > func Bar(args ...any) {}
> >
> > Technically, the language allows the existing users to continue to work:
> >
> > foo.Bar() // still OK.
> >
> > As long as we don't change how Bar() behaves by default, it seems that 
> the code that previously used foo.Bar() is still considered valid and not 
> breaking. Is introducing this type of API signature change considered a 
> breaking change in the standard library?
> > What if we propose API changes like this to the existing standard 
> library, will it be considered differently compared to an actual breaking 
> change?
>
> In the standard library we would consider that a breaking change,
> because it would break code like
>
> var f = []func() { Bar }
>
> You might be interested in Jonathan Amsterdam's Gophercon talk:
> https://www.youtube.com/watch?v=JhdL5AkH-AQ
>
> 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/b4db9ee0-ee65-4f69-a51f-11ed736b43dan%40googlegroups.com.


Re: [go-nuts] Add comparisons to all types

2022-05-03 Thread Jan Mercl
On Wed, May 4, 2022 at 4:27 AM Ian Lance Taylor  wrote:

> In saying this I don't at all claim that Go is perfect.  There are
> places where we made mistakes.

May I please ask you to share what you personally consider a mistake
and, if possible, what would you change if you can, say, travel back
in time?

I believe this could be a benefit to anyone thinking about/designing
nowadays a programming language that is similar to Go and/or C etc.

Thanks,

-j

-- 
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-X79w2ynAOLM6Gd%3D-7XFpBCjtdp%3DjHNVTN7uD0e7s6BKA%40mail.gmail.com.


Re: [go-nuts] Add comparisons to all types

2022-05-03 Thread Jan Mercl
On Wed, May 4, 2022 at 12:15 AM Will Faught  wrote:

> I'm not sure we're on the same page in terminology. I meant shallow as 
> opposed to deep. E.g. pointer equality in terms of `==` vs. 
> `reflect.DeepEqual`. Unequal pointers can reference values that are 
> equivalent.

I think we are on the same page. This thread is about comparisons on
the language level, not about some library functions. Thus we can
ignore reflect.DeepEqual and focus on the equality operator only. That
makes things simpler.

Now, what I wanted to point out is that the equality operator, as
defined in Go, always compares the values of its operands. It's a
simple and easy to remember rule. It could have been defined in a
different way, sure. Anyway, it was not and talking about deep vs
shallow comparison of the Go equality operator does not make the
discussion any clearer - because it does not, in our case, apply.

-- 
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-VnGvnsuohiFVkLZ9JsB6ZyD-NNuGd8BDj5_haM_O7G7Q%40mail.gmail.com.


Re: [go-nuts] Add comparisons to all types

2022-05-03 Thread Will Faught
On Tue, May 3, 2022 at 10:59 PM 'Axel Wagner' via golang-nuts <
golang-nuts@googlegroups.com> wrote:

> On Wed, May 4, 2022 at 1:40 AM will@gmail.com 
> wrote:
>
>> I don't think controversy is a good counterargument. It's vague,
>> unquantifiable, and subjective. I could easily claim the opposite, while
>> also giving no proof.
>>
>
> Sure. It was not intended to be an argument. It was intended to be an
> explanation.
> I can't proof to you whether or not it is a good idea to design the
> language as it is. I can only try to explain why it was.
>
>
>> Just because there are two ways to do something, and people tend to lean
>> different ways, doesn't mean we shouldn't pick a default way, and make the
>> other way still possible. For example, the range operation can produce per
>> iteration an element index and an element value for slices, but a byte
>> index and a rune value for strings. Personally, I found the byte index
>> counterintuitive, as I expected the value to count runes like slice
>> elements, but upon reflection, it makes sense, because you can easily count
>> iterations yourself to have both byte indexes and rune counts, but you
>> can't so trivially do the opposite. Should we omit ranging over strings
>> entirely just because someone, somewhere, somehow might have a minority
>> intuition, or if something is generally counterintuitive, but still the
>> best approach?
>>
>
> I think this is an interesting example, in that I've thought a couple of
> times in the past that it might have been a mistake to introduce the
> current semantics for strings. I think there are good arguments to make
> ranging over strings behave as ranging over []byte and consequently, there
> are arguments that we maybe should not have allowed either.
>
> But the language is, at is is and we can't break compatibility.
>
>
>> The best path is to pick the best way for the common case, and make the
>> other way possible. If slices are compared shallowly, we can still compare
>> them deeply ourselves, or with `reflect.DeepEqual`
>>
>
> FTR, I don't think I ever found reflect.DeepEqual to give the semantics I
> want, when it comes to slices. In particular, it considers nil and empty
> slices to be different. Which is the right decision to make, probably, but
> it is almost never the semantics I want. Which is why I don't use
> reflect.DeepEqual, but use go-cmp, which gives me the option to configure
> that.
>
> Note that it is totally possible to make comparable versions of slices as
> a library now . So at least the "make
> other ways possible" part is now done, with whatever semantics you want.
>
> As a tangent, I don't understand why this wasn't made unambiguous in the
>> language spec. Why not have `new(struct{})` always allocate a new pointer?
>> Who's allocating all these empty structs on the heap where this is
>> something that needs to be optimized for? Is that really worth complicating
>> the language? 🤔
>>
>
> I don't know why that decision was made. I do believe there are some less
> obvious cases, where you at least have to add special casing in the
> implementation (e.g. make([]T, x) would have to check at runtime if x is
> 0). But I agree that it would probably be okay to change the spec here.
>
>
>> I would argue this isn't really a deficiency with pointer comparisons,
>> but rather with `new`. If `new(struct{}) == new(struct{})` is true, then
>> they point to the same value in memory; that's all it means. Pointer
>> comparisons are still valid in that case, it's just that the behavior of
>> `new` can vary.
>>
>
> Sure. That seems to be a distinction without a difference to me. Note that
> I didn't say it's a deficiency, quite the opposite. I said that
> pointer-comparisons work just fine, as they have (mostly) unambiguous and
> intuitive semantics. But the same is not true for slices and maps.
>
>
>>
>>
>>> For slices, even with your definition, there are questions. For example,
>>> should s[0:0] == s[0:0:0], for non-empty slices? That is, should capacity
>>> matter? Should make([]T, 0) == make([]T, 0)? That is, what if the "pointer"
>>> in the slice header doesn't actually mean anything, as the slice has
>>> capacity 0?
>>>
>>>
>> I specified slice comparisons like this:
>>
>> > Slices: Compare the corresponding `runtime.slice` (non-pointer struct)
>> values. The time complexity is constant.
>>
>
> Yes. I understand what you suggested and I understood how it *would* work,
> if implemented that way. But why is that the best way to compare them?
> Doing it that way has a bunch of semantic implications, some of which are
> perhaps counterintuitive, which I tried to mention.
>
>
I explained that in detail in the subsequent paragraphs.


> Note that the language doesn't mention "runtime.slice", BTW. So, even if
> we did it that way, we would have to phrase it as "two slices are equal, if
> they point to the same underlying array and have the same length and
> capacity", or so

Re: [go-nuts] Add comparisons to all types

2022-05-03 Thread Will Faught
Well, I agree! :) Comparisons should be shallow where possible for every
type, including slices and maps. That's my initial argument.

On Tue, May 3, 2022 at 11:22 PM Jan Mercl <0xj...@gmail.com> wrote:

> On Wed, May 4, 2022 at 12:15 AM Will Faught  wrote:
>
> > I'm not sure we're on the same page in terminology. I meant shallow as
> opposed to deep. E.g. pointer equality in terms of `==` vs.
> `reflect.DeepEqual`. Unequal pointers can reference values that are
> equivalent.
>
> I think we are on the same page. This thread is about comparisons on
> the language level, not about some library functions. Thus we can
> ignore reflect.DeepEqual and focus on the equality operator only. That
> makes things simpler.
>
> Now, what I wanted to point out is that the equality operator, as
> defined in Go, always compares the values of its operands. It's a
> simple and easy to remember rule. It could have been defined in a
> different way, sure. Anyway, it was not and talking about deep vs
> shallow comparison of the Go equality operator does not make the
> discussion any clearer - because it does not, in our case, apply.
>

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