Faced with these 2 wonderful options, most people simply decide to copy or write again the same piece of code...
interface{} could be avoided by having an interface that reflects the behavior you actually need from the types involved in the algorithm. If you need to only test for equality, you could have an algorithm that works on []Eq. That is one of the downsides to Go, unlike Haskell, interfaces are usually defined by the consumer, which makes negotiating common interfaces like Ord and Eq difficult.
It's also a good way to make bugs proliferate throughout your code base. IIRC bug count correlates well with line count regardless of the language(!).
The idea of []Eq sounds nice, but again, the problem is that I can't pass an []X to a function that wants an []Eq, I need to build a new array and copy all my elements over. This is correct from the type-theory point of view (as slices are not covariant) , but of course Go doesn't offer anything else that would be more user friendly and still correct, such as a covariant read-only slice that we could use instead.
In 2019, having a generic implementation of the algorithm is also "a good way to make it apparent what the code is actually going to do and the associated cost of the solution" in most languages.
On top of that, it helps you have less code (easier to check, code is a liability), skip manual text-template-generated code pre-process steps, let's you code what you know you want without a context switch to copy something that you've already written for another type, and stop bugs stemming from e.g. changing things in one implementation of the same algorithm and not another (because you're forced to have 10 implementations for different types).
And copying has been an anti-pattern since the times of Algol. Except if it's replaced by the "wrong abstraction" prematurely. But nobody ever called having a generic version of an algorithm "the wrong abstraction".
Go is full of special blessed functions and ad-hoc handling because of the lack of generics.
>If you need to merge two lists you can just write a method that does that. Publish it as a package and reuse it.
Only it wont work in any other type of list contents (or it will revert to interface{} and lose type safety and speed). Which is the parent's point.
Contrast that with C++ where all containers are implemented in C++ without cheating.
Then write it in assembly and damn all the abstractions. /S
It’s madness. When writing application software in any language, you’re already umpteen levels of abstraction away from what’s really happening under the hood. You call a library that calls a library that calls another library over FFI that itself dynamically links to a function written in another language that’s all compiled down to object code that runs in little tiny timeslices chosen by a scheduler in an operating system that abstracts away the hardware running on a CPU that actually rewrites instructions using microcode to actually run on an internal RISC core but one more level of abstraction on top of all this is apparently where everything will go wrong.