Live data from Hacker News

Go(lang): Robust generic functions on slices

go.dev

71–80 of 85 posts

Re: Go(lang): Robust generic functions on slices

#71
post #65

Earlier quoted context omitted.

If the problem is that slices are somewhat complex, allowing aliasing sure. But then it's a problem of understanding what slices are so it does help in practice. I am more concerned by the difficulty of mutating a slice (deleting and appending element) while iterating over it for instance. Done it and that's more difficult, ceremonious.

> If the problem is that slices are somewhat complex, allowing aliasing sure. No, the problem is what I put at the top, that slices are hopelessly confused, they have two completely different and incompatible purposes and Go does not let you differentiate between the two. Understanding what slices are does not help, neither in theory nor in practice.

The way I see it, this is just lower level for mechanical sympathy and one is still free to implement a copy-on-write wrapper. Been there, done that.

The trick in understanding what they are is to understand that these are not vectors if I try to get closer to your semantics. Once it is viewed purely as a kind of reference type, a view in a backing array, it has only one meaning.

It's easier for a traditional functional language to implement lists and vectors perhaps because they operate on the premise of immutability first. Beware of the memory footprint though.

I admit that it might be easier to think in term of vectors. That's kind of what was done designing the wrapper.

Still, as I understand, slices are just lower-level. Immutability is a higher, language level concept.

Re: Go(lang): Robust generic functions on slices

#72
post #2

A missing tidbit that may help contextualize this post: One of the things about Go that surprised me is that if you have a slice which does not represent the full capacity of the underlying array, you can go ahead and reslice it up to that full capacity even though it's a panic to access the things you're reslicing directly: https://go.dev/play/p/oThz2bNFwgr Consequently, the GC has to assume that anything forward of…

> ... the GC has to assume that anything forward of any given slice into the underlying array may become accessible in the future as there is legal Go that can access it.

This property complicates the semantics of slices.Delete() in an interesting way:

  It is out of scope of the current proposal to write anything to the original tail
https://github.com/golang/go/issues/63393

Re: Go(lang): Robust generic functions on slices

#73
post #70

Earlier quoted context omitted.

None of that makes any sense. Slices don't force you to be explicit on allocating new memory in any way. you can literally do this: s := []int{} for i := range 29754 { s = append(s, i) } do you see explicit allocation of new memory? As far as I'm concerned that's not in any way more explicit than e.g. var s = new List(); foreach(var i in Enumerable.Range(0, 29754)) { s.Add(i); }

s = append(s,i) makes you realize something happened to the memory behind s. Otherwise you would simply call s.append(i)

The something that happens is that slices are not heap collections (unlike hashmap which say even less about allocations, but I'm sure you'll find an other excuse), so you can't even increment their length without returning a new copy.

I also fail to see how this would translate to

> make programmers mindful of the cost of allocating new space on array operations.

anyway. `append` is not taking in an allocator, reporting allocation failures, or reporting reallocations here. Like proper vectors, it's silently reallocating (and over-allocating) as needed.

Re: Go(lang): Robust generic functions on slices

#74
post #56

Earlier quoted context omitted.

It's golang, it has a long and proud tradition of being footguns all the way down.

It’s the C tradition, continued.

Go would have undoubtedly been called D had the name not already been taken.

Re: Go(lang): Robust generic functions on slices

#75
post #46

Earlier quoted context omitted.

> Without defending this API, the easiest way to go about avoiding bugs when working with slice mutating functions is to consider all those "fine" scenarios as not fine. Always assume that only the return value of slice mutating functions are the valid reference and the old one always invalid. The first "fine" scenario is fine because `slices.Sort` works in place, it doesn't return a value at all. And the other "fine…

By returning nil, the function makes it clear that it doesn't move the input reference

Yes, but now I have to keep track of which functions invalidate-and-return and which return nil. If I forget that `slices.Delete` returns a new slice instead of mutating in-place, the language doesn’t help me.

Re: Go(lang): Robust generic functions on slices

#76
post #67
post #30

Earlier quoted context omitted.

IMHO, all these comes from the million dollar mistake that Go made from the very beginning, with slices being a bastard between both an owned and non-owned data container. Indeed, any function accepting a slice may simply use it as an immutable view on whatever data it needs, or modify it, potentially re-alloc it, and wreak havoc by invalidating all previous references to it and/or its elements. And God forbid you pa…

my feeling with slices is that go wanted to really make programmers mindful of the cost of allocating new space on array operations. By having this weird API on slices, they force you to be explicit on allocating new memory.

> they force you to be explicit on allocating new memory.

Not at all. With e.g. `append` being a “maybe I'll allocate, maybe not, take a guess” kind of method, it's basically just like Java's `List.add` with extra steps and much worse egonomics.

Re: Go(lang): Robust generic functions on slices

#77
post #70

Earlier quoted context omitted.

None of that makes any sense. Slices don't force you to be explicit on allocating new memory in any way. you can literally do this: s := []int{} for i := range 29754 { s = append(s, i) } do you see explicit allocation of new memory? As far as I'm concerned that's not in any way more explicit than e.g. var s = new List(); foreach(var i in Enumerable.Range(0, 29754)) { s.Add(i); }

s = append(s,i) makes you realize something happened to the memory behind s. Otherwise you would simply call s.append(i)

[deleted]

Re: Go(lang): Robust generic functions on slices

#78
post #40

> func Index[S ~[]E, E comparable](s S, v E) int { After seeing this signature, I think that Go is giving up on it's simpleness principle.

They shot themselves in the foot when they forced themselves to add generics based on bogus surveys.

Anything reusable must be generic; that’s a cost of static typing.

Re: Go(lang): Robust generic functions on slices

#79
post #36

I feel like I’m taking crazy pills. How are these APIs even remotely defensible? slices.Sort(s) // fine slices.Compact(s) // broken s = slices.Compact(s) // fine s := slices.Compact(s) // broken (!!!) slices.Delete(s, …) // broken s = slices.Delete(s, …) // fine How is one intended to remember which functions require overwriting (due to invalidating) their input and which don’t? Why does the language make it so easy…

It's golang, it has a long and proud tradition of being footguns all the way down.

To reiterate, absolutely.

Re: Go(lang): Robust generic functions on slices

#80
post #39

Earlier quoted context omitted.

Without defending this API, the easiest way to go about avoiding bugs when working with slice mutating functions is to consider all those "fine" scenarios as not fine. Always assume that only the return value of slice mutating functions are the valid reference and the old one always invalid. This is not always completely accurate, but it is very useful in that, it is also never "wrong".

> Without defending this API, the easiest way to go about avoiding bugs when working with slice mutating functions is to consider all those "fine" scenarios as not fine. Always assume that only the return value of slice mutating functions are the valid reference and the old one always invalid. The first "fine" scenario is fine because `slices.Sort` works in place, it doesn't return a value at all. And the other "fine…

The nil return assignment is a compile time error, so easy to spot. It is not supposed to be the defence of the API or a silver bullet, just a practical rule that can save you some hustle.
Post reply on HN