Other than that, generics have not really solved an actual problem for me in the real world. Nice to have, but too mush fuss about nothing relevant.
Other than that, generics have not really solved an actual problem for me in the real world. Nice to have, but too mush fuss about nothing relevant.
1. Mapping SQL response to maps/structs or mapping maps/structs to SQL parameters might be useful, but that's rather trivial functionality and probably doesn't qualify as ORM. Things get harder when we're talking about complex joins and structs with relationships, but still manageable.
2. Introducing intermediate language which is converted to SQL is bad. Inevitably it will have less features. It will stay in the way for query optimisations. It'll make things much less obvious, as you would need to understand not only SQL, but also the process of translating intermediate language to SQL.
3. Automatic caching is bad. Database has its own caching and if that's not enough, application can implement custom caching where it makes sense.
In my opinion the only worthy database integration could be implemented with full language support. So far I only saw it with C# LINQ or with database-first languages (PL/SQL, etc). C# and Go are like on opposite spectrum of language design, so those who use Go probably should keep its approach by writing simple, verbose and obvious code.
It's very subjective but my gut feeling is they probably didn't expand their community much by adding generics to the language.
Second use I usually find is when I have some structs with some behavior and some associated but parameterizable helper. In my case, differential equations together with guess initializers for those differential equations. You can certainly do it without generics, but then the initial guess can be the wrong shape if you copy paste and don't change the bits accordingly. The differential equation solver can then take equations that are parameterized by a solution type (varying in dimension, discretisation and variables) together with an initializer that produces an initial guess of that shape.
Finally, when your language can do a bit of introspection on the type or the type may have static methods or you have type classes, you can use the generic to control the output.
Basically, they are useful (like the article implies) when you want to statically enforce constraints. Some people prefer implicitly enforcing the constraint (if the code works the constraint is satisfied) or with tests (if the tests pass the constraint is satisfied). Other people prefer to have the constraints impossible to not satisfy.
Programming is about building abstractions, abstractions are a way to reduce boilerplate.
Why do we need `func x(/* args / ) { / body */ }`, when you can just inline the function at each callsite and only have a single main function? Functions are simply a way to reduce boilerplate by deduplicating and naming code.
If 'reducing boilerplate is bad', then functions are bad, and practically any abstraction is bad.
In my opinion, "reducing boilerplate is bad in some scenarios where it leads to a worse abstraction than the boilerplate-ful code would lead to".
I think you have to evaluate those things on a case-by-case basis, and some ORMs make sense for some use-cases, where they provide a coherent abstraction that reduces boilerplate... and sometimes they reduce boilerplate, but lead to a poor abstraction which requires more code to fight around it.
Can you elaborate and give some examples of why reducing boilerplate is generally "bad"?
Generics have been tremendously helpful for me and my team anytime we are not satisfied with the existing ecosystem and need to write our own library code. And as time goes on the libraries that everyone uses will be using generics more.
There are two things an sql lib must do to be very useful: prepared statements and mapping results. That’s enough.
That's my experience as well in C# - most of other usages of generics are painful to maintain in the long run. I've had most problems with code that joins generics with inheritance.
> That is most definitely not true.
I think at best, you could say that Go is a multi-paradigm language.
It's possible to write Go in an object oriented style.
It's also possible to write programs with no methods at all (although you'd probably have to call methods from the standard library).
That's in contrast to a language like Java or Ruby where it's actually impossible to avoid creating objects.
Creating objects on the heap isn't the only defining feature how a language does OOP or not.
You should convert when you reach the point "I wish I had that code but with this other type". Even then, sometimes interfaces are the right answer, rather than generics.
If you need to make code more complex just to reduce boilerplate, it's a bad thing. If you managed go make code simpler and reduced boilerplate at the same time, it's a good thing.
And boilerplate might be a good thing when you need to type something twice and if you would make error once, the whole thing wouldn't work, so basically you'll reduce the possibility of typo. It might look counter intuitive. Just unrelated example: recently I wrote C code where I need to type the same signature in the header file and in the source file. I made mistake in the source file, but I didn't make the same mistake in the header file and the whole program didn't link. I figured out the mistake and corrected it. Without this boilerplate it's possible that I wouldn't notice the mistake and "helpful" autocomplete would keep the mistake forever. That's how HTTP Referer header made it into standards, I guess.
I.e., when you want to write a function that take some slice of any type T that implements interface I, such that []T is a valid input instead of just explicitly []I.
No, it still feels like programming with a blindfold on and one hand tied behind my back. I truly don't get it. I've worked with a lot of languages and paradigms, am not a zealot by any means. Other than fast compiles and easy binary distribution, I don't see any value here, and I see even experienced Go programmers constantly wasting time writing unreadable boilerplate to work around the bad language design. I know I must be missing something because some people much smarter than me like this language, but... what is it?
They are far closer to Rust in some areas (definitely not in type inference sadly, but F# is a different story) than it seems.
Of course if one declares that they are an expert in a dozen of languages, most of which have poorly expressive type systems, the final product will end up not taking advantage of having proper generics.
For libraries (that adopt generics): yes they can be complicated. But using them is mostly zero-effort and gets rid of a ton of reflection.
i think Go having a pretty bad implementation of parametric polymorphism (a programming concept from the 70s) is probably the root cause here
if it is unreadable, in Go, probably the most readable language used today, i would question the aforementioned experience.
I've seen ~100 line HTTP handler methods that are implemented using generics and then a bunch of type-specific parameters inevitably get added when the codepaths start to diverge and now you've got a giant spaghetti ball of generics to untangle, for what was originally just trying to deduplicate a few hundred lines of code.
If you "other than" two huge-for-many-use-cases good things, sure, it might look bad. ;)
But I would add good overall performance and in particular straightforward flexible concurrency support to the list of good things.
And IMO once you're in the set of "things with good perf" there's generally a lot of "boilerplate" of one sort or another anyway.
> end up copy pasting code instead
bit of my original comment
I'm more comparing it against languages like Kotlin and Swift, or even Scala.
I still remember people gaslighting everyone that any feature Go had was ESSENTIAL, and every feature Go didn't have was USELESS or too complicated for mere mortals "delivering value".
And the fast compiles at least are in big parts because the language is so horrendously basic. Can't get hung up on checking type constraints if you barely have any.
But the amount of queries that aren't fancy, and that an ORM is perfectly capable of abstracting away is (imho) 90% of all queries run.
Why make 90% or queries more tedious and error prone, just to make 10% slightly easier?
Heck, Go went out of it's way to "subvert expectations" more than the last season of Game of Thrones.
99% of decent C-ish languages either do "String thing" or "thing: String", but Go is so fancy and quirky, it does "thing String" for no freaking reason. Don't get me started on the nightmare that is map types.
I find that exception-based code is much harder to read. The happy path is clearer, but exceptional code paths are often completely obscured. It's harder to reason about what state the program is in when the exception is handled.
But as I've gotten older, I've started striving more and more for simplicity above all else, especially in systems design (disclaimer: I'm an SRE). Go is pretty good at being simple.
There are some things that still annoy me a whole bunch, though. Like - just one example - `fmt.Errorf` not being a first-class syntactic construct (or the difference between `%v` and `%w` in `fmt.Errorf`).
If you know your concrete types, they're just not that useful.
Even in home-grown libraries, I find generics to be a convenience rather than a necessity. It's useful to not have my library code so tightly coupled to my non-library code. But it does also come with a cost: every so often I have to check what the library actually does because being loosely coupled meant that iterations in the rest of the system didn't automatically have to involve the library, so the library code can get left behind.
Without generics, your library has to define interfaces that your users have to implement and it all gets a bit strange and unintuitive.
With generics you can write library code that is easier to use.
The thing I was worried about with this (adding generics) is that we'd start moving more towards the NPM Hell of everyone just writing plumbing code for imported packages. But thankfully that hasn't happened and idiomatic Go still tends to just use the standard lib and very few external packages.
Entity Framework was the thing that made me spit the dummy with C#, uninstall Windows, install Linux and discover Go in the first place.
Knowing how to write good SQL is a superpower as a developer, and every time I've worked with an ORM fan I get this reinforced. "The database is too slow!" No, your SQL just sucks.
Generics support is a ubiquitous feature in static programming languages. If it was included on day one in Go, nobody would have blinked an eye. This is only such a controversial topic in Go because the language maintainers made it one.
I'd add what I think is perhaps its most significant benefit to the list: Go fully solved the function coloring I/O problem in a way few other languages (Erlang/Elixir and ... Bend? Others?) have.
That's adjacent to the concurrency benefits in the parent comment, but a little different: allowing procedural, non-colored code to be efficiently concurrent over I/O without introducing function coloring or requiring people to code specifically to an event loop/IO multiplexer in some other way requires good concurrency support in the language, to be sure. However, getting rid of function coloring while providing efficient concurrent IO also requires: a solid stdlib of IO capabilities; a very very good runtime that can coalesce goroutine-concurrent IO into multiplexing OS primitives in the same way a function-colored event loop would (while pre-empting/scheduling in a reasonable way that mitigates unexpected blocking); a strong "critical mass" of libraries to talk to common IO-ful systems; a strong community convention of "we will generally prefer reimplementing IO drivers in Go rather than binding/Cgo-ing in foreign code".
It's when you combine all of those that Go shines as a platform for concurrent (usually network) IO.
I'd say this is still the norm in discourse around Go, it's just that the goalposts have moved somewhat since it has more features now.
1. Software development is a fashion industry.
2. OOP is currently uncool.
3. People who identify as Go programmers don’t want it to be thought of as connected to OOP at all, because then it is uncool by association.
https://bob.stephenafamo.com/docs/query-builder/psql/example...
https://github.com/stephenafamo/bob/blob/main/gen/bobgen-psq...