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

2022-05-05 Thread Will Faught
The proposal was declined, so no need to respond. Some responses inline
below. Thanks so much for your feedback, Axel. :)



On Thu, May 5, 2022 at 11:39 AM 'Axel Wagner' via golang-nuts <
golang-nuts@googlegroups.com> wrote:

> On Thu, May 5, 2022 at 7:32 PM Will Faught  wrote:
>
>> Yeah, it's a trade-off. I can understand that it's not worth it.
>>
>
> I find it confusing to me that you seem to be willing to allow the
> existence of tradeoffs on the one hand. And on the other you treat most
> arguments as all-or-nothing questions. Like:
>
>
I acknowledged that a trade-off exists, not that I agreed that performance
outweighed all other considerations, although I would weigh performance
costs very highly. The point was moot since function values can have
multiple addresses.

In the email you're responding to, I explained that arguments are supposed
to balance conflicting principles. Performance vs. consistency vs.
simplicity vs etc. is an example.


> I think what you've put forth as counter arguments are instead
>> *principles*, like "simplicity is good," "complexity is bad," "changes
>> have cost," and so on. Principles aren't themselves arguments, so they
>> can't be used as premises in an argument.
>>
>> 1. Complexity is bad.
>> 2. This change increases complexity in one area.
>> Therefore, 3. This change is bad.
>>
>>
>> is not a good argument because "complexity is bad" would rule out all
>> changes, and it would be inconsistent with the past and recent behavior of
>> the Go Team.
>>
>
> "Complexity is bad" is indeed in the "contra" column for most changes.
> That doesn't rule them out though. It just means they have to justify their
> complexity.
>
> There are many, individual arguments at play here. Each one individually
> doesn't lead to the conclusion. And taking each one individually out of
> context and applying it in totality as the only deciding factor leads to
> ridiculous results like this. But *taken together*, they can paint a
> picture that the downsides of adding comparison for certain types outweigh
> their benefits. And that it's different for other types.
>
>
Sure. You have to weigh various aspects according to various principles,
and make a decision. The explicit, concrete weighing of these aspects is
what has been missing from the counter arguments. That's been my point.
"Complexity is bad" is useless on its own. "This would require XYZ changes
to the spec, PQR changes to the implementation, users would have to keep
ABC in their head now as opposed to DEF when having to roll their own
comparisons..." is an actual analysis of complexity, which is actually
tractable as a premise in an argument. "Users don't get compiler errors
when they try to compare slices, we don't have to maintain a FAQ entry
about it, we don't have to point users to it, they learn how it works when
they learn how slices work and how == works for all types, it's constant
time and clearly defined..." is something that can be weighed too. Now we
can try to pin down specific aspects or constraints that force the decision
one way or another, or feel out which adds up to more value, etc. It's not
a simple "my principle vs. your principle" slugout.


> Pointers and Slices have commonalities, true. But that doesn't mean "if
> you can compare pointers, you should be able to compare slices". They also
> have differences. And it's entirely reasonable, that the downsides for
> adding a comparison for slices do not apply for pointers, or apply to a
> lesser degree. And similar for benefits.
>
>
That sounds like it would have been a promising counter argument :),
although I wonder if it would have survived the point that Go programmers
are expected to learn what == does for each type.


> All that said, assuming you agree, what is your full counter argument, or
>> set of counter arguments, to my initial argument for slice and map
>> comparisons, shaped by the principles you've listed? It doesn't need to be
>> in a rigid numbered list format, but it should be obvious how your counter
>> arguments could logically fit into that format.
>>
>
> One argument goes roughly like
>
> 1. We believe most people would expect comparison of slices to compare the
> contents of slices/maps, not the "shallow" comparison you suggest¹.
>

Only insofar as they don't understand slices at all. If you know they point
to an array, and copying them around is a lightweight operation that
doesn't copy the actual array, then IMO it becomes natural to think of
slice comparisons working like they do for Slice1000. Hence why I used that
example.


> 2. Doing that has technical problems making it prohibitive (e.g. the
> existence of cyclic data structures).
>

Deep comparisons do, not shallow ones.


> 3. Even *if* we would add a "shallow" comparison instead, there are still
> open questions where it is hard to say what the "right" answer would be² .
>

Shallow comparisons :), for reasons like what you just listed, e.g. cyclic
data structures. Shallow 

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

2022-05-05 Thread Will Faught
Yeah, it would have performance implications, which is a good counter
argument. Regardless, it seems from the discussion that function values can
have multiple addresses, so it wouldn't work currently anyway.

On Thu, May 5, 2022 at 6:39 AM David Arroyo  wrote:

> On Mon, May 2, 2022, at 22:43, will@gmail.com wrote:
> >- Functions: Compare the corresponding memory addresses. The time
> >complexity is constant.
>
> How would this apply to inlined functions? supporting equality would
> essentially force the compiler to keep a the function in the output binary
> even if it's inlined everywhere it's called, just for comparisons. It would
> also complicate
>
> Here's another example:
>
> type worker struct { ... }
> func shutDown() { ... }
>
> func (w *worker) run(orders <-chan func()) {
> for f := range orders {
> if(f == shutDown) {
> log.Printf("got a shutdown command")
> w.Cleanup()
> }
> f()
> }
> }
>
> currently, Go programs run on a single computer. What if a Go runtime was
> built that ran Go programs across many computers? Or, put another way, what
> if a system architecture emerged where the instruction access time varied
> so drastically across CPU cores that it made sense to duplicate functions
> across cores' fast memory regions, so that the receive operation in the
> above example actually received a duplicate copy of a function? I will
> admit that closures with mutable data segments already complicate such an
> optimization, but function equality would thwart such an optimization
> altogether.
>
> David
>
> --
> You received this message because you are subscribed to a topic in the
> Google Groups "golang-nuts" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/golang-nuts/b-WtVh3H_oY/unsubscribe.
> To unsubscribe from this group and all its topics, 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/f566007f-aa47-4aaf-bc9e-a0fa9dab1e62%40www.fastmail.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/CAKbcuKgo_xhpiMjjW-ePQ0TFW2%2BM0iiSy5KDpQCBwoLinx2Vqw%40mail.gmail.com.


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

2022-05-05 Thread Will Faught
On Thu, May 5, 2022 at 2:53 AM Axel Wagner 
wrote:

> On Thu, May 5, 2022 at 3:11 AM Will Faught  wrote:
>
>> 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 slice capacity, so it should be included in comparisons. `cap(S1[:1])
>>> != cap(S1[:1:1])` is true, therefore `S1[:1] != S1[:1:1]` should be true,
>>> even though `len(S1[:1]) == len(S1[:1:1])` is true.
>>
>>
>> Do you agree that is a good thing, yes or no, and if not, why?
>>
>
> Sure.
>
>
>> This approach to comparisons for functions, maps, and slices makes all
>>> values of those types immutable, and therefore usable as map keys.
>>
>>
>> Do you agree that is a good thing, yes or no, and if not, why?
>>
>
> Sure.
>
> I wrote in the proposal an example of how slices work in actual Go code,
>> then asked:
>>
>> Do you expect `c` to be true? If not (it's false, by the way), then why
>>> would you expect `make([]int, 2) == make([]int, 2)` to be true?
>>
>>
>> What was your answer? Yes or no? This isn't rhetorical at this point, I'm
>> actually asking, so please answer unambiguously yes or no.
>>
>
> To be clear, demanding an unambiguous answer doesn't make a question
> unambiguous. If you'd ask "is light a wave or a particle, please answer yes
> or no", my response would be to stand up and leave the room, because there
> is no way to converse within the rules you are setting. So, if you insist
> on these rules, I will try my best to leave the room, metaphorically
> speaking and to write you off as impossible to have a conversation with.
>
> My position is that the comparison should be disallowed. Therefore, I
> can't answer yes or no.
>
> I think "both slides in the comparison contain the same elements in the
> same order" is a strong argument in favor of making the comparison be true.
> I think "this would make it possible for comparisons to hang the program"
> is a strong argument in favor of making the comparison be false.
> I think that the fact that there are strong arguments in favor of it being
> true and strong arguments in favor of it being false, is itself a strong
> argument in not allowing it.
>
> If your answer was yes, then you don't understand Go at a basic level.
>>
>
> Please don't say things like this. You don't know me well enough to judge
> my understanding of Go. If you did, I feel confident that you wouldn't say
> this. It is just a No True Scotsman fallacy
> at best and a baseless
> insult at worst.
>
>

The reason why I've been explicitly asking you and Ian whether you agree
with my points is because you've been ignoring or skipping over them in
your responses. The points I make in response to yours are meant to
synchronize us through agreement (if we agree), and ensure we are on the
same page. When you don't respond with something equivalent to "agree" or
"disagree because" to each point, it's easy to lose track of where each of
us is, and what ground is left to cover or explore. We're already 3-5
levels deep in email quotations at this point. Debate is unproductive and
pointless if we can't even agree on what an argument means.

I say all this because it's clear from what you've written here that you
fundamentally misunderstood my initial argument for why slice comparisons
should be shallow. I didn't write the Slice1000 example because I enjoy
typing, I wrote it because the synchronization forced by the question of
what `c` evaluates to ensures that you and I are on the same page of what
my argument means. The statement about not understanding Go at a basic
level was phrased very specifically to make it clear whether you understood
what I was saying. If something comes across as insulting, the odds are
good it's because *you* don't understand the point. The first thing we
should ask ourselves about an argument is, "Is this true?" The second is,
"How *can* this be true?" By ignoring the two questions about Slice1000 in
the initial argument, you might have constructed in your mind a strawman
argument, and been arguing against that ever since. If there's a single
sentence, a single *word*, in an argument that you don't understand, the
first step is to ask clarifying questions to understand it, not ignore it
and hope for the best. This entire time, I thought you had answered that
first question as no. I didn't start off requiring every initial response
to include the answers to those questions because I, you know, assumed
people would thoroughly read and understand the argument, and point out
basic comprehension problems with it, and otherwise base their responses on
it.

Again, this was in the initial argument:

If you think slice equality should incorporate element equality, here's an
example for you:

```
type Slice1000[T any] struct {
xs *[1000]T
len, cap int
}

func (s Slice1000[T]) 

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

2022-05-05 Thread Ian Lance Taylor
On Thu, May 5, 2022 at 10:32 AM Will Faught  wrote:
>
> I think what you've put forth as counter arguments are instead principles, 
> like "simplicity is good," "complexity is bad," "changes have cost," and so 
> on. Principles aren't themselves arguments, so they can't be used as premises 
> in an argument.

We are evidently completely failing to communicate.  I'm sorry, but I
don't see any point in continuing to engage in this discussion.


> In my humble opinion, I've seen the Go Team often use vague rationale. In my 
> humble opinion, it's why they were so wrong for over a decade about generics, 
> despite many, many people making the argument for adding generics over that 
> time. Even when generics were added, the Go Team only did it (according to 
> their statements that I've seen regarding this) due to a poll, not because of 
> any rational conclusion. It's a blind spot. I say this with love, because I 
> want Go to be the best it can be, and I support the Go Team's efforts to make 
> it so, but I believe the best way to get there is by the adversarial process 
> of argumentation, a battle of ideas, just like what is used in the U.S. 
> justice system to determine truth. In order for that to work, we have to have 
> a fair "fight," using arguments that can be deconstructed and judged/weighed 
> concretely.

I'm sorry, but your comments about why generics were added to Go are incorrect.


> If we can't agree on this, then there isn't any utility in debate. If you're 
> not interested in debate, then ultimately that's fine, but please make that 
> clear up front. The Go proposal process now specifies that proposals should 
> start as golang-nuts discussions, so that's why I'm here. This isn't intended 
> to be a casual discussion thread.
>
> All that said, assuming you agree, what is your full counter argument, or set 
> of counter arguments, to my initial argument for slice and map comparisons, 
> shaped by the principles you've listed? It doesn't need to be in a rigid 
> numbered list format, but it should be obvious how your counter arguments 
> could logically fit into that format.

I and others have already presented the counter-arguments.  Somebody
also pointed to an earlier discussion thread which made the same
counter-arguments.  The fact that you don't agree with those
counter-arguments does not mean that they haven't been presented.

Language design is not a matter of precise arguments.  It is a matter
of weighing costs and benefits.  If there were precise arguments for
language design, we would all be using the same language (or perhaps
one dynamically typed language, one statically typed language, etc.).

In my opinion, the benefits of permitting slice and map comparisons in
Go do not outweigh the costs.

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


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

2022-05-05 Thread 'Axel Wagner' via golang-nuts
On Thu, May 5, 2022 at 7:32 PM Will Faught  wrote:

> Yeah, it's a trade-off. I can understand that it's not worth it.
>

I find it confusing to me that you seem to be willing to allow the
existence of tradeoffs on the one hand. And on the other you treat most
arguments as all-or-nothing questions. Like:

I think what you've put forth as counter arguments are instead *principles*,
> like "simplicity is good," "complexity is bad," "changes have cost," and so
> on. Principles aren't themselves arguments, so they can't be used as
> premises in an argument.
>
> 1. Complexity is bad.
> 2. This change increases complexity in one area.
> Therefore, 3. This change is bad.
>
>
> is not a good argument because "complexity is bad" would rule out all
> changes, and it would be inconsistent with the past and recent behavior of
> the Go Team.
>

"Complexity is bad" is indeed in the "contra" column for most changes. That
doesn't rule them out though. It just means they have to justify their
complexity.

There are many, individual arguments at play here. Each one individually
doesn't lead to the conclusion. And taking each one individually out of
context and applying it in totality as the only deciding factor leads to
ridiculous results like this. But *taken together*, they can paint a
picture that the downsides of adding comparison for certain types outweigh
their benefits. And that it's different for other types.

Pointers and Slices have commonalities, true. But that doesn't mean "if you
can compare pointers, you should be able to compare slices". They also have
differences. And it's entirely reasonable, that the downsides for adding a
comparison for slices do not apply for pointers, or apply to a lesser
degree. And similar for benefits.

All that said, assuming you agree, what is your full counter argument, or
> set of counter arguments, to my initial argument for slice and map
> comparisons, shaped by the principles you've listed? It doesn't need to be
> in a rigid numbered list format, but it should be obvious how your counter
> arguments could logically fit into that format.
>

One argument goes roughly like

1. We believe most people would expect comparison of slices to compare the
contents of slices/maps, not the "shallow" comparison you suggest¹.
2. Doing that has technical problems making it prohibitive (e.g. the
existence of cyclic data structures).
3. Even *if* we would add a "shallow" comparison instead, there are still
open questions where it is hard to say what the "right" answer would be² .
4. It is better not to have any comparison, than one which behaves badly.
The programmer can always be explicit about their intentions, if need be.

[1] Note that you mentioned Rust, Ruby and Python to support comparisons on
their respective equivalents. Their comparisons all behave this way.
[2] This alone would probably not prevent us from doing it, but given that
we'd want a different notion of comparability anyways, it still matters.


> 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/CAKbcuKh_vGQTjtuBECxDJM%2BuHs5eiRpw2iq-%3DK77YyeUpcR%2BfQ%40mail.gmail.com
> 
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAEkBMfFYJ%3DHFYCBuaqRbjmD7H39gdihYObrwcdBTidqfJt1PJg%40mail.gmail.com.


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

2022-05-05 Thread Will Faught
On Wed, May 4, 2022 at 1:00 PM Ian Lance Taylor  wrote:

> On Tue, May 3, 2022 at 11:01 PM Will Faught  wrote:
> >
> > On Tue, 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()) }
> >>
> >
> > 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  } }
> >> 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."
>
> I don't think that is clear, because it seems to me that each call to
> F(), above, returns the same function literal, yet you said that F()
> == F() is false.
>
>
It doesn't return a function literal, it returns a function *value*. "Function
values are equal if they were created by the same function literal or
declaration." A function literal *creates* a function value conceptually in
this wording, although perhaps that's not how it actually works under the
hood.


> As an implementation note, currently F() does not allocate.  If we
> require that F() != F(), then calling F() must allocate.  Adding an
> allocation there is straightforward, but even if we permitted function
> comparisons I think that function literals will be used far more than
> they are compared, so adding an allocation seems like a poor use of
> resources.
>
>
>
Yeah, it's a trade-off. I can understand that it's not worth it.

This is all a moot point anyway, since functions don't have unique
addresses. Interesting discussion, though! :)


>
> >> > 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).
>
> I don't want to get into a terminology war, but I don't understand the
> distinction that you are drawing.  If I have "wrong expectations,"
> then what actually happens when I try something is "unexpected
> behavior."  It's literally not what I expected.
>
>
I meant to draw a distinction between unexpected behavior due to ignorance
or mistakes ("wrong expectations"), and unexpected behavior due to
complexity, undefined behavior, etc.


> It's reasonable to say that if you are using a new language you should
> read the friendly manual.  But it's also reasonable to say that as
> much as possible languages should be unsurprising.  Computer languages
> build on each other.  Go has obvious debts to languages like C and
> Oberon, and it would be confusing if constructs in Go acted
> differently than the identical constructs in those languages.
>
>
I can't speak to Oberon, but C has arrays that are usually passed around as
a pointer with a length (or an equivalent terminal value). Comparing those
arrays can be done shallowly with those pointers, or deeply by iterating
the array manually and comparing elements. One can even declare their own
slice-like struct with an array pointer and a length, if 

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

2022-05-05 Thread 'Axel Wagner' via golang-nuts
On Thu, May 5, 2022 at 3:39 PM David Arroyo  wrote:

> Go programs run on a single computer. What if a Go runtime was built that
> ran Go programs across many computers?


I don't think this would pose any problems to this topic specifically. It
would pose many problems, but once you have them all solved, this topic
wouldn't be any harder than it is today.
In particular, in this scenario you'd already have to solve
pointer-comparison and dereference and itables and function calls and once
all of those are solved, you can just apply those solution to the arguments
here.

Which is to say, the Go spec already basically assumes that your program
runs in a single memory space.


> Or, put another way, what if a system architecture emerged where the
> instruction access time varied so drastically across CPU cores that it made
> sense to duplicate functions across cores' fast memory regions, so that the
> receive operation in the above example actually received a duplicate copy
> of a function? I will admit that closures with mutable data segments
> already complicate such an optimization, but function equality would thwart
> such an optimization altogether.
>
> David
>
> --
> 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/f566007f-aa47-4aaf-bc9e-a0fa9dab1e62%40www.fastmail.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/CAEkBMfE6iDgOpMuFs-2k4mQA5G0q-zWhSGO9_6eTH_3LMuew_g%40mail.gmail.com.


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

2022-05-05 Thread David Arroyo
On Mon, May 2, 2022, at 22:43, will@gmail.com wrote:
>- Functions: Compare the corresponding memory addresses. The time 
>complexity is constant.

How would this apply to inlined functions? supporting equality would 
essentially force the compiler to keep a the function in the output binary even 
if it's inlined everywhere it's called, just for comparisons. It would also 
complicate 

Here's another example:

type worker struct { ... }
func shutDown() { ... }

func (w *worker) run(orders <-chan func()) {
for f := range orders {
if(f == shutDown) {
log.Printf("got a shutdown command")
w.Cleanup()
}
f()
}
}

currently, Go programs run on a single computer. What if a Go runtime was built 
that ran Go programs across many computers? Or, put another way, what if a 
system architecture emerged where the instruction access time varied so 
drastically across CPU cores that it made sense to duplicate functions across 
cores' fast memory regions, so that the receive operation in the above example 
actually received a duplicate copy of a function? I will admit that closures 
with mutable data segments already complicate such an optimization, but 
function equality would thwart such an optimization altogether.

David

-- 
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/f566007f-aa47-4aaf-bc9e-a0fa9dab1e62%40www.fastmail.com.


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

2022-05-05 Thread 'Axel Wagner' via golang-nuts
On Thu, May 5, 2022 at 3:11 AM Will Faught  wrote:

> 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 slice capacity, so it should be included in comparisons. `cap(S1[:1])
>> != cap(S1[:1:1])` is true, therefore `S1[:1] != S1[:1:1]` should be true,
>> even though `len(S1[:1]) == len(S1[:1:1])` is true.
>
>
> Do you agree that is a good thing, yes or no, and if not, why?
>

Sure.


> This approach to comparisons for functions, maps, and slices makes all
>> values of those types immutable, and therefore usable as map keys.
>
>
> Do you agree that is a good thing, yes or no, and if not, why?
>

Sure.

I wrote in the proposal an example of how slices work in actual Go code,
> then asked:
>
> Do you expect `c` to be true? If not (it's false, by the way), then why
>> would you expect `make([]int, 2) == make([]int, 2)` to be true?
>
>
> What was your answer? Yes or no? This isn't rhetorical at this point, I'm
> actually asking, so please answer unambiguously yes or no.
>

To be clear, demanding an unambiguous answer doesn't make a question
unambiguous. If you'd ask "is light a wave or a particle, please answer yes
or no", my response would be to stand up and leave the room, because there
is no way to converse within the rules you are setting. So, if you insist
on these rules, I will try my best to leave the room, metaphorically
speaking and to write you off as impossible to have a conversation with.

My position is that the comparison should be disallowed. Therefore, I can't
answer yes or no.

I think "both slides in the comparison contain the same elements in the
same order" is a strong argument in favor of making the comparison be true.
I think "this would make it possible for comparisons to hang the program"
is a strong argument in favor of making the comparison be false.
I think that the fact that there are strong arguments in favor of it being
true and strong arguments in favor of it being false, is itself a strong
argument in not allowing it.

If your answer was yes, then you don't understand Go at a basic level.
>

Please don't say things like this. You don't know me well enough to judge
my understanding of Go. If you did, I feel confident that you wouldn't say
this. It is just a No True Scotsman fallacy
at best and a baseless
insult at worst.


> I don't follow why `a[0:0:0] == b[0:0:0]` would be true if they have
> different array pointers.
>

Because above, you made the argument that focusing the definition of
equality on observable differences is a good thing. The difference between
a[0:0:0] and b[0:0:0] is unobservable (without using unsafe), therefore
they should be considered equal.

Note that `a[0] = 0; b[0] = 0; a[0] = 1; b[0] == 1` can observe whether the
> array pointers are the same.
>

No. This code panics, if the capacity of a and b is 0 - which it is for
a[0:0:0] and b[0:0:0]. There is no way to observe if two capacity 0 slices
point at the same underlying array, without using unsafe.

Feel free to prove me wrong, by filling in Eq so this program prints "true
false", without using unsafe: https://go.dev/play/p/xqj_DhBi392


>
>> But really, the point isn't "which semantics are right". The point is
>> "there are many different questions which we could argue about in detail,
>> therefore there doesn't appear to be a single right set of semantics".
>>
>
> I've already addressed this point directly, in a response to you. You
> commented on the particular example I'd given (iterating strings), but not
> on the general point. I'd be interested in your thoughts on that now.
>
Here it is again:
>
> 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 don't understand what your unaddressed point is.

It seems to be that we fundamentally disagree. Where Ian and I say "if we
can't clearly decide what to do, we should do nothing", you say "doing
anything is better than doing nothing" (I'm paraphrasing, because pure
repetition doesn't move things forward).

In that case, I don't see how I could 

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

2022-05-05 Thread Jan Mercl
On Wed, May 4, 2022 at 10:10 PM Ian Lance Taylor  wrote:

> I can only give some personal opinions.  Others will disagree.  And my
> opinions change over time, and could well be mistaken.
>
> I think that deciding that := declares a single variable in a
> for/range statement was a mistake (https://go.dev/issue/20733).
>
> I think that naked return statements in functions with named result
> parameters was a mistake (but named result parameters in themselves
> are useful).
>
> I think that ranging over strings may have been a mistake.  I go back
> and forth on that.
>
> I think that wrapping on integer overflow was a mistake (it should panic).
>
> I think that making "var i = 1 / 0" a compile-time error was a
> mistake.  Similarly for `"abc"[3]`.
>
> Let's not even get started on the standard library.

Thanks!

-- 
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-XaNHpP6uBgyLTgcCOZux2F2-_Qu2gu1a6LbKX5%2BEBfgA%40mail.gmail.com.


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

2022-05-04 Thread Ian Lance Taylor
On Wed, May 4, 2022 at 9:21 PM Will Faught  wrote:
>
> Makes sense, although I chuckled when I realized that the creator of that 
> issue didn't actually have his issue resolved.

I'll just note that I was the creator of that issue.

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/CAOyqgcUpEe%3DcQzXXppC%3DP7FavxsG41UbkqGvJ1tEyN4L01W4BA%40mail.gmail.com.


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

2022-05-04 Thread Will Faught
Makes sense, although I chuckled when I realized that the creator of that
issue didn't actually have his issue resolved.

Creator: This behavior is undefined. Which one should it be?
Go Team: Both.



On Wed, May 4, 2022 at 12:46 PM Ian Lance Taylor  wrote:

> On Tue, May 3, 2022 at 10:59 PM 'Axel Wagner' via golang-nuts
>  wrote:
> >
> >> 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.
>
> Zero sized values are useful in Go, because they can have methods and
> they can be stored in interfaces.  But if the addresses of zero-sized
> values must be distinct, then although zero-sized values appear to
> have zero size they must in fact be implemented as taking up one byte.
> For example, given `struct { a, b struct{} }`, the addresses of `a`
> and `b` must be distinct, so that struct is actually two bytes.  So,
> sure, we could change it.  But there are surprising results either
> way.
>
> (Historically this was introduced for https://go.dev/issue/2620.)
>
> Ian
>
> --
> You received this message because you are subscribed to a topic in the
> Google Groups "golang-nuts" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/golang-nuts/b-WtVh3H_oY/unsubscribe.
> To unsubscribe from this group and all its topics, 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/CAOyqgcU%3DTdSF1co2jyeQuUhk2P7giwf33tdK6p%2Bd8UgaXO4Tng%40mail.gmail.com
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAKbcuKgKhE1Mk8EcG889%3DjJpmk6EgAO2xbXbnVVkP3wUo7HGJA%40mail.gmail.com.


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

2022-05-04 Thread Robert Engels
I was only pointing out that if you use method based operations you can do 
whatever comparisons you find appropriate. 

> On May 4, 2022, at 8:19 PM, Will Faught  wrote:
> 
> 
> I don't follow how that's related to slice comparisons, aside from that fact 
> that slice comparison could be a method, but an Equals() method would be left 
> out to be consistent with how it is today.
> 
>> On Wed, May 4, 2022 at 5:22 AM Robert Engels  wrote:
>> Seems easier to move to a Go without operators and do everything with 
>> functions and generics. This is essentially the Java model.
>> 
>> This is pretty much the approach that the sync, sort, etc packages took and 
>> with generics you can have type safety and less code duplication. 
>> 
 On May 4, 2022, at 1:49 AM, Will Faught  wrote:
 
>>> 
>>> 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.
> 
> -- 
> 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/CAKbcuKhSCJV8Xb9Zehtfh1yLuWX1PYd6yFdiBpqAbM-PT%2BakAw%40mail.gmail.com.

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/FCDEB1DA-7D84-4AEF-AAE5-8D94159ABE26%40ix.netcom.com.


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

2022-05-04 Thread Will Faught
I don't follow how that's related to slice comparisons, aside from that
fact that slice comparison could be a method, but an Equals() method would
be left out to be consistent with how it is today.

On Wed, May 4, 2022 at 5:22 AM Robert Engels  wrote:

> Seems easier to move to a Go without operators and do everything with
> functions and generics. This is essentially the Java model.
>
> This is pretty much the approach that the sync, sort, etc packages took
> and with generics you can have type safety and less code duplication.
>
> On May 4, 2022, at 1:49 AM, Will Faught  wrote:
>
> 
> 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
> 
> .
>
>

-- 
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/CAKbcuKhSCJV8Xb9Zehtfh1yLuWX1PYd6yFdiBpqAbM-PT%2BakAw%40mail.gmail.com.


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

2022-05-04 Thread Will Faught
Can you explain what you mean by tokens? Do you mean something like:

```
var red, green, blue = new(struct{}), new(struct{}), new(struct{})
```

If so, I don't see how that's useful. Why not use integers or bytes instead?

On Wed, May 4, 2022 at 12:35 AM 'Dan Kortschak' via golang-nuts <
golang-nuts@googlegroups.com> wrote:

> On Wed, 2022-05-04 at 07:58 +0200, 'Axel Wagner' via golang-nuts wrote:
> > > 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.
>
> For zero-sized types (as opposed to zero-length types like zero-length
> []T), there is a good reason not to allocate a new zero-sized slot on
> the heap; since zero-sized values are commonly used as tokens, the
> language allows them and they don't store anything, it is fruitless
> work to go through the motions of asking the allocator to get a pointer
> when the same one can be used always with close to zero cost (does
> sizeof equal zero).
>
> For history, the treatment of this was introduced here
> https://codereview.appspot.com/10136043 (in malloc.goc).
>
>
> --
> You received this message because you are subscribed to a topic in the
> Google Groups "golang-nuts" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/golang-nuts/b-WtVh3H_oY/unsubscribe.
> To unsubscribe from this group and all its topics, 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/e3cff142d482ad17973538256a6ded7923d9a2e5.camel%40kortschak.io
> .
>

-- 
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/CAKbcuKiNFqs%3DhmHoX-Q2Ng2nLVaxGgbXpcgXn3bvXef0sRJRZg%40mail.gmail.com.


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

2022-05-04 Thread Will Faught
On Wed, May 4, 2022 at 12:13 AM Axel Wagner 
wrote:

> On Wed, May 4, 2022 at 8:42 AM Will Faught  wrote:
>
>> 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.
>>
>
> I don't believe you did. Looking over it again, those paragraphs explain
> *what* you are proposing. Not *why* those semantics are the right ones.
>
>
I wrote in those subsequent paragraphs:

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 slice capacity, so it should be included in comparisons. `cap(S1[:1])
> != cap(S1[:1:1])` is true, therefore `S1[:1] != S1[:1:1]` should be true,
> even though `len(S1[:1]) == len(S1[:1:1])` is true.


Do you agree that is a good thing, yes or no, and if not, why?

I wrote in the proposal:

This approach to comparisons for functions, maps, and slices makes all
> values of those types immutable, and therefore usable as map keys.


Do you agree that is a good thing, yes or no, and if not, why?

I wrote in the proposal an example of how slices work in actual Go code,
then asked:

Do you expect `c` to be true? If not (it's false, by the way), then why
> would you expect `make([]int, 2) == make([]int, 2)` to be true?


What was your answer? Yes or no? This isn't rhetorical at this point, I'm
actually asking, so please answer unambiguously yes or no.

If your answer was yes, then you don't understand Go at a basic level. If
your answer was no, then my argument is that it would be consistent for
comparisons of *built-in* slices to work the same way.

Do you agree that is a good thing, yes or no, and if not, why?

One very specific case is that your semantics consider a[0:0:0] ==
> b[0:0:0], if a and b come from different slices *even though the
> difference between them is unobservable without unsafe*. You make the
> argument that capacity should matter (which is one of the questions I
> posed), because of observability. So, by the same token, that difference
> shouldn't matter here, should it?
>
>
I don't follow why `a[0:0:0] == b[0:0:0]` would be true if they have
different array pointers. I'm arguing that they shouldn't be equal *because*
the array pointers are different. What are you saying is observable, but
not being accounted for by this proposal? Note that `a[0] = 0; b[0] = 0;
a[0] = 1; b[0] == 1` can observe whether the array pointers are the same.


> But really, the point isn't "which semantics are right". The point is
> "there are many different questions which we could argue about in detail,
> therefore there doesn't appear to be a single right set of semantics".
>
>
I've already addressed this point directly, in a response to you. You
commented on the particular example I'd given (iterating strings), but not
on the general point. I'd be interested in your thoughts on that now. Here
it is again:

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?



>>
>>> 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.
>>>
>>>
>> Perhaps I'm at fault for not being precise in my wording, but the intent
>> was to specify slice comparisons as encompassing the array pointer, the
>> length, and the capacity. It doesn't matter if other fields are added
>> later, or if the type is renamed.
>>
>
> The language spec does not know about array pointers. I'm not saying "a
> field could be added later". I'm saying "one of the fields you are talking
> about does not exist, as it is a choice made by gc, not something
> prescribed by the spec".
>
>
>From https://go.dev/ref/spec#Slice_types:

A slice is a descriptor for 

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

2022-05-04 Thread Ian Lance Taylor
On Wed, May 4, 2022 at 12:53 PM 'Axel Wagner' via golang-nuts
 wrote:
>
> On Wed, May 4, 2022 at 9:46 PM Ian Lance Taylor  wrote:
>>
>> Zero sized values are useful in Go, because they can have methods and
>> they can be stored in interfaces. But if the addresses of zero-sized
>> values must be distinct, then although zero-sized values appear to
>> have zero size they must in fact be implemented as taking up one byte.
>> For example, given `struct { a, b struct{} }`, the addresses of `a`
>> and `b` must be distinct, so that struct is actually two bytes.  So,
>> sure, we could change it.  But there are surprising results either
>> way.
>>
>> (Historically this was introduced for https://go.dev/issue/2620.)
>
>
> The other side of the question is "why not define them to always be the 
> same"? It seems a fairly simple optimization.
> I guess it can be argued that `a[0:0:0]` points to a zero-sized object, so 
> should always be the same, but that's expensive to do, as every slicing 
> operation has to check for 0? But that seems a weak argument.
> Is there a better one?

Using the reflect package we can walk through the fields of a struct
and get the address based on the size and alignment of earlier fields
in the struct.  But if the address of all zero-sized values is the
same, then that doesn't work.

It's not a great argument, but it's not nothing.

Also it's a little weird that given `var v1 v2 struct { f1 int; f2
struct{}; f3 int }` then  == , but maybe that's not much
weirder than how it works today.

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/CAOyqgcXBQ14JMzAJbN9NZ%2BJeMYPRH9Za0G7iJDQgQ4eUGqLvBQ%40mail.gmail.com.


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

2022-05-04 Thread Ian Lance Taylor
On Tue, May 3, 2022 at 11:13 PM Jan Mercl <0xj...@gmail.com> wrote:
>
> 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 can only give some personal opinions.  Others will disagree.  And my
opinions change over time, and could well be mistaken.

I think that deciding that := declares a single variable in a
for/range statement was a mistake (https://go.dev/issue/20733).

I think that naked return statements in functions with named result
parameters was a mistake (but named result parameters in themselves
are useful).

I think that ranging over strings may have been a mistake.  I go back
and forth on that.

I think that wrapping on integer overflow was a mistake (it should panic).

I think that making "var i = 1 / 0" a compile-time error was a
mistake.  Similarly for `"abc"[3]`.

Let's not even get started on the standard library.

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/CAOyqgcXuE%2B3w8J4X0L_OyeVm%2B59VNCOLDQ2TouwaZWUoBS-dGQ%40mail.gmail.com.


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

2022-05-04 Thread Ian Lance Taylor
On Tue, May 3, 2022 at 11:01 PM Will Faught  wrote:
>
> On Tue, 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()) }
>>
>
> 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  } }
>> 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."

I don't think that is clear, because it seems to me that each call to
F(), above, returns the same function literal, yet you said that F()
== F() is false.

As an implementation note, currently F() does not allocate.  If we
require that F() != F(), then calling F() must allocate.  Adding an
allocation there is straightforward, but even if we permitted function
comparisons I think that function literals will be used far more than
they are compared, so adding an allocation seems like a poor use of
resources.



>> > 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).

I don't want to get into a terminology war, but I don't understand the
distinction that you are drawing.  If I have "wrong expectations,"
then what actually happens when I try something is "unexpected
behavior."  It's literally not what I expected.

It's reasonable to say that if you are using a new language you should
read the friendly manual.  But it's also reasonable to say that as
much as possible languages should be unsurprising.  Computer languages
build on each other.  Go has obvious debts to languages like C and
Oberon, and it would be confusing if constructs in Go acted
differently than the identical constructs in those languages.


>> 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?

The fact that Go is imperfect, which it is, is not an argument for
adding further imperfections.

> I've argued that slice comparisons make Go even simpler and more consistent 
> with various examples, analogies, and so on. Please see my response to Axel, 
> if you haven't already. Do you have a specific counter argument to any 
> specific argument that I've made regarding simplicity or consistency?

All changes to languages have costs and benefits.  Your arguments
about simplicity and consistency are benefits.  The counter-arguments
that I and several others have been making are costs.  In deciding
whether to change the language we must weigh those costs and benefits
and 

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

2022-05-04 Thread 'Axel Wagner' via golang-nuts
On Wed, May 4, 2022 at 9:46 PM Ian Lance Taylor  wrote:

> Zero sized values are useful in Go, because they can have methods and
> they can be stored in interfaces. But if the addresses of zero-sized
> values must be distinct, then although zero-sized values appear to
> have zero size they must in fact be implemented as taking up one byte.
> For example, given `struct { a, b struct{} }`, the addresses of `a`
> and `b` must be distinct, so that struct is actually two bytes.  So,
> sure, we could change it.  But there are surprising results either
> way.
>
> (Historically this was introduced for https://go.dev/issue/2620.)
>

The other side of the question is "why not define them to always be the
same"? It seems a fairly simple optimization.
I guess it can be argued that `a[0:0:0]` points to a zero-sized object, so
should always be the same, but that's expensive to do, as every slicing
operation has to check for 0? But that seems a weak argument.
Is there a better one?


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


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

2022-05-04 Thread Ian Lance Taylor
On Tue, May 3, 2022 at 10:59 PM 'Axel Wagner' via golang-nuts
 wrote:
>
>> 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.

Zero sized values are useful in Go, because they can have methods and
they can be stored in interfaces.  But if the addresses of zero-sized
values must be distinct, then although zero-sized values appear to
have zero size they must in fact be implemented as taking up one byte.
For example, given `struct { a, b struct{} }`, the addresses of `a`
and `b` must be distinct, so that struct is actually two bytes.  So,
sure, we could change it.  But there are surprising results either
way.

(Historically this was introduced for https://go.dev/issue/2620.)

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/CAOyqgcU%3DTdSF1co2jyeQuUhk2P7giwf33tdK6p%2Bd8UgaXO4Tng%40mail.gmail.com.


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

2022-05-04 Thread Robert Engels
Seems easier to move to a Go without operators and do everything with functions 
and generics. This is essentially the Java model.

This is pretty much the approach that the sync, sort, etc packages took and 
with generics you can have type safety and less code duplication. 

> On May 4, 2022, at 1:49 AM, Will Faught  wrote:
> 
> 
> 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.

-- 
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/CF8968B4--413C-A6F7-9295ADD5ECC3%40ix.netcom.com.


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

2022-05-04 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.


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

2022-05-04 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 

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

2022-05-04 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-04 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-04 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 

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

2022-05-04 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  } }
> 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 

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 

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  } }
> 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 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  } }
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 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 

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] 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.


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

2022-05-03 Thread Jan Mercl
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/CAA40n-VkT9FZz4P7A6e-iscYzhEGN20kXnxZhsNHuwv6pzEt3Q%40mail.gmail.com.


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

2022-05-03 Thread Will Faught
>
> 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?

In LISP terms, these implementations do something more like `eq`, not
> `equal`. I want to know if the slices or maps are _equivalent_, not if
> they point to identical memory. No one wants this semantics for slice
> equality. Checking if they are equivalent raises difficult issues
> around recursion, slices that point to themselves, and other problems
> that prevent a clean, efficient solution.


Can't the same argument be made for pointer comparisons? Why should we
compare pointer values when doing so won't compare the referenced, logical
values? Because sometimes we want to compare just the pointer values
because it's fast, and pointers are small, and you can conclude logical
equivalence if they're equal. Sometimes shallow/literal comparisons are
useful, and sometimes deep/logical comparisons are useful.

Just as pointer comparisons are shallow, so too are comparisons for types
that contain pointers. I included the Slice1000 example above specifically
to address your point. Based on your argument here, I assume your answer to
the question about `c` in that example would be "yes," however the answer
is no, according to current Go behavior. Comparison of structs containing
pointers does a shallow comparison of the pointer value, not the value it
references. My argument is that under the hood, Go slices work the same way.

I'm proposing a shallow comparison, not a deep comparison, and that's
arguably a feature here. I highlighted the time complexity for a reason,
because I recall someone in the Go Team at one point arguing somewhere that
doing a logical comparison would be too slow, since one of the benefits of
Go comparisons is that their time complexity is small and well-understood.
Checking for equality for your struct-based type won't ever cause your
program to slow or hang; it's "safe."

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.

On Mon, May 2, 2022 at 9:58 PM Rob Pike  wrote:

> * Functions: Compare the corresponding memory addresses. The time
> complexity is constant.
>
> 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.
>
> * Maps: Compare the corresponding `*runtime.hmap` (pointer) values.
> The time complexity is constant.
> * Slices: Compare the corresponding `runtime.slice` (non-pointer
> struct) values. The time complexity is constant.
>
> In LISP terms, these implementations do something more like `eq`, not
> `equal`. I want to know if the slices or maps are _equivalent_, not if
> they point to identical memory. No one wants this semantics for slice
> equality. Checking if they are equivalent raises difficult issues
> around recursion, slices that point to themselves, and other problems
> that prevent a clean, efficient solution.
>
> Believe me, if equality for these types was efficient _and_ useful, it
> would already be done.
>
> -rob
>
> On Tue, May 3, 2022 at 2:41 PM Will Faught  wrote:
> >
> > You seem to have misunderstood the point. It's an idea for changing the
> language. You're just demonstrating the current behavior, which is what
> would be changed. The 

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

2022-05-02 Thread Rob Pike
* Functions: Compare the corresponding memory addresses. The time
complexity is constant.

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.

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

In LISP terms, these implementations do something more like `eq`, not
`equal`. I want to know if the slices or maps are _equivalent_, not if
they point to identical memory. No one wants this semantics for slice
equality. Checking if they are equivalent raises difficult issues
around recursion, slices that point to themselves, and other problems
that prevent a clean, efficient solution.

Believe me, if equality for these types was efficient _and_ useful, it
would already be done.

-rob

On Tue, May 3, 2022 at 2:41 PM Will Faught  wrote:
>
> You seem to have misunderstood the point. It's an idea for changing the 
> language. You're just demonstrating the current behavior, which is what would 
> be changed. The argument is to make `make([]int, 2) == make([]int, 2)` legal, 
> and evaluate to false.
>
> On Mon, May 2, 2022 at 8:22 PM Kurtis Rader  wrote:
>>
>> On Mon, May 2, 2022 at 7:44 PM will@gmail.com  
>> wrote:
>>>
>>> ```
>>> type Slice1000[T any] struct {
>>> xs *[1000]T
>>> len, cap int
>>> }
>>>
>>> func (s Slice1000[T]) Get(i int) T {
>>> // ...
>>> return s.xs[i]
>>> }
>>>
>>> func (s Slice1000[T]) Set(i int, x T) {
>>> // ...
>>> s.xs[i] = x
>>> }
>>>
>>> var xs1, xs2 [1000]int
>>>
>>> var a = Slice1000[int]{, 1000, 1000}
>>> var b = Slice1000[int]{, 1000, 1000}
>>> var c = a == b
>>> ```
>>>
>>> Do you expect `c` to be true? If not (it's false, by the way), then why 
>>> would you expect `make([]int, 2) == make([]int, 2)` to be true?
>>
>>
>> No. Did you actually try your hypothetical `make([]int, 2) == make([]int, 
>> 2)`? When I do so using the source below this reply the Go compiler emits 
>> the error "slice can only be compared to nil". Which is what I expect given 
>> the specification for the Go language. This seems like an example of the XY 
>> Problem. What caused you to open this thread?
>>
>> package main
>>
>> import (
>> "fmt"
>> )
>>
>> func main() {
>> fmt.Printf("%v\n", make([]int, 2) == make([]int, 2))
>> }
>>
>> --
>> Kurtis Rader
>> Caretaker of the exceptional canines Junior and Hank
>
> --
> 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/CAKbcuKheNk99JrYJ8u6knu15LSwf6nZXxD6_UqUOF_1JhFVHjA%40mail.gmail.com.

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAOXNBZRFShduyB7QYxVgwJa6GA7NbK6eO1i3ETps1f3nQRQVCw%40mail.gmail.com.


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

2022-05-02 Thread Will Faught
You seem to have misunderstood the point. It's an idea for changing the
language. You're just demonstrating the current behavior, which is what
would be changed. The argument is to make `make([]int, 2) == make([]int,
2)` legal, and evaluate to false.

On Mon, May 2, 2022 at 8:22 PM Kurtis Rader  wrote:

> On Mon, May 2, 2022 at 7:44 PM will@gmail.com 
> wrote:
>
>> ```
>> type Slice1000[T any] struct {
>> xs *[1000]T
>> len, cap int
>> }
>>
>> func (s Slice1000[T]) Get(i int) T {
>> // ...
>> return s.xs[i]
>> }
>>
>> func (s Slice1000[T]) Set(i int, x T) {
>> // ...
>> s.xs[i] = x
>> }
>>
>> var xs1, xs2 [1000]int
>>
>> var a = Slice1000[int]{, 1000, 1000}
>> var b = Slice1000[int]{, 1000, 1000}
>> var c = a == b
>> ```
>>
>> Do you expect `c` to be true? If not (it's false, by the way), then why
>> would you expect `make([]int, 2) == make([]int, 2)` to be true?
>>
>
> No. Did you actually try your hypothetical `make([]int, 2) == make([]int,
> 2)`? When I do so using the source below this reply the Go compiler emits
> the error "slice can only be compared to nil". Which is what I expect given
> the specification for the Go language. This seems like an example of the XY
> Problem . What caused you to open this thread?
>
> package main
>
> import (
> "fmt"
> )
>
> func main() {
> fmt.Printf("%v\n", make([]int, 2) == make([]int, 2))
> }
>
> --
> Kurtis Rader
> Caretaker of the exceptional canines Junior and Hank
>

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


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

2022-05-02 Thread Kurtis Rader
On Mon, May 2, 2022 at 7:44 PM will@gmail.com 
wrote:

> ```
> type Slice1000[T any] struct {
> xs *[1000]T
> len, cap int
> }
>
> func (s Slice1000[T]) Get(i int) T {
> // ...
> return s.xs[i]
> }
>
> func (s Slice1000[T]) Set(i int, x T) {
> // ...
> s.xs[i] = x
> }
>
> var xs1, xs2 [1000]int
>
> var a = Slice1000[int]{, 1000, 1000}
> var b = Slice1000[int]{, 1000, 1000}
> var c = a == b
> ```
>
> Do you expect `c` to be true? If not (it's false, by the way), then why
> would you expect `make([]int, 2) == make([]int, 2)` to be true?
>

No. Did you actually try your hypothetical `make([]int, 2) == make([]int,
2)`? When I do so using the source below this reply the Go compiler emits
the error "slice can only be compared to nil". Which is what I expect given
the specification for the Go language. This seems like an example of the XY
Problem . What caused you to open this thread?

package main

import (
"fmt"
)

func main() {
fmt.Printf("%v\n", make([]int, 2) == make([]int, 2))
}

-- 
Kurtis Rader
Caretaker of the exceptional canines Junior and Hank

-- 
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/CABx2%3DD8HHjc3cYL_KL%3Dzga2V02Fpvz3EoXW0PSkhCx9p-B2w5g%40mail.gmail.com.