The problem is that different programs need different things for slice
equality. Some want pointer equality as you suggest. Some want
element comparisons, as is done for array equality. Without an
obvious semantics for the operation, the language omits it entirely.
This would probably introduce unnecessary confusion. People are used to the equality operator comparing values in go, as opposed to references. It's much better if the slices finally support the equality operator, even though the comparison speed will depend on the number of items in the slices.
--
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.
For more options, visit https://groups.google.com/d/optout.
Your proposition merely moves the semantical constraint to another
place: as Ian pointed out, if we define equality for slices to be
something resembling pointer equality suddenly []byte{1, 2} is not equal
to []byte{1, 2}.
The approach I have proposed (a custom "keying" function) is actually
what "every other language" does, just more explicit. Say, .NET's
collections rely on their members implementing various interfaces such
as IHashCodeProvider. My "keying" function merely provides a mapping
from values which cannot be used as keys directly to some other values
which can -- using your own rules. Since your rules would be codified
explicitly, there would be no cognitive burden for the next programmer
reading your code.
> And it's on any type that has one or more fields of type slice/map/
> or func.
>
> The alternative is to implement one's own associative array
> datastructure with one's own hashing algorithm etc but that's still
> going to be clunky since it will be implementation dependent and will
> rely on the use of unsafe.
I propose putting such "hashing algorithm" outside of the datastructure.
Basically what I propose is replacing
collection(K → V)
with
collection(K' → V)
where K' is derived from K using your own function: K' = fn(K).
The collection stays unmodified, you just transform its keys.
You don't have to, you can use a pointer indirection instead.
On Fri, Jul 1, 2016 at 3:15 AM, Chad <send...@gmail.com> wrote:
> No, it's actually fine. You are comparing values.
>
> A slice being a struct under the hood, for slices you would compare the
> structs (slice headers)
Yes, when you remember that a slice is a just a small struct with a
pointer and some bookkeeping information (length, capacity), then you
can compare two slices easily. So with
a := []int{10, 20, 30}
b := a[:]
then a == b would be fast: simply compare the pointers and the
bookkeeping information. Direct value equality between the structs
gives you the expected and correct answer.
But what about
[]int{10, 20} == []int{10, 20}
where the two slices refer to different underlying arrays? I think you
argue that this comparison should be false?
I would hope that slices from different underlying arrays could be
compared for equality based on their values, just like strings can.
Two strings (that are not slices from the same original string) can be
compared for equality just fine. I hope Go takes the shortcut when the
strings refer to the same memory and otherwise does the slower
per-byte comparison. The same could perhaps apply to slices in
general.
--
Martin Geisler
On Fri, Jul 1, 2016 at 12:52 PM, Chad <send...@gmail.com> wrote:
> However, that it's a valid point you raise (re strings)
> But that's exactly the reason for which, someone would probably ask whether
> a string is a reference type or a value type.
>
> This could probably made clearer in the documentation.
I keep seeing references (hah!) to this concept of a "reference type"
:-) However, I just tried searching the language spec and Effective Go
and there doesn't seem to be such a concept defined in those
documents.
On Friday, July 1, 2016 at 3:44:10 PM UTC+2, Martin Geisler wrote:On Fri, Jul 1, 2016 at 12:52 PM, Chad <send...@gmail.com> wrote:
> However, that it's a valid point you raise (re strings)
> But that's exactly the reason for which, someone would probably ask whether
> a string is a reference type or a value type.
>
> This could probably made clearer in the documentation.
I keep seeing references (hah!) to this concept of a "reference type"
:-) However, I just tried searching the language spec and Effective Go
and there doesn't seem to be such a concept defined in those
documents.I think it should. It is mentioned here however https://blog.golang.org/go-maps-in-action
Without that, one of the first instinct of beginning programmers is often to create pointers to slices ** or maps **
On Fri, Jul 1, 2016 at 4:01 PM, Chad <send...@gmail.com> wrote:
>
>
> On Friday, July 1, 2016 at 3:44:10 PM UTC+2, Martin Geisler wrote:
>>
>> On Fri, Jul 1, 2016 at 12:52 PM, Chad <send...@gmail.com> wrote:
>> > However, that it's a valid point you raise (re strings)
>> > But that's exactly the reason for which, someone would probably ask
>> > whether
>> > a string is a reference type or a value type.
>> >
>> > This could probably made clearer in the documentation.
>>
>> I keep seeing references (hah!) to this concept of a "reference type"
>> :-) However, I just tried searching the language spec and Effective Go
>> and there doesn't seem to be such a concept defined in those
>> documents.
>
>
> I think it should. It is mentioned here however
> https://blog.golang.org/go-maps-in-action
You're right, that article calls maps, slices and pointers "reference types".
I feel that is a little unfortunate since it muddles the picture and
makes the implementation more obscure. I would have been happy to have
been told right from the start that a slice is a small struct, small
enough that you can pass it by value instead of with a pointer. That
allows me to build a mental model in terms of other Go constructs.
Conceptually, the value of a string is the sequence of bytes it contains, just like an array of bytes. It doesn't matter that the implementation of strings looks more like a slice internally. On the other hand the value of a slice is a reference to (part of) an underlying array, together with a length. In all cases == should compare values.
I agree that it would be confusing to make == work for slices, because many people, myself included would intuitively want that to compare the contents of the slice (values from the underlying array) rather than the value of the slice itself which is essentially 3 pointers.
I never had the need to compare equality for memory locations of anything in Go (especially not for slices / maps oe their elements).
Go does not have reference types. As far as I know, the word was purposefully removed from the spec to remove the ambiguity surrounding the word.
https://groups.google.com/forum/m/#!topic/golang-dev/926npffb6lA
The language authors omitted this functionality for a reason. One of the reasons is that arrays and slices are similar, and changing their comparison semantics seemed confusing.
I wish I could be pointed to such discussion.
An analogous “shallow” equality test for slices could be useful, and it would solve the problem with maps, but the inconsistent treatment of slices and arrays by the == operator would be confusing. The safest choice is to disallow slice comparisons altogether.
I wish I could be pointed to such discussion.
It was briefly discussed in The Go Programming Language book by Donovan & Kernighan (p 132):
An analogous “shallow” equality test for slices could be useful, and it would solve the problem with maps, but the inconsistent treatment of slices and arrays by the == operator would be confusing. The safest choice is to disallow slice comparisons altogether.
Given this program, how would you modify it to include interfaces?
https://play.golang.org/p/9-rhDCZol_
Map lookup requires an equality operator, which slices do not implement. They don't implement equality because equality is not well defined on such types; there are multiple considerations involving shallow vs. deep comparison, pointer vs. value comparison, how to deal with recursive types, and so on.
Slice, map, and function values are not comparable.
Array values are comparable if values of the array element type are comparable. Two array values are equal if their corresponding elements are equal.
Slice values are deeply equal when all of the following are true: they are both nil or both non-nil, they have the same length, and either they point to the same initial entry of the same underlying array (that is, &x[0] == &y[0]) or their corresponding elements (up to length) are deeply equal. Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil)) are not deeply equal.
The problem is that different programs need different things for slice equality. Some want pointer equality as you suggest. Some want element comparisons, as is done for array equality. Without an obvious semantics for the operation, the language omits it entirely.
--
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+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
- When slices can be compared, they can be used as map keys. What happens if the contents of a slice are changed after it has been added to a map?
- It is possible to have self-referential slices [1]. How would comparison work in such a case?
type A []A
// not equal
a0 := A{A{}}
a1 := A{}
// not equal
a2 := A{A{}, A{A{}}}
a3 := A{A{}, A{A{}}}
// not equal
a4 := A{a0}
a5 := A{A{A{}}}
// equal
a6 := A{a0}
a7 := A{a0}
// equal
a8 := A{a0}
a8[0] = a1
a9 := A{a1}
a0 := A{}
// equal
a1 := A{a0, A{}}
a1[0] = a1
a2 := A{a0, a1[1]}
// not equal
a3 := A{A{}, a1[1]}
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
a0 := A{}
// equal
a1 := A{a0, A{}}
a1[0] =
a1
a2 := A{a1, a1[1]}
// not equal
a3 := A{A{a1, a1[1]}, a1[1]}
- When slices can be compared, they can be used as map keys. What happens if the contents of a slice are changed after it has been added to a map?I’m not too familiar with Go map internals, but my thought is the key hash would depend on the backing array values. Go maps also allow reading the keys back using iteration so the slice backing array (up to length) would have to be copied. If the slice contents are changed then that would be a different key and the original key would be intact.- It is possible to have self-referential slices [1]. How would comparison work in such a case?Perhaps a slice of slices would compare the contents, the slice headers, to each other like structs.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
Are you sure that's the only edge-case? Because this thread is kinda long and there might even be things we are not thinking about.
The usecase you mentioned above seems - to me - to be served by a map[string]*T just fine. Did I misunderstand it?
Each path represented as a slice of coordinates could be easily encoded to a string and compared that way.
As far as resource expenses go, we'd need benchmarks to say much. I understand looking at expensive cases too, but that doesn't mean that there aren't smaller regular use cases. Goroutines can be overused too.MattOn Wednesday, January 31, 2018 at 10:24:30 AM UTC-6, rog wrote:On 30 January 2018 at 23:19, <matthe...@gmail.com> wrote:
>> - When slices can be compared, they can be used as map keys. What happens
>> if the contents of a slice are changed after it has been added to a map?
>
>
> I’m not too familiar with Go map internals, but my thought is the key hash
> would depend on the backing array values. Go maps also allow reading the
> keys back using iteration so the slice backing array (up to length) would
> have to be copied. If the slice contents are changed then that would be a
> different key and the original key would be intact.
Note that copying the slice contents to make a map key implies
that using a slice as a map key might imply copying a whole tree,
which seems rather expensive to me (especially as it might end up
using more memory than the original if some elements are duplicated,
unless an alias-aware copy algorithm is used which would be
more expensive still)
BTW you can already do something like this:
https://play.golang.org/p/q4bz8-AckN3
You can even do it without reflect, which I'll leave as an exercise
for the reader :)
--
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+unsubscribe@googlegroups.com.
BTW you can already do something like this:
https://play.golang.org/p/q4bz8-AckN3
You can even do it without reflect, which I'll leave as an exercise for the reader :)
Something else. No size limit. Not what one might call efficient though. :)> I'll bite, does it involve struct { len int; content [maxCount]int }, or did
> you have something else in mind?
... tick, tick, tick, tick... dide-dudi-diddlidedum...di
What do you mean by a "slice pointer key" ?
map[*[]Something]string
What do you mean by a "slice pointer key" ?
map[*[]Something]stringThen map consumers can do key comparisons. I originally used this for an unordered set type.