Yes
> Don't other languages renowned for their type systems do this?
No. In languages with good type systems, nominal types are very valuable. E.g. "known-immutable list" and "read-only view onto a mutable list" are very different types, but offer the same set of methods.
It's "structural subtyping", which is the type-safe equivalent of duck typing. It's a feature that allows implementations to exist without needing to know exactly every interface they implement. TFA's concern is purely theoretical.
> That's horrifying.
append() doesn't operate on arrays, it operates on slices. Arrays are fixed-length, contiguous blocks of memory that can't be appended to. Slices are backed by arrays, and if you append to a slice whose backing array is full, it will "grow" by allocating a bigger array elsewhere and copying the original data into it. This is a pretty standard data structure in most languages.
Yes, vectors are common, but they don't typically require compiler modifications to avoid mis-using them. In this case I'd make it super clear whether the method mutates the existing value or returns a new one, and "possibly both" seems like the worst of both worlds. If this behavior is preferred, it seems like `append` should take a handle to a slice pointer, possibly to a slice rooted in the stack, and write to it if replacing the underlying slice.
https://play.golang.org/p/qWCnF7d7Fl9
This is unexpected behavior at best.
Vectors are standard data structures.
Append possibly mutating in place and possibly return a new vector instead, not so much.
That's exactly what Go does.
Not to mention the ability to share a backing array between two "vectors" and to append to both. That's a Go innovation right there.
It's been pointed out in the comment right below this, but it is a bug if you do this. If the backing array has any free capacity the results will be very different from what you expect.
If you go with most languages definition of "slice", that would be pretty horrible just by itself.
But, as you said, Go's definition is different (what is also bad, just not much):
> Slices are backed by arrays, and if you append to a slice whose backing array is full, it will "grow" by allocating a bigger array elsewhere and copying the original data into it
What would be pretty much like Java's ArrayList, or Python's list if it was designed on a sane way. So, just the name would be non-standard... But the container actually migrates when it is reallocated! That's a broken design in any language.
Most languages don't have a definition of "slice" at all.
> What would be pretty much like Java's ArrayList, or Python's list if it was designed on a sane way. So, just the name would be non-standard... But the container actually migrates when it is reallocated! That's a broken design in any language.
It's not broken, it's just fast and simple. But yes, it does require you to know that Go's slices are not exactly Python's lists (which do gratuitous copies). Java's implementation does an insert on the original list (both surprising and an unnecessary copy).
If you say so. I know I should go read the docs more carefully, but this whole slice thing is confusing to me, and often yields surprises like this.
I'm not a big fan of how this was implemented.
The feature leaves me 'meh'. I don't find slices as awesome as everyone is always saying, especially without negative indices.
I guess part of it it that it isn't always clear to me when you're dealing with a slice, or an array, or whatever.
I know, when it's confusing, there's always some reasonable sounding explanation after the fact, sure, but the whole thing feels like a confusing mess.
v := a; if t { v = b }
Sadly, this doesn't work well if a is a function call :-(go fmt may not be anyone's favorite code style, but having go fmt is so much better than dealing with everyone else's favorite (incompatible!) styles.
It has no switches to disable features.
It destroys many nice single-line constructs, e.g.
if err != nil { return err }
Much of my code would be painful to read without that one.On the bright side, it artificially inflates your line count, so you appear more productive!
The ecosystem-wide consistency of go fmt is its biggest strength. Adding switches to disable features would eliminate that.
I've put a lot of thought into my style guide, and anyone who hasn't done the same has no useful insight on whether go fmt is a help or a hindrance.
Is it more powerful in some way than the auto-formatters that exist for nearly every other language?
Edit: I didn't mean this as an insult, I meant it as a genuine question. I don't know much about the Go ecosystem, and I meant to find out what Go's killer feature is in 2019, when other languages now have strong and elegant concurrency, C-like performance with higher-level syntax and without manual memory management, etc.
Of course, this is not as good for having fun programming, unless the programmer feels having written something is more fun than writing it.
Fast compilation speed, easy integration with C, and the "one large static binary" are probably my favourite features, although I do wish those binary sizes weren't quite so large...
I do not like Go the language but I can begrudgingly admire the ecosystem
I find myself choosing Go for:
- concurrency - memory footprint management - cpu speed - that nebulous feeling I want a program etched in stone (strongly typed)
I've written a lot of Api based clients. Read from a queue and update AWS permissions. Receive a callout from something and passing it along to somewhere else. Come to think of it, I've written a lot of queue-reading workers, some replaced existing Ruby workers for CPU or concurrency reasons.
I think in the space of having computers talk to computers Go is highly successful because it handles nearly everything with the standard library. So I find a lot of sdk's for specific platforms written in go that can get something going with the standard library and a single package for the platform.
But from my experience with Java, Go, and Python, I find Go a very versatile programming language. Being able to compile binaries means it's great for infrastructure deployments and CLI tools. Goroutines, concurrency, and etc make it a great choice for server/micro service use cases as well. Some can say that not having a JVM is a plus as well.
However, developers do have to be disciplined with error handling and dependency management. But one can argue that this is the case for software development in general.
IMO, the fundamental advantages of Go over competing languages in the long run are its fast compile time and its combination of native code+garbage collection.
> Structs do not explicitly declare which interfaces they implement. This is done implicitly by matching the method signatures. This design makes a fundamental error: It assumes that if two methods have the same signature, then they have the same contract.
Isn't this just duck typing? Don't other languages renowned for their type systems do this?
> There’s no ternary (?:) operator. Every C-like language has had this, and I miss it every day that I program in Go. The language is removing functional idioms right when everyone is agreeing that these are useful.
Expressive if/else is much more readable. Of course if Go doesn't have that, removing the ternary operator is a bad call.
> The tried and true approach of providing a compare method works great and has none of these drawbacks.
Hmm... except in anonymous cases. This author clearly has some Java stockholm syndrome.
> The append() function modifies the array in-place when it can, and only returns a different array if it has no place left.
That's horrifying.