zlacker

[return to "Go(lang): Robust generic functions on slices"]
1. stouse+9l3[view] [source] 2024-02-24 10:56:08
>>signa1+(OP)
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 to render function parameters unusable upon return but impossible to enforce that they aren’t used afterward?

How on earth did it take twelve years for this “simple” language to make a function to delete elements from an array, with `s = append(s[:start], s[end:]...)` having to suffice until then? How on earth does the “better” API twelve years later have such a gaping footgun? This is a core type that quite literally every program uses. How have things gone so far off the rails that “setting the obsolete pointers to nil” is such an intractable problem for end users they had to add a new keyword to the language?

For other languages I see posts where weird language corner cases bring up challenging issues that really reinforce the idea that language design is hard. Rust—for example—has unsoundness issues in corner cases of the type system. But for go, it feels like there’s a constant stream of own goals on core areas of the language where the design should have knocked it out of the park. “Simple manipulation of arrays” just should not have this many footguns.

◧◩
2. maskli+dq3[view] [source] 2024-02-24 12:19:34
>>stouse+9l3
It's pretty simple really: Go's slice API was compromised from the start by making it the unholy hybrid of a list/vector and an actual slice, it's one of the original sins of the language. And it's not really fixable.

It must be said that even splitting them properly you'd have issues as long as you mix slices and mutability (e.g. take a full slice out of a vector then delete() on the vector, the slice will see the hole). Though the issues would be less prominent.

◧◩◪
3. aatd86+WV3[view] [source] 2024-02-24 16:48:31
>>maskli+dq3
The way it is listed, it definitely looks problematic.

I had to dig a little and in fact, once we remember that a slice is a view into an array and that "some" of these methods return slices, it's actually fine.

The only issue is perhaps s:=slices.Compact() But that's not even because of the API. It's because of the short variable assignment that may allow shadowing, unfortunately.

The issue is probably overblown here.

To be even more thorough, I've had the pleasure (lol) to implement a wrapper to have some form of immutable slices so I can say that it is mitigable. Not pleasant but mitigable. (I needed to compare values of a slice stored in a map, value before retrieval from map vs. value when it had been inserted back into the map, so having aliasing was problematic since the value in the map would be updated at the same time the value retrieved was (obviously, duh!) , had to implement a copy-on-write variant).

:)

◧◩◪◨
4. maskli+eY3[view] [source] 2024-02-24 17:02:58
>>aatd86+WV3
> once we remember that a slice is a view into an array and that "some" of these methods return slices, it's actually fine.

Only in the meme sense, it does not actually solve or help solve the problem in any way.

◧◩◪◨⬒
5. aatd86+Ma4[view] [source] 2024-02-24 18:25:46
>>maskli+eY3
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.

◧◩◪◨⬒⬓
6. maskli+yg4[view] [source] 2024-02-24 19:04:53
>>aatd86+Ma4
> 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.

◧◩◪◨⬒⬓⬔
7. aatd86+wj4[view] [source] 2024-02-24 19:25:38
>>maskli+yg4
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.

[go to top]