News for hackers. Stuff that matters.
Re:Ten Years of “Go: The Good, the Bad, and the Meh by rowanseymour on Tuesday July 18, 2023 @12:21PM (#36774991)
Prior to generics we relied heavily on dynamic typing via interface{}, for example:

  BulkInsert(db *sql.DB, objs []interface{})
This unfortunately meant that any time you had []Foo.. you had to allocate a new []interface{} and copy over the items. Now a function like that can look like:

  BulkInsert[T any](db *sql.DB, objs []T)
And we're not wasting CPU cycles to copy the slice of []Foo. I'm struggling to see how that's code smell or less excellent than using []interface{} or duplicating the code for BulkInsert for every insertable type in our application.
Re:Ten Years of “Go: The Good, the Bad, and the Meh by Veraticus on Tuesday July 18, 2023 @12:42PM (#36775386)
Dynamic typing via interface{} is also huge Go code smell though, so...

Generics are definitely better for you. But I would say the overall pattern you're employing of bulk inserting different kinds of data structures with one function is the problem. Of course, I don't know your code so I'm sure you have a good reason for choosing what you did, but a BulkInsert of Any certainly made me raise my eyebrow.

Re:Ten Years of “Go: The Good, the Bad, and the Meh by rowanseymour on Tuesday July 18, 2023 @01:16PM (#36775984)
The reason is the same reason that anyone ever writes a generic function (outside of writing a library) - to keep code DRY and avoid duplication. There is no upside to maintaining a large number of identical function implementations, and no scenario in which that is preferable to using generics.
Re:Ten Years of “Go: The Good, the Bad, and the Meh by Veraticus on Tuesday July 18, 2023 @01:49PM (#36776547)
But you probably will have to differentiate at some point between what you're inserting. Why not just do that, instead of artificially combine it into one function?

Nevertheless I feel like I'm getting lost in the weeds of this example. Obviously DRY and less duplication is good. However, you have to strike a balance between being clever and being clear. And frankly I would prefer 3 lines of clear code to 1 line of hard-to-read code.

Re:Ten Years of “Go: The Good, the Bad, and the Meh by z0r on Tuesday July 18, 2023 @02:29PM (#36777170)
Writing and reading repetitive code leads to unintentional defects. This is why for loop syntax that directly iterates through a collection is less error prone than the equivalent loop built on indexed look-ups. "Clever" code, at least when it is shorter, is often clearer than "simple" code.
Re:Ten Years of “Go: The Good, the Bad, and the Meh by Veraticus on Tuesday July 18, 2023 @03:53PM (#36778267)
I entirely disagree; defects are created by complexity, not repetition.
Re:Ten Years of “Go: The Good, the Bad, and the Meh by soulbadguy on Tuesday July 18, 2023 @04:38PM (#36778757)
> defects are created by complexity, not repetition.

Any reference for this ?

I though there was a pretty strong connection between the total number of line of code and the number of bug... That's the whole point of DRY.

Re:Ten Years of “Go: The Good, the Bad, and the Meh by joshuamorton on Tuesday July 18, 2023 @02:44PM (#36777377)
> But you probably will have to differentiate at some point between what you're inserting. Why not just do that, instead of artificially combine it into one function?

Why prematurely optimize for differences that may not (and in practice aren't really likely to) happen?

Keep in mind that the tradeoff with generics is usually not 3 lines of clear or 1 line of hard to read, it's one line of clear code (T -> T), one line of unclear code (Interface{} -> Interface{} + casts), or n lines of complex code (concretely reimplementing the function for your particular case).

Re:Ten Years of “Go: The Good, the Bad, and the Meh by Veraticus on Tuesday July 18, 2023 @03:24PM (#36777917)
I would say that a BulkInsert(Any) is a significant premature optimization over just inserting an object as would apply specifically to that object. Because it sounds like you'd have to do weird reflection stuff on the object to determine how and what to insert where.

If you are inserting an object, you should insert it, rather than create complicated generic insert methods that morph themselves based on the object being inserted. That is idiomatic Go.

Re:Ten Years of “Go: The Good, the Bad, and the Meh by rowanseymour on Tuesday July 18, 2023 @04:01PM (#36778332)
My example was a bit simplified but basically the function takes a SQL statement and a slice of structs that use db:column field tags. It's no more doing "weird reflection stuff" than say json.Marshal
Re:Ten Years of “Go: The Good, the Bad, and the Meh by didntcheck on Tuesday July 18, 2023 @06:53PM (#36780112)
But why is it a problem (assuming the database in this example can handle any type)? If I'm writing a function that returns the 5th element of a list, I will always write that using generics, even if I know it's only used with one type (right now). Not only is it basically free (`<T> T getFifth(List<T>)` is really not any more complicated than `Foo getFifth(List<Foo>)`), it's also a separation of concerns. The logic is just about the container, so no need to complicate it with a "red herring" of a forced type