Most active commenters
  • rad_gruchalski(4)
  • vbezhenar(3)
  • indulona(3)

←back to thread

Constraints in Go

(bitfieldconsulting.com)
210 points gus_leonel | 28 comments | | HN request time: 0.326s | source | bottom
Show context
indulona ◴[] No.42163167[source]
i have been writing Go exclusively for 5+ years and to this day i use generics only in a dedicated library that works with arrays(slices in Go world) and provides basic functionality like pop, push, shift, reverse, filter and so on.

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.

replies(10): >>42163189 #>>42163244 #>>42163415 #>>42163694 #>>42163834 #>>42164296 #>>42164983 #>>42165141 #>>42165270 #>>42165680 #
1. throwaway63467 ◴[] No.42163189[source]
Honestly so many things profit from generics, e.g. ORM code was very awkward before especially when returning slices of objects as everything was []any. Now you can say var users []User = orm.Get[User](…) as opposed to e.g var users []any = orm.Get(&User{}, …), that alone is incredibly useful and reduces boilerplate by a ton.
replies(3): >>42163242 #>>42163295 #>>42168753 #
2. vbezhenar ◴[] No.42163242[source]
ORM is anti-pattern and reducing boilerplate is bad.
replies(5): >>42163314 #>>42163315 #>>42163626 #>>42163744 #>>42168321 #
3. indulona ◴[] No.42163295[source]
understandable. thee are always valid uses cases. although ORM in Go is not something that is widely used.
4. makapuf ◴[] No.42163314[source]
I agree. The best language to handle data in a RDBMs is SQL, and in that case the best language to handle application logic is Go (or Kotlin, Python or whatever). So there must be some meeting point. Handling everything in Go is not optimal, and all in sql not always practical. So how to avoid redundant data description ? I often have structs in a model Go file that reflect queries I do, but that's not optimal since I tend to have to repeat what's in a query to the language and the query to struct gathering is often boilerplate. I also almost can reuse the info I need for a query for another query but leave some fields blank since they're not needed.. the approaches are not optimal. Maybe a codegen sql to result structs / gathering info ?
5. bluesnews ◴[] No.42163315[source]
Could you expand on this?

I don't like ORM because in my experience you inevitably want full SQL features at some point but not sure if you have the same issues in mind or not

replies(1): >>42163404 #
6. vbezhenar ◴[] No.42163404{3}[source]
ORM is for object-relation mapping. Go is not object-oriented language and OOP-patterns are not idiomatic Go, so using ORM for Go cannot be idiomatic. That's generic answer. As for more concrete points:

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.

replies(2): >>42163641 #>>42163647 #
7. TheDong ◴[] No.42163626[source]
> reducing boilerplate is bad

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.

8. indulona ◴[] No.42163641{4}[source]
> Go is not object-oriented language

That is most definitely not true. Go just uses composition instead of inheritance. Still OOP, just the data flow is reversed from bottom to the top.

replies(4): >>42163740 #>>42164085 #>>42164581 #>>42194567 #
9. kgeist ◴[] No.42163647{4}[source]
I find libraries like sqlx more than enough. Instead of a full-blown ORM, they simply help hydrate Go structs from returned SQL data, reducing boilerplate. I prefer the repository pattern, where a repository is responsible for retrieving data from storage (using sqlx) using simple, clean code. Often, projects which use full-blown ORMs, tend to equate SQL table = business object (aka ActiveRecord) which leads to lots of problems. Business logic should be completely decoupled from underlying storage, which is an implementation detail. But more often than not, ORM idiosyncracies end up leaking inside business logic all over the place. As for complex joins and what not, CQRS can be an answer. For read queries, you can write complex raw SQL queries and simply hydrate the results into lightweight structs, without having to construct business objects at all (i.e. no need for object-relational mapping in the first place). Stuff like aggregated results, etc. Such structs can be ad hoc, for very specific use cases, and they are easy to maintain and are very fast (no N+1 problems, etc). With projects like sqlx, it's a matter of defining an additional struct and making a Select call.
10. ◴[] No.42163740{5}[source]
11. bobnamob ◴[] No.42163744[source]
Not liking ORM I can understand, db table <-> object impedance mismatch is real, but "reducing boilerplate is bad" is an interesting take.

Can you elaborate and give some examples of why reducing boilerplate is generally "bad"?

replies(2): >>42163929 #>>42164240 #
12. rad_gruchalski ◴[] No.42163929{3}[source]
Not the person you’re replying to. The orm sucks because as soon as you go out of the beaten path of your average select/insert/update/delete, you are inevitably going to end up writing raw sql strings. Two cases in point: postgres cte and jsonb queries, there are no facilities in gorm for those, you will be just shoving raw sql into gorm. You might as well stop pretending. There’s a difference between having something writing the sql and mapping results into structs. The latter one can be done with the stdlib sql package and doesn’t require an „orm”.

There are two things an sql lib must do to be very useful: prepared statements and mapping results. That’s enough.

replies(4): >>42163980 #>>42164006 #>>42167612 #>>42210541 #
13. bobnamob ◴[] No.42163980{4}[source]
You haven’t answered my question at all.

The parent comment made two claims: ORM not great (I agree) and “boilerplate reduction bad” which still needs some elaboration

replies(1): >>42164432 #
14. metaltyphoon ◴[] No.42164006{4}[source]
Perhaps you have to yet use a good ORM? I could probably count on my fingers the times I had to drop to raw SQL in EFCore. Even when you do that you can still have mapped results, which reduces boilerplate.
replies(1): >>42164459 #
15. nordsieck ◴[] No.42164085{5}[source]
>> Go is not object-oriented language

> 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.

replies(1): >>42164205 #
16. pjmlp ◴[] No.42164205{6}[source]
Unless you happen to want to warm up the CPU, there is very little Go code that is possible to write that does anything useful without OOP concepts, like interfaces, methods and dynamic dispatch.

Creating objects on the heap isn't the only defining feature how a language does OOP or not.

17. vbezhenar ◴[] No.42164240{3}[source]
What I mean is reducing boilerplate is not something one should strive to achieve. It is not bad in the sense that one should introduce more boilerplate for the sake of it. But reducing boilerplate for the sake of it is not good thing either.

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.

18. rad_gruchalski ◴[] No.42164432{5}[source]
I answered it, you just don’t see it. One ends up with the boilerplate anyway as soon as one attempts to step out of the usual crud path. There’s no gain, there’s no difference in templating an sql string vs fighting an orm api.
19. rad_gruchalski ◴[] No.42164459{5}[source]
I’m doing this job for 25 years and I haven’t seen a good orm. Sorry. Look, linq is nice. But linq is not enough of a productivity gain to switch the whole stack from go to .net. I used linq 15 years ago extensively and it feel like magic. But then again, how would you model jsonb select for a variable set of of properties and include nested or and and conditions using its notation? Maybe you could but how much longer is it going to take you rather than templating a string?
replies(2): >>42169720 #>>42170875 #
20. randomdata ◴[] No.42164581{5}[source]
Go has objects, but objects alone does not imply orientation. For that, you need message passing.
21. LinXitoW ◴[] No.42167612{4}[source]
Any ORM worth it's salt has an escape hatch that allows you to do all those fancy raw SQL queries.

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?

replies(1): >>42170888 #
22. ◴[] No.42168321[source]
23. the_gipsy ◴[] No.42168753[source]
gorm just takes a pointer of your type and does reflection magic. It's worse than generica, I agree, but you don't get []any.
24. marcus_holmes ◴[] No.42169720{6}[source]
I've been doing this job for over 30 years and I agree.

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.

25. rob74 ◴[] No.42170875{6}[source]
Maybe people who have only ever used ORMs are happy with them, because they don't know how much more flexible and faster "raw" SQL queries can be?
26. rad_gruchalski ◴[] No.42170888{5}[source]
I don’t even comprehend your argument. Look: https://gorm.io/docs/, it’s full of strings everywhere. That’s as error prone as anything else and it puts an additional layer of abstraction between you and your sql.
27. frou_dh ◴[] No.42194567{5}[source]
It goes like this:

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.

28. jd3 ◴[] No.42210541{4}[source]
https://github.com/stephenafamo/bob supports both CTEs and JSONB in the psql (postgres) dialect

https://bob.stephenafamo.com/docs/query-builder/psql/example...

https://github.com/stephenafamo/bob/blob/main/gen/bobgen-psq...