On Wed, May 4, 2022 at 1:00 PM Ian Lance Taylor <i...@golang.org> wrote:

> On Tue, May 3, 2022 at 11:01 PM Will Faught <will.fau...@gmail.com> wrote:
> >
> > On Tue, May 3, 2022 at 7:27 PM Ian Lance Taylor <i...@golang.org> 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 &i } }
> >> func J() { fmt.Println(H(0) == H(1)) }
> >>
> >
> > It would print false for the same reason.
> >
> >>
> >> Whatever we define for cases like this some people will be ready to
> >> argue for a different choice.  The costs of forcing a decision exceed
> >> the benefits.
> >
> >
> > So on the balance, the cost of making a decision is worth it for
> something big like dependencies or generics, but not function equality.
> Well, I guess that's fair enough, but it seems like one could use that kind
> of argument to undermine any language change, though, including
> dependencies and generics. It doesn't seem like the function equality rule
> I sketched out would add much, if any, language complexity. It's only one
> sentence: "Function values are equal if they were created by the same
> function literal or declaration."
>
> 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 one were so
inclined. What I'm proposing is *straight out* of C.

Other languages, like Rust, Ruby, and Python, enable slices/lists to be
compared. It's unusual that Go doesn't have any way to compare slices out
of the box. I'm not a genius who invented the idea that slices can or
should be comparable. I'm bringing this idea here in part because I think
it *is* consistent with other languages.

What is your counter argument to this point I made earlier, the context
being that new Java programmers found == behavior with strings confusing:

Arguably, old Java programmers would find `==` confusing for structs, since
> it doesn't compare references.


Why allow == to compare structs, when C didn't let you? Why not allow == to
compare slices, when C let you do the equivalent?


> >> 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 didn't argue that. I argued that the change to the language spec would be
done in a typical way. "Special explanation" as defined as literally any
wording in the language spec could be used to block anything. It could have
been used to block the addition of underscores in number literals.

Do you agree, yes or no, and if not, why?


> > 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 decide which are more important.
>
> To put it another way, I don't have specific counter arguments to your
> specific arguments about simplicity and consistency.  But that doesn't
> mean that I agree, it just means that I think that other consequences
> are more important.
>
>
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.

Principles are good for guiding us in forming premises for arguments. For
example, principles like "freedom is good" and "safety is good" are often
applied when devising new laws, but they aren't themselves reasons for
accepting or rejecting an idea. "Freedom is good" alone would rule out all
laws, police, protections, order, and so on. "Safety is good" would permit
nothing potentially interesting, fun, or meaningful to ever happen. So
people use these principles to guide them in forming arguments, weighing
the pros and cons for a particular idea in light of each principle, to find
the best trade-off among them. Like:

1. Not wearing a seat belt is dangerous.
2. Wearing a seat belt takes little effort and the discomfort is minimal to
none.
Therefore, 3. A seat belt law is a good idea.


Premise 1 took into account "safety is good." Premise 2 took into account
"freedom is good." The conclusion (3) reflects both principles, but doesn't
*rely* on them directly.

1. Safety is good.
Therefore, 2. A seat belt law is a good idea.


is a bad argument because you can justify almost anything that way:

1. Safety is good.
Therefore, 2. A law that requires everyone to wear a helmet 24/7 is a good
idea.


If you want to rule out all change in Go, then just use the premise/axiom
"nothing can be changed:"

1. Nothing can be changed.
2. This idea will change something.
Therefore, 3. This is a bad idea.


I would agree with that argument, assuming the first premise was official
policy. But

1. Simplicity is good.
2. Complexity is bad.
3. Change has cost.
Therefore, 4. This is a bad idea.


is a bad argument, because you can say no to almost anything that way:

1. Simplicity is good.
2. Complexity is bad.
3. Change has cost.
Therefore, 4. No modules. No generics. No enhanced number literals. No
embed package. No io/fs. No nothing.


You haven't used these principles to put forth any kind of argument with
concrete premises that are guided by them. Your and Rob's counter argument
for function comparisons was great because it made sense from an
implementation perspective, a concrete premise, which I accepted.

This issue is related to the "no new rationale" issue that Russ Cox has
discussed <https://research.swtch.com/proposals-intro>. I don't know how
the Go Team as a whole thinks about that issue, but it's something I
wholeheartedly agree with. *This* issue could perhaps be called something
like "no vague rationale." *Principles* are vague rationale; *arguments*
are not. Arguments that are *shaped* by principles are stronger for it.

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.

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.

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.

Reply via email to