As for documentation for how `func` works BTW: This design doc for Go 1.1
is a good description
<https://docs.google.com/document/d/1bMwCey-gmqZVTpRax-ESeVuZGmjwbocYs1iHplK-cjo/pub>
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 <axel.wagner...@googlemail.com>
wrote:

> On Wed, May 4, 2022 at 1:40 AM will....@gmail.com <will.fau...@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 <https://go.dev/play/p/xeeY6HSSvuv>. 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 expressions are equal.
>>
>
> No, it does not. Otherwise, `make([]T, 0)` would be equal to `nil`.
> `make([]T, 0)` is allowed to use a constant element pointer or not - it
> must point to a zero-sized array. That pointer must be distinguishable from
> a nil-slice, but can be arbitrary apart from that. Note, BTW, that the
> language also does not *force* an implementation to even use that
> representation. A slice could well be represented as `struct{ Ptr *T; Len
> int; Cap int; NonNil bool }`, if the implementation want to. Or a miriad
> other ways.
>
> reflect.DeepEqual does not compare the pointers for capacity 0 slices
> <https://go.dev/play/p/vO2m-1zzCc8>, BTW. To drive home how ambiguous
> this question actually is, given that you assumed it woudl.
>
>
>> We *could* do deep comparisons for pointers
>>
>
> Not without allowing for a bunch of unpleasant consequences, at least. For
> example, this code would hang forever:
>
> type T *T
> var t1, t2 T
> t1, t2 = &t2, &t1
> fmt.Println(t1 == t2)
>
> Note that it is *very common* to have circular pointer structures. For
> example, in a doubly-linked list.
>
>
>> , but we don't, because shallow comparisons are useful, and we can
>> dereference pointers when we need to do deep comparisons. As I argued
>> above, this is exactly the same situation for slices regarding shallow and
>> deep 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
>>>> <https://go.dev/doc/faq#map_keys> 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 <r...@golang.org> 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 <will....@gmail.com> 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 <kra...@skepticism.us>
>>>>> wrote:
>>>>> >>
>>>>> >> On Mon, May 2, 2022 at 7:44 PM will....@gmail.com <
>>>>> 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]{&xs1, 1000, 1000}
>>>>> >>> var b = Slice1000[int]{&xs2, 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...@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...@googlegroups.com.
>>>>
>>> To view this discussion on the web visit
>>>> https://groups.google.com/d/msgid/golang-nuts/CAKbcuKicTHJZZ7yAYKO96MhCPQct%3DQZs07S4%3D-_vXvmoe_ndqA%40mail.gmail.com
>>>> <https://groups.google.com/d/msgid/golang-nuts/CAKbcuKicTHJZZ7yAYKO96MhCPQct%3DQZs07S4%3D-_vXvmoe_ndqA%40mail.gmail.com?utm_medium=email&utm_source=footer>
>>>> .
>>>>
>>> --
>> 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/a533b35c-6ab7-4fe3-9698-6a0f0a71f091n%40googlegroups.com
>> <https://groups.google.com/d/msgid/golang-nuts/a533b35c-6ab7-4fe3-9698-6a0f0a71f091n%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>>
>

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

Reply via email to