News for hackers. Stuff that matters.
Re:a[low:high:max] in Golang – A Rare Slice Trick by gleenn on Saturday March 18, 2023 @01:30PM (#35211184)
I don't know much Go but what does it mean to "accidentally" append to an immutable array? Are there just no particular guards for arrays and they are saying something which should be treated as immutable?
Re:a[low:high:max] in Golang – A Rare Slice Trick by UncleEntity on Saturday March 18, 2023 @01:37PM (#35211257)
I know even less go but it sounds like it prevents accidentally writing past the end of the array by making a copy and appending to that.
Re:a[low:high:max] in Golang – A Rare Slice Trick by tedunangst on Saturday March 18, 2023 @01:43PM (#35211325)
There are no immutable arrays. If you have a slice of capacity ten with four elements, you can write a fifth element to it. If you reduce the capacity to four, then a new slice must be allocated to store five elements.

The backing store for a slice is just a pointer. The rule is you don't give people pointers that you don't want them to write to.

Re:a[low:high:max] in Golang – A Rare Slice Trick by andrewstuart2 on Saturday March 18, 2023 @01:47PM (#35211385)
That's just not true. There are immutable arrays in length. [4]int cannot be appended to. The backing of all slices are array types and array doubling is used for appends that go beyond the capacity.

https://go.dev/ref/spec#Array_types

Re:a[low:high:max] in Golang – A Rare Slice Trick by tedunangst on Saturday March 18, 2023 @02:29PM (#35211770)
Yeah, that was a mistake.
Re:a[low:high:max] in Golang – A Rare Slice Trick by yencabulator on Sunday March 19, 2023 @10:06PM (#35226378)
No array can change its length, the length is part of the type. There really are no "immutable array"s.
Re:a[low:high:max] in Golang – A Rare Slice Trick by Groxx on Saturday March 18, 2023 @01:44PM (#35211341)
Append mutates or copies-and-appends based on the capacity of the slice it's given. And since slicing an array or a slice gives you just a view, not a copy, it can cause "spooky action at a distance" and mutate things you didn't expect, especially if it was a slice with extra capacity of data used somewhere else. Which is what this article is describing.

Plus the knowledge of the change in length (to see the appended data) is only visible with the returned slice (a new view with the larger length), but the underlying data is changed either way.

It's not often a source of errors, but when it is it can be extremely hard to diagnose.

Re:a[low:high:max] in Golang – A Rare Slice Trick by DougBTX on Saturday March 18, 2023 @03:29PM (#35212282)
So in other words, appending to a slice may overwrite data in the middle of a different slice?
Re:a[low:high:max] in Golang – A Rare Slice Trick by Groxx on Saturday March 18, 2023 @03:31PM (#35212296)
Yep. Depending on how the slice was constructed, not how it is used.

Slices (largely) behave like this in most languages, Go's contribution is mostly that slices and append are ubiquitous, so pretty much every Go coder is exposed to it. Prior to generics, literally any alternative was so much more work they essentially haven't been used.

Re:a[low:high:max] in Golang – A Rare Slice Trick by iainmerrick on Saturday March 18, 2023 @04:15PM (#35212695)
Slices (largely) behave like this in most languages

Is that really the case? I can’t think of many languages that let you construct a view into the middle of array, pass that view as an argument to a function call, and allow that function to add new values into your array via the view.

In Python or JS, for example, the “slice” would just be a brand new array, right?

Re:a[low:high:max] in Golang – A Rare Slice Trick by Groxx on Saturday March 18, 2023 @04:53PM (#35212994)
If the slice syntax creates a copy, I would argue that it's simply syntactic sugar for array copying, not actually producing a slice (i.e. there is no slice type). But yes, `somefunc(ary[1:])` in Python produces a copy, not a reference to the underlying value. You could build a more "true" slice class, but the builtin stuff doesn't do that. JavaScript is similar.

Java however has `List<>.sublist` which largely behaves like Go: https://docs.oracle.com/javase/8/docs/api/java/util/List.htm... . Sometimes these kinds of things are also referred to as "views". Go calls them slices though, and this is a Go article.

edit: C# apparently has "spans": https://learn.microsoft.com/en-us/archive/msdn-magazine/2018...

Re:a[low:high:max] in Golang – A Rare Slice Trick by klodolph on Saturday March 18, 2023 @05:12PM (#35213156)
Another example is typed arrays in JavaScript, which have both .slice() and .subarray(). One creates a copy, and one creates a new view into the same underlying memory.
Re:a[low:high:max] in Golang – A Rare Slice Trick by iainmerrick on Saturday March 18, 2023 @06:36PM (#35213860)
You can’t change the length of a JS ArrayBuffer.
Re:a[low:high:max] in Golang – A Rare Slice Trick by morelisp on Saturday March 18, 2023 @07:48PM (#35214410)
ArrayBuffers have a byteLength and a maxByteLength and this is similar to Go's len and cap, with the `resize()` being equivalent to a reslice or append on the subview.
Re:a[low:high:max] in Golang – A Rare Slice Trick by iainmerrick on Saturday March 18, 2023 @08:24PM (#35214633)
The difference is that those operations are on the root ArrayBuffer in JS, not the TypedArray views on that buffer.

In Go, the underlying array is fixed, and each slice independently has resize/reallocate operations.

The JS approach seems better and more comprehensible to me. (I note also that MDN lists “resizable” and “maxByteLength” as new experimental features.)

Re:a[low:high:max] in Golang – A Rare Slice Trick by iainmerrick on Saturday March 18, 2023 @06:34PM (#35213846)
It doesn’t look like any of those allow you to arbitrarily add and remove elements from the parent list in the same way Go does (if I understand Go slices correctly).

Java does let you make modifications, but only under very tight restrictions:

The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list. (Structural modifications are those that change the size of this list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.)

I take that to mean the parent list can’t be modified at the same time, nor can multiple views be modified.

So, I think Go’s ability to do those structural modifications on slices is rather unusual (as well as error-prone and not obviously useful).

Re:a[low:high:max] in Golang – A Rare Slice Trick by morelisp on Saturday March 18, 2023 @07:19PM (#35214216)
Go slices cannot change the structure (len or cap) of their "parent" slice. (Unlike Java, the semantics of every operation are even well-defined.) In some sense this is the root of the problem - the "child" slice can start using spare capacity it 'inherited' with no way to tell the parent it has done so, and the parent may inadvertantly pass some of its length as capacity down to its child.
Re:a[low:high:max] in Golang – A Rare Slice Trick by iainmerrick on Saturday March 18, 2023 @07:31PM (#35214304)
You can't insert or remove elements, but you can overrun the buffer??

I have a hard time thinking of a situation when that would be useful at all, let alone useful enough to be the default and widely-used behaviour! If I want to let the callee add stuff to the end of my list, can't I just pass them the whole list and let them modify that?

Re:a[low:high:max] in Golang – A Rare Slice Trick by morelisp on Saturday March 18, 2023 @07:41PM (#35214361)
You cannot "overrun the buffer" nor can a callee add stuff to the end of your list; you can give them a memory buffer with unused space and they may use it, and then you can also mistakenly use it later.

Honestly, you seem to be too detached to understand this. If you really want to know how it works go through some official Go documentation.

Re:a[low:high:max] in Golang – A Rare Slice Trick by iainmerrick on Saturday March 18, 2023 @08:06PM (#35214523)
I coincidentally was just looking at some docs, and I think I have a handle on it (to it?:) now.

The specific strange design decision in Go is that you can create a child slice that has its own starting offset and length, but may or may not inherit its parent’s capacity. Just because it’s well-defined doesn’t mean it isn’t tricky.

Even if you kept the semantics the same but made cap=len for subslices by default, surely that would be an improvement. Rather than a “rare slice trick”, it ought to be normal. Or is there an advantage to the current default that I’m overlooking?

Re:a[low:high:max] in Golang – A Rare Slice Trick by Groxx on Saturday March 18, 2023 @10:12PM (#35215304)
cap==len is effectively the default - most slicing is done with [start:end], not [start:end:additional-cap]
Re:a[low:high:max] in Golang – A Rare Slice Trick by iainmerrick on Saturday March 18, 2023 @11:28PM (#35215737)
No it’s not -- I just checked the language specification and it says:

After slicing the array a

a := [5]int{1, 2, 3, 4, 5}

s := a[1:4]

the slice s has type []int, length 3, capacity 4

Many of us here are arguing that the default should be cap==len==3, but it’s not, it’s 4.

Re:a[low:high:max] in Golang – A Rare Slice Trick by Groxx on Monday March 20, 2023 @12:08AM (#35227172)
Welp. TIL.

Yeah, that's much more error prone I think. It's the sort of thing that only kinda makes sense on zero-valued arrays... and even then it's dubious at best.

Bleh. I'm gonna go review some old code now D:

Re:a[low:high:max] in Golang – A Rare Slice Trick by morelisp on Sunday March 19, 2023 @06:08AM (#35217560)
I mean, if you want Java, it's right there.

The advantage is that you get the fast path by default. Since Go programs are generally not awash in buffer reuse bugs despite its semantic trickiness, this seems like something different languages can reasonably prioritize differently.

Re:a[low:high:max] in Golang – A Rare Slice Trick by masklinn on Saturday March 18, 2023 @04:25PM (#35212778)
> Slices (largely) behave like this in most languages

Only on assignment, not in appending.

> Go's contribution is mostly that slices and append are ubiquitous

Go's contribution is the conflation of slices and vectors, which in most languages are separate (or really most languages only have the latter and don't provide access to backing arrays, thus precluding this specific confusion).

Re:a[low:high:max] in Golang – A Rare Slice Trick by klodolph on Saturday March 18, 2023 @05:21PM (#35213229)
In C++, until C++20’s std::span, you would just use std::vector for stuff you can modify and const std::vector for stuff you can’t.

I think the real problem is that Go’s type system doesn’t catch the common error of keeping a reference to something you don’t own, or similar errors. C# catches some of these errors by letting you return a IReadOnlyList<T> or some other restricted type. Golang has ways to narrow certain types, but the slice type is primitive and has no narrowed read-only variation. C# instead forces you to pay a higher runtime cost, because you’re paying a lot more for indirection with IReadOnlyList<T>.

People fret about the difference between T[] and List<T> in C#, and they do come with different performance characteristics… Go’s choice to use slices everywhere does have a certain advantage that you’re getting the fast path everywhere, and you’re spending less time thinking about which one to use.

Not trying to say that Go is “right” here, it’s just my viewpoint that these language design decisions are rational.

Re:a[low:high:max] in Golang – A Rare Slice Trick by or10nx on Sunday March 19, 2023 @02:24AM (#35216534)
> People fret about the difference between T[] and List<T> in C#

That doesn't align with my experience. I've worker for small (4 programmers) and large (100s of programmers) C# shops, and I don't recall people "fretting" about T[] and List<T>. People see T[] as a non-growable, less useful version of List<T>. HashSet<T> vs List<T> seems to cause much more trouble for novice (and sometimes experienced) C# programmers.

Re:a[low:high:max] in Golang – A Rare Slice Trick by klodolph on Sunday March 19, 2023 @11:16PM (#35226828)
I'm thinking more about the places where people care about performance, since accessing a T[] is faster than accessing a List<T> (less indirection).
Re:a[low:high:max] in Golang – A Rare Slice Trick by TwentyPosts on Saturday March 18, 2023 @01:48PM (#35211391)
Go has no way to ensure "const correctness". If you pass around a slice (which you'll be doing a lot, since it's essentially Go's equivalent of a vector), everyone can modify the slice however they please. It's just a fat pointer.

So yes, something which should be treated as immutable.

The way Go works here easily leads to bugs, especially when concurrency is involved and slices get captured or used by goroutines.

If you send a slice to a function, they essentially receive a pointer pointing at the same block of memory as in your calling function. They can modify it. If they append to the slice, however, and surpass the capacity of the slice they received, then their slice gets reallocated and doesn't point at the same block of memory anymore.

In other words, if you receive a slice, append to it, and then change the first value of the slice, then this might modify a slice (or array) somewhere completely different, depending on whether your append surpasses the allocated capacity of the slice or not.

Welcome to Golang.

Re:a[low:high:max] in Golang – A Rare Slice Trick by Groxx on Saturday March 18, 2023 @01:53PM (#35211430)
But also strings are actually immutable sometimes (at the very least when hardcoded), and they can be converted to a `[]byte` slice that looks mutable, but panics when modified. AFAIK no other slice-like data in Go behaves like this.

Fun!

Re:a[low:high:max] in Golang – A Rare Slice Trick by tedunangst on Saturday March 18, 2023 @02:37PM (#35211841)
I don't think you can convert a string to []byte without unsafe except by copying?
Re:a[low:high:max] in Golang – A Rare Slice Trick by throwaway894345 on Saturday March 18, 2023 @10:45PM (#35215476)
Agreed. You can copy a string into a byte slice, but I don’t think you can convert one into a byte slice that panics on mutation.
Re:a[low:high:max] in Golang – A Rare Slice Trick by morelisp on Sunday March 19, 2023 @06:01AM (#35217533)
Is it panicking on mutation because Go has some special logic, or is it panicking because the text segment is PROT_READ? If it's the latter, sure you can do that too.
Re:a[low:high:max] in Golang – A Rare Slice Trick by throwaway894345 on Sunday March 19, 2023 @06:51PM (#35224562)
I don’t know what PROT_READ is, but as far as I know, Go doesn’t allow you to convert a string into a byte slice apart from ‘unsafe’ (and presumably you’re free to modify that without panicking, but even if not, you used unsafe so the onus is on you to know what you’re doing). There’s syntax support for creating a byte slice by copying a string’s data, but that won’t panic on mutation.
Re:a[low:high:max] in Golang – A Rare Slice Trick by wyager on Saturday March 18, 2023 @02:29PM (#35211764)
This is a pretty good vignette of how Go is designed. "Users don't need this feature (immutability, polymorphism, etc.), let's not include it. Ah crap, turns out we actually need it for a core language feature. Is there a lesson we can take away here? Nah, just make an ad-hoc implementation of the functionality in this one place."
Re:a[low:high:max] in Golang – A Rare Slice Trick by Groxx on Saturday March 18, 2023 @02:32PM (#35211790)
"X for me but not for thee" is very much the vibe Go gives me, yeah.

I mean, I have completely replaced my adhoc Python use with it and I'm much happier. But it's terrifying to use to build large systems.

Re:a[low:high:max] in Golang – A Rare Slice Trick by ikiris on Saturday March 18, 2023 @03:43PM (#35212414)
Part of it is that "large systems" are almost all combinations of small systems with proto boundaries, so it's not much of an actual risk unless you're making giant monolith code.
Re:a[low:high:max] in Golang – A Rare Slice Trick by andrewflnr on Saturday March 18, 2023 @06:30PM (#35213804)
"Proto" meaning protobuffers?
Re:a[low:high:max] in Golang – A Rare Slice Trick by konart on Sunday March 19, 2023 @03:20AM (#35216785)
I think it rather "communication via some protocol" in general.

In other words if we are not talking about giant monolith - we have N small systems to begin with.

Re:a[low:high:max] in Golang – A Rare Slice Trick by ikiris on Sunday March 19, 2023 @04:36PM (#35223300)
I meant specifically protobuffers in that case, but sure, that works.
Re:a[low:high:max] in Golang – A Rare Slice Trick by avgcorrection on Saturday March 18, 2023 @04:57PM (#35213027)
For what it’s worth it sounds no worse than Java: you have immutable strings but you have no way to enforce that you can’t modify something that you have recieved, which is why the underlying char array for a string has to be defensively copied if the user asks for `asArray()` or whatever it is.

Strings being immutable doesn’t seem like a special case, though. The underlying mutable array is just encapsulated, which is something that you can implement yourself. (But reflection… maybe you can break the rules with reflection.)

Re:a[low:high:max] in Golang – A Rare Slice Trick by Groxx on Saturday March 18, 2023 @10:07PM (#35215277)
No worse, possibly better, yeah. In a fair number of ways I prefer it over Java, e.g. reflection being so limited makes code dramatically easier to understand with confidence, because a large number of common reflection shenanigans in Java simply can't exist at all in Go. The clean slate and lack of inheritance also means the incredible towers of inheritance insanity simply don't exist - it's wonderful.

But I work on a pretty big system. The near-inability to both efficiently and safely abstract things is a big problem, and it'll be years before mature uses of generics truly start to address that... when it even can. Then I miss Java quite a lot. Or maybe more accurately Kotlin. Or sophisticated code generation and bytecode modification. Or MAT. Or...

Re:a[low:high:max] in Golang – A Rare Slice Trick by Alphaeus on Saturday March 18, 2023 @11:02PM (#35215578)
I don't think you can reflect onto the standard library anymore, with the recent "strong encapsulation".
Re:a[low:high:max] in Golang – A Rare Slice Trick by saagarjha on Sunday March 19, 2023 @03:12AM (#35216757)
Java gets a pass because it was early. Go had time to learn from its mistakes and…just refused to do so.
Re:a[low:high:max] in Golang – A Rare Slice Trick by klodolph on Saturday March 18, 2023 @05:08PM (#35213127)
Const is a hard language feature to design well. There are a lot of pitfalls in the way const is used in C++ and TypeScript (I’m including “readonly” in the discussion).

Rust gets it right, but Rust is relatively complicated.

The languages which are most similar to Go are Java and C#, both of which also lack const types, or have a very limited version of constness.

Re:a[low:high:max] in Golang – A Rare Slice Trick by TwentyPosts on Sunday March 19, 2023 @05:10AM (#35217290)
What are some of these pitfalls in C++'s const design? I assume the issue is that it's difficult to guarantee const correctness as long as you have the ability to fiddle with pointers?

Function signatures showing when a parameter gets modified and when not (such as in Rust, or even in properly const-speckled C++) would benefit Go, but I'm not sure how difficult it'd be to implement that. I could see it being difficult considering that one of Go's core features (slices) results in opaque and overlapping memory ownership.

Re:a[low:high:max] in Golang – A Rare Slice Trick by cdogl on Saturday March 18, 2023 @08:08PM (#35214533)
I don’t think you can fairly call go’s generics an “ad-hoc” implementation. They added syntax for it.
Re:a[low:high:max] in Golang – A Rare Slice Trick by jeffparsons on Saturday March 18, 2023 @09:04PM (#35214883)
I think in this context "ad hoc" refers to the context in which that syntax was added. IIRC the original creators were against generics ever being added to Golang, so they wouldn't have thought about their eventual introduction when choosing Go's initial syntax. The result is that the generics that eventually were added feel awkward and "bolted on" to many people.

(I don't have any strong opinions on it personally, because I'm not invested in that particular ecosystem. I'm merely attempting to distil what I've heard from other people.)

Re:a[low:high:max] in Golang – A Rare Slice Trick by preseinger on Saturday March 18, 2023 @10:10PM (#35215293)
go's authors were never against generics, they just didn't consider them a table stakes feature

go's generics are pretty good, i've never heard anyone disparage them as "bolted on" or hard to use

Re:a[low:high:max] in Golang – A Rare Slice Trick by kjksf on Saturday March 18, 2023 @11:13PM (#35215656)
Maybe you shouldn't play game of telephone like that.

Go generics are exceptionally well designed.

The reason it took so long for Go to get generics is because Go designers took their time to arrive at a design that fits with the rest of Go.

It's not rushed, it's not "bolted on".

They did several designs that they rejected before they accepted the design that got implemented.

Re:a[low:high:max] in Golang – A Rare Slice Trick by wyager on Saturday March 18, 2023 @11:59PM (#35215901)
This is a truly unreal level of blub paradox and/or brown nosing. It's almost a complete inversion of reality.

Go's maintainers had to be beaten into bolting on a poorly-done implementation of polymorphism over like a decade. I can't imagine anyone who's used any language with polymorphism baked in to the design describing Go's implementation as "exceptionally well designed". If this is a Poe's law thing and you're just joking, then you got me.

Re:a[low:high:max] in Golang – A Rare Slice Trick by fooster on Sunday March 19, 2023 @01:17AM (#35216273)
If you are going to make such a claim then please back it up rather than state opinions as facts.
Re:a[low:high:max] in Golang – A Rare Slice Trick by the_gipsy on Sunday March 19, 2023 @03:46AM (#35216914)
Re:a[low:high:max] in Golang – A Rare Slice Trick by Groxx on Sunday March 19, 2023 @07:34PM (#35224979)
Yeah. Go generics are quite crippled. I'm still very, very glad that they exist though - even crippled, they're a vast improvement.

I think they took a... rather extremely-conservative step towards what their generics will eventually be, at which point they'll probably be pretty reasonable. As it stands now they're kinda weird and and very incomplete, though thankfully simple (in behavior).

They did at least leave syntactic and semantic room to improve them though, so I think it'll happen eventually. It was cut off at a safe point. They just need to be brow-beaten further.

Re:a[low:high:max] in Golang – A Rare Slice Trick by slimsag on Sunday March 19, 2023 @10:15PM (#35226443)
I'm a big fan of Go, have been using it since r59 (pre 1.0), professionally working with it the past ~8 years at one of the earliest companies to adopt it.

The fact that you cannot have a generic method at all, and instead have to rewrite methods as functions.. that seems like a pretty glaring flaw.

I'm happy Go got some form of generics, definitely, but they really do feel bolted on to the language.

Re:a[low:high:max] in Golang – A Rare Slice Trick by throwaway894345 on Saturday March 18, 2023 @11:19PM (#35215700)
I haven’t heard many gripes about the syntax, but I have heard plenty of gripes about how long it took them to add them. Frankly my biggest grievance with Go’s generics is the goofy dictionary implementation that makes performance difficult to reason about. I also think the people who complain the loudest about missing generics were often just unaware that there are other (often simpler) ways to achieve the same thing—it often feels like people were just angry that Go didn’t look exactly like $theirFaveLang. There are definitely some use cases that are improved by generics, but most complaints were about avoiding writing relatively simple loops (iterator chains are more readable in the trivial cases, but quickly become less readable particularly when you need to short circuit on errors and so on—a loop is often cleaner, clearer, and faster to implement).
Re:a[low:high:max] in Golang – A Rare Slice Trick by hdhrufjdi on Sunday March 19, 2023 @07:10AM (#35217848)
I had to add a code generator, because of the lack of generics
Re:a[low:high:max] in Golang – A Rare Slice Trick by throwaway894345 on Sunday March 19, 2023 @06:53PM (#35224585)
Yeah, like I said, there are some cases where generics are genuinely helpful. It’s just a small percentage of the code I tend to write (generics have been out for quite a while, and I still avoid them in Go despite my familiarity with languages like Rust, TypeScript, etc that use them extensively).
Re:a[low:high:max] in Golang – A Rare Slice Trick by wyager on Sunday March 19, 2023 @12:05AM (#35215929)
When you finally cave and jury-rig bolt on language features like a decade after the fact, that's not quite as ad-hoc as what they had for the first decade (generics for builtins only), but it's still pretty ad-hoc in the sense of "not being derived from a coherent theory"
Re:a[low:high:max] in Golang – A Rare Slice Trick by Thaxll on Saturday March 18, 2023 @09:15PM (#35214955)
Which modern languages do const properly?
Re:a[low:high:max] in Golang – A Rare Slice Trick by wyager on Sunday March 19, 2023 @12:02AM (#35215915)
Rust and Haskell come to mind.
Re:a[low:high:max] in Golang – A Rare Slice Trick by pjmlp on Sunday March 19, 2023 @05:25AM (#35217370)
Any ML derived language.
Re:a[low:high:max] in Golang – A Rare Slice Trick by throwaway894345 on Saturday March 18, 2023 @11:08PM (#35215614)
I mean, what language got everything exactly right from day 0? Yes, Go started from a minimalist position, but that’s been a wildly successful decision—Go lacks a lot of the cruft of other languages. Go is an easily understandable language, it compiles super quickly, it has top notch tooling (e.g., compiling almost any Go project on any system with ‘go build’ and even cross compile by changing a couple of env vars), it compiles to relatively small[^1] static binaries by default, and it does all of this with pretty good performance.

[^1]: Someone is going to come in with some rant about how big a “hello world” binary is compared to C, as if this is emblematic of some real-world use case.

Re:a[low:high:max] in Golang – A Rare Slice Trick by TwentyPosts on Sunday March 19, 2023 @05:20AM (#35217341)
A cynical view is that 80% of Go's success is a result of the excellent tooling, and the fact that it's associated for and pushed by Google, while the underlying language is "mediocre" at best.

In theory I agree with Go's minimalist perspective, it just also feels inconsistent, and like they made a whole lot of bad decisions along the way. Favoring C-style enums over proper sum-types is one of the biggest one, and ties into Go's error handling, which continues to be a major talking point.

Re:a[low:high:max] in Golang – A Rare Slice Trick by morelisp on Sunday March 19, 2023 @05:52AM (#35217493)
Is the excellent tooling not also a property of a minimal language? Lisp has the "least" syntax/semantics and the best tooling, because it's so easy to write tools. Go has more complex syntax/semantics, but still much easier to wrangle e.g. control flow out of an AST, compared to other Algol derivatives.
Re:a[low:high:max] in Golang – A Rare Slice Trick by throwaway894345 on Sunday March 19, 2023 @06:48PM (#35224528)
I fully agree with the enums thing—that’s by far my biggest gripe with the language. But even still, I can be a lot more productive in Go than I can in any other mainstream language, including languages that have sum types (Rust, OCaml, etc) which isn’t to say sum types make me less productive but rather that those languages have other productivity issues that outweigh the benefits afforded by sum types. Basically, Go gets a whole lot of little things right that most languages miss, but everyone fixates on “generics” and “sum types” which are, overall, quite small things IMHO.
Re:a[low:high:max] in Golang – A Rare Slice Trick by pjmlp on Sunday March 19, 2023 @05:24AM (#35217357)
Ironically that is exactly the same approach they had with C, so at least they are consistent.

"Although we entertained occasional thoughts about implementing one of the major languages of the time like Fortran, PL/I, or Algol 68, such a project seemed hopelessly large for our resources: much simpler and smaller tools were called for. All these languages influenced our work, but it was more fun to do things on our own."

-- https://www.bell-labs.com/usr/dmr/www/chist.html

Re:a[low:high:max] in Golang – A Rare Slice Trick by morelisp on Saturday March 18, 2023 @04:05PM (#35212601)
This is misleading enough it's a lie; to do this you need to use a function named `unsafe.Pointer` with a type named `reflect.StringHeader`, not just `[]byte` and `string`.
Re:a[low:high:max] in Golang – A Rare Slice Trick by Groxx on Saturday March 18, 2023 @04:23PM (#35212760)
It means there is special protection for strings that is not available to anything else, and that library-authors get panic reports about immutable byte slices. To that degree, it's something that the specialized type system does not protect adequately.

But yes, AFAIK this requires use of `unsafe`. Which does change things, and puts the blame for misuse squarely on the immutable-byte-slice creators.

Re:a[low:high:max] in Golang – A Rare Slice Trick by morelisp on Saturday March 18, 2023 @05:08PM (#35213129)
> It means there is special protection for strings that is not available to anything else

Lots of languages have no C/C++-style const objects yet immutable strings. It's weird to pick on Go specifically for this. (Since it's the JVM model, at this point it may even be a majority of mainstream languages.)

If you mean specifically the panic when modifying a static string, is this not just the default `mprotect` on rodata? You can do that to any memory page you want. It's a feature of the kernel's memory management, not the type system or even the language runtime.

> library-authors get panic reports about immutable byte slices.

I'm really skeptical this happens in any meaningful amount. Go offers the feature to transform a byte slice you know you won't want anymore into a string without a memory allocation; or to pass a string to a function which wants a []byte and will not mutate it, usually to wrap an optimized implementation working on both strings and []byte. Modifying a string's contents is always undefined behavior, even if it doesn't panic immediately - the compiler will assume strings are immutable and make "as if" judgements accordingly.

Re:a[low:high:max] in Golang – A Rare Slice Trick by MrBuddyCasino on Sunday March 19, 2023 @03:43AM (#35216901)
> Since it's the JVM model

Java model. Kotlin collections are immutable by default (not sure about Scala, but I suspect they behave the same).

Re:a[low:high:max] in Golang – A Rare Slice Trick by morelisp on Sunday March 19, 2023 @05:41AM (#35217442)
No, I'm pretty sure it's the JVM model. Kotlin collections are "immutable" in that they have no mutating accessors, which is not the same thing as a memory region defined to be immutable by the specification which can be relied on to e.g. trivialize certain compiler optimizations.

You can also look at what kinds of things are allowed in class constant pools; you will not find any collections.

https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.ht...

Re:a[low:high:max] in Golang – A Rare Slice Trick by MrBuddyCasino on Sunday March 19, 2023 @07:46AM (#35218057)
If you specifically mean RO memory pages, then yes, thats not the same thing. The language and the runtime will enforce collection immutability though, throwing an exception if you attempt to mutate. Poking at the underlying byte code will defeat this, of course.
Re:a[low:high:max] in Golang – A Rare Slice Trick by throwaway894345 on Saturday March 18, 2023 @10:49PM (#35215497)
It seems pretty absurd to me that you would blame Go’s design for silly things people do with unsafe. Yes, I wish ago had more/better immutability semantics and it’s slightly odd that strings are immutable but other types are not, but griping that something bad happened when you used a package called “unsafe” is pretty silly.
Re:a[low:high:max] in Golang – A Rare Slice Trick by pstuart on Saturday March 18, 2023 @04:01PM (#35212569)
On the bright side, if one tests with race detection enabled these problems are usually made apparent.
Re:a[low:high:max] in Golang – A Rare Slice Trick by masklinn on Saturday March 18, 2023 @04:20PM (#35212729)
This issue can trivially occur in sequential code.
Re:a[low:high:max] in Golang – A Rare Slice Trick by eternalban on Saturday March 18, 2023 @05:12PM (#35213152)
"one goroutine has access to the value" :O

If you stick to "values" Go works as originally advertised (see below). With values iirc the consensus became that for high performance the channel overhead was too much of a hit and so back to locks and 'traditional' concurrency.

The concurrency issue in PLTs is not a logical puzzle. It's a performance challenge revolving around copying stuff and hand-offs. With multicores thrown in, it seems to really require addressing the challenges once and for all (as services) at the OS and possibly even hardware layer. IF we have efficiencies at the hw & os around 'message passing', any variation on CSP would address the concurrency issue, "by design", as it says below in Go's "Effective Go" documentation.

https://web.archive.org/web/20091111073232/http://golang.org...

https://web.archive.org/web/20091113154825/http://golang.org...

Share by communicating

Concurrent programming is a large topic and there is space only for some Go-specific highlights here.

Concurrent programming in many environments is made difficult by the subtleties required to implement correct access to shared variables. Go encourages a different approach in which shared values are passed around on channels and, in fact, never actively shared by separate threads of execution. Only one goroutine has access to the value at any given time. Data races cannot occur, by design. To encourage this way of thinking we have reduced it to a slogan:

Do not communicate by sharing memory; instead, share memory by communicating."

Re:a[low:high:max] in Golang – A Rare Slice Trick by chrsig on Saturday March 18, 2023 @02:06PM (#35211551)
Here's a succinct demonstration: https://go.dev/play/p/Li6_Rpe2R5L

Basically: a slice is a triple of {ptr to allocation, number of elements used, size of allocation}

the slices will share the same underlying allocation. By specifying the third parameter in the slice function, you set the capacity equal to the number of elements in your slice. This forces the next append to reallocate and copy the contents into a new region.

Without bounding the capacity when creating a new slice, the append operation could possibly continue using the same allocation, shared with the original slice. It could possibly resize and copy as well. It depends on how full the slice is, and is an implementation detail subject to change between versions.

Re:a[low:high:max] in Golang – A Rare Slice Trick by ErikCorry on Saturday March 18, 2023 @02:50PM (#35211951)
Here another illustration of how slices sometimes alias each other, sometimes not. https://mobile.twitter.com/erikcorry/status/1561635841495236...
Re:a[low:high:max] in Golang – A Rare Slice Trick by thomaslkjeldsen on Saturday March 18, 2023 @03:05PM (#35212088)
Re:a[low:high:max] in Golang – A Rare Slice Trick by Izkata on Saturday March 18, 2023 @03:16PM (#35212185)
Sounds like they're just badly describing copy-on-write.

The original slice uses no extra memory because as long as you treat it as immutable it won't actually make a copy. As soon as you try to modify it, then the copy is made and the extra memory used.

Re:a[low:high:max] in Golang – A Rare Slice Trick by mook on Saturday March 18, 2023 @03:44PM (#35212423)
Yes-ish; because go has no immutable slices, it's just copy-on-realloc. Which is if course obvious if realize that's what it's actually doing…
Re:a[low:high:max] in Golang – A Rare Slice Trick by masklinn on Sunday March 19, 2023 @03:16AM (#35216768)
> Sounds like they're just badly describing copy-on-write.

It’s not quite copy-on-write, and what it’s really describing is a workaround / safeguard.

The problem is that by default Go slices will have as large a capacity as they can based on the parent, this is a problem if you return a slice to a still-in-use slice or array, and the caller decides to use that slice as a vector (either because the contract is not well documented or because they fucked up): appending to the “borrow” will happily go and stomp over the backing array, which may be holding in-use data of an other slice or the original array.

By forcing the slice to have no extra capacity, if a caller tries to append it’ll force a “fork” by realloc-ing and avoid the issue.