[HN Gopher] Constraints in Go
       ___________________________________________________________________
        
       Constraints in Go
        
       Author : gus_leonel
       Score  : 148 points
       Date   : 2024-11-17 08:44 UTC (14 hours ago)
        
 (HTM) web link (bitfieldconsulting.com)
 (TXT) w3m dump (bitfieldconsulting.com)
        
       | indulona wrote:
       | 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.
        
         | throwaway63467 wrote:
         | 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.
        
           | vbezhenar wrote:
           | ORM is anti-pattern and reducing boilerplate is bad.
        
             | makapuf wrote:
             | 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 ?
        
             | bluesnews wrote:
             | 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
        
               | vbezhenar wrote:
               | 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.
        
               | indulona wrote:
               | > 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.
        
               | nordsieck wrote:
               | >> 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.
        
               | pjmlp wrote:
               | 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.
        
               | randomdata wrote:
               | Go has objects, but objects alone does not imply
               | orientation. For that, you need message passing.
        
               | kgeist wrote:
               | 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.
        
             | TheDong wrote:
             | > 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.
        
             | bobnamob wrote:
             | 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"?
        
               | rad_gruchalski wrote:
               | 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.
        
               | bobnamob wrote:
               | 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
        
               | rad_gruchalski wrote:
               | 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.
        
               | metaltyphoon wrote:
               | 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.
        
               | rad_gruchalski wrote:
               | 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?
        
               | LinXitoW wrote:
               | 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?
        
               | vbezhenar wrote:
               | 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.
        
           | indulona wrote:
           | understandable. thee are always valid uses cases. although
           | ORM in Go is not something that is widely used.
        
         | kgeist wrote:
         | Just checked, in my current project, the only place where I use
         | generics is in a custom cache implementation. From my
         | experience in C#, generics are mostly useful for implementing
         | custom containers. It's nice to have a clean interface which
         | doesn't force users to cast types from any.
        
           | BlackFly wrote:
           | Containers are sort of the leading order use of generics: I
           | put something in and want to statically get that type back
           | (so no cast, still safe).
           | 
           | 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.
        
           | aljarry wrote:
           | > From my experience in C#, generics are mostly useful for
           | implementing custom containers.
           | 
           | 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.
        
           | neonsunset wrote:
           | C# generics are way more powerful than that when it comes to
           | writing high-performance or just very, err, generic code.
           | Generic constraints and static interface members are
           | immensely useful - you can have a constraint that lets you
           | write 'T.Parse(text[2..8])'.
           | 
           | 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.
        
         | tonyedgecombe wrote:
         | I sometimes wonder if they should have implemented generics. On
         | the one hand you had a group of people using go as it was and
         | presumably mostly happy with the lack of generics. On the other
         | side you have people (like me) complaining about the lack of
         | generics but who were unlikely to use the language once they
         | were added.
         | 
         | It's very subjective but my gut feeling is they probably didn't
         | expand their community much by adding generics to the language.
        
           | cherryteastain wrote:
           | I think a lot of the people who wanted generics wanted them
           | more to be like C++ templates, with compile time duck typing.
           | Go maintainers were unwilling to go that route because of
           | complexity. However, as a result, any time I think "oh this
           | looks like it could be made generic" I fall into a rabbit
           | hole regarding what Go generics do and dont allow you to do
           | and usually end up copy pasting code instead.
        
             | wyufro wrote:
             | I think "oh this looks like it could be made generic" is
             | the wrong time to convert to generics.
             | 
             | 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.
        
               | cherryteastain wrote:
               | I mostly agree, hence the
               | 
               | > end up copy pasting code instead
               | 
               | bit of my original comment
        
           | vbezhenar wrote:
           | Generic containers are needed in some cases. Using generic
           | containers with interface{} is very slow and memory-
           | intensive. Not a problem for small containers, but for big
           | containers it's just not feasible, so you would need to
           | either copy&paste huge chunks of code or generate code.
           | Compared to those approaches, generic support is superior in
           | every way, so it's needed. But creating STL on top of them is
           | not the indended use-case.
        
           | sbrother wrote:
           | Having recently had to work on a Go project for the first
           | time, I think I agree with you here. I'd tried Go a little
           | bit when it came out, had zero interest in what it offered,
           | and then when I was asked to work on this project a couple
           | months ago I thought it would be fun to try it out again
           | since I had read the language had improved.
           | 
           | 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?
        
             | quinnirill wrote:
             | Mad LoCs, dude, gotta make alotta lines, that's what
             | productivity is!
        
             | indulona wrote:
             | > I see even experienced Go programmers constantly wasting
             | time writing unreadable boilerplate
             | 
             | if it is unreadable, in Go, probably the most readable
             | language used today, i would question the aforementioned
             | experience.
        
               | DangitBobby wrote:
               | What you're doing right now is called "coping".
        
               | LinXitoW wrote:
               | I don't think a language where for every 1 line of
               | functionality, you need 3 lines of error handling
               | boilerplate gets to be called readable.
               | 
               | 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.
        
             | majormajor wrote:
             | > 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?
             | 
             | 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.
        
               | sbrother wrote:
               | Yeah that's fair. In terms of "things with good perf" I'd
               | rather be writing C++ or Rust, but there are significant
               | issues with using either of those on a large team.
               | 
               | I'm more comparing it against languages like Kotlin and
               | Swift, or even Scala.
        
               | LinXitoW wrote:
               | It might be nit picking, but that's more the ecosystem or
               | tooling that's great. The language is mediocre, but it's
               | what everyone gushes about.
               | 
               | 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.
        
         | kaba0 wrote:
         | Well, generics are mostly meant for library code. Just because
         | you don't need it, doesn't mean that code you use doesn't need
         | it.
        
         | gregwebs wrote:
         | There's an existing ecosystem that already works with the
         | constraints of not having generics. If you can write all your
         | code with that, then you won't need generic much. That
         | ecosystem was created with the sweat of library authors,
         | dealing with not having generics and also with users learning
         | to deal with the limitations and avoid panics.
         | 
         | 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.
        
         | jppittma wrote:
         | The most frequent use case I and my coworkers run into where we
         | use them is when we want type covariance on a slice.
         | 
         | 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.
        
         | eweise wrote:
         | here you go.
         | 
         | func Ptr[T any](v T) *T { return &v }
        
         | Groxx wrote:
         | That's kinda the point. Generics are mostly a library concern,
         | improving end-user experience and performance. End-user
         | _creation_ of generic types is relatively rare, and you can use
         | them in very simple ways and that 's almost always good enough
         | because you don't need them to be _maximally correct_ , only
         | good enough.
         | 
         | 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.
        
           | slimsag wrote:
           | Unfortunately not everyone shares that opinion of their
           | restricted use-cases.
           | 
           | 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.
        
             | Groxx wrote:
             | tbh I'll still take it over a similar Gordian knot with
             | interfaces. At least you can tell the restrictions are met
             | at compile time, rather than silently failing at runtime
             | because you (and/or someone else in the past) didn't notice
             | one edge case lodged somewhere surprising.
        
         | peterldowns wrote:
         | My most common use of generics is when testing -- check out my
         | library for typesafe test comparisons. I find it really useful
         | because I like having readable helpers for asserting in tests,
         | but I also want compiler errors if I refactor things.
         | 
         | https://github.com/peterldowns/testy
        
         | whateveracct wrote:
         | this is wild because i use parametric polymorphism by writing
         | `forall` in basically every Haskell PR i do for work ever
         | 
         | i think Go having a pretty bad implementation of parametric
         | polymorphism (a programming concept from the 70s) is probably
         | the root cause here
        
       | pansa2 wrote:
       | I'm surprised by the complexity of Go's generic constraints,
       | given the language's focus on simplicity. Things like the
       | difference between "implementing" and "satisfying" a constraint
       | [0], and exceptions around what a constraint can contain [1]:
       | 
       | > _A union (with more than one term) cannot contain the
       | predeclared identifier comparable or interfaces that specify
       | methods, or embed comparable or interfaces that specify methods._
       | 
       | Is this level of complexity unavoidable when implementing
       | generics (in any language)? If not, could it have been avoided if
       | Go's design had included generics from the start?
       | 
       | [0] https://stackoverflow.com/questions/77445861/whats-the-
       | diffe...
       | 
       | [1]
       | https://blog.merovius.de/posts/2024-01-05_constraining_compl...
        
         | rendaw wrote:
         | There are tons of random limitations not present in other
         | languages too, like no generic methods.
        
           | bigdubs wrote:
           | That's not a random limitation, there are very specific
           | reasons[1] you cannot easily add generic methods as struct
           | receiver functions.
           | 
           | [1] https://go.googlesource.com/proposal/+/refs/heads/master/
           | des...
        
             | abound wrote:
             | For someone not well-versed in language implementation
             | details, it may very well feel random.
             | 
             | I've been using Go as my primary language for a decade, and
             | the lack of generics on methods was surprising to me the
             | first time I ran into it, and the reasoning not obvious.
        
         | burakemir wrote:
         | Generics are a powerful mechanism, and there is a spectrum. The
         | act of retrofitting generics on go without generics certainly
         | meant that some points in the design space were not available.
         | On the other hand, when making a language change as adding
         | generics, one wants to be careful that it pulls its own weight:
         | it would be be sad if generics had been added and then many
         | useful patterns could not be typed. The design choices revolve
         | around expressivity (what patterns can be typed) and inference
         | (what annotations are required). Combining generics with
         | subtyping and inference is difficult as undecidability looms.
         | In a language with subtyping it cannot be avoided (or the
         | resulting language would be very bland). So I think the answer
         | is no, this part of the complexity could not have been avoided.
         | I think they did a great job at retrofitting and leaving the
         | basic style of the language intact - even if I'd personally
         | prefer a language design with a different style but more
         | expressive typing.
        
         | jerf wrote:
         | In practice, none of this impacts your program. The standard
         | advice I give to people messing around with this stuff is,
         | never use the pipe operator. The standard library already
         | implements all the sensible uses of it.
         | 
         | In particular, people tend to read it as the "sum type"
         | operator, which it is not. I kind of wish the syntax has used &
         | instead of |, what it is doing is closer to an "and" then an
         | "or".
         | 
         | By the time you know enough to know you can ignore that advice,
         | you will. But you'll also likely find it never comes up,
         | because, again, the standard library has already implemented
         | all the sensible variants of this, not because the standard
         | library is magic but because there's really only a limited
         | number of useful cases anyhow. I haven't gone too crazy with
         | generics, but I have used them nontrivially, even done s could
         | tricks [1], and the pipe operator is not that generally useful.
         | 
         | When the generic constraint is an interface with methods is the
         | case that can actually come up, but that makes sense, if
         | generics make sense to you at all.
         | 
         | It probably is a good demonstration of the sort of things that
         | come up on generic implementations, though. Despite the
         | rhetoric people often deployed prior to Go having them, no,
         | they are never easy, never without corner cases, never without
         | a lot of complications and tradeoffs under the hood. Even
         | languages designed with them from the beginning have them, just
         | better stuffed under the rug and with less obvious conflict
         | with other features. They're obviously not impossible, and can
         | be worthwhile when deployed, certainly, but it's always because
         | of a lot of work done by the language designers and
         | implementations, it's never just "hey let's use generics, ok,
         | that one sentence finishes the design I guess let's go
         | implement them in a could of hours".
         | 
         | [1]: Just about the edge of the "tricky" I'd advise:
         | https://github.com/thejerf/mtmap
        
           | tapirl wrote:
           | > In particular, people tend to read it as the "sum type"
           | operator, which it is not. I kind of wish the syntax has used
           | & instead of |, what it is doing is closer to an "and" then
           | an "or".
           | 
           | I don't understand here. In my understanding, the pipe
           | operator is indeed closer to "or" and "sum type" operator.
           | Interpreting it as "and" is weird to me.
        
             | Groxx wrote:
             | I think they're reading it as "a bitwise-and of the
             | functionality of the types passed", which is accurate
             | (since you're getting the lowest common denominator of all
             | |'d types).
             | 
             | I'm... not sure which way I lean tbh, now that I've seen
             | that idea. Both have merit, it's more of a problem for
             | educational material than anything. If you present it as
             | "these types", | makes sense. If you instead use "these
             | behaviors", & makes sense. | is slightly easier to type for
             | me though, and & has more meanings already (address-of), so
             | maybe I'd still favor |.
        
               | tapirl wrote:
               | Okay, it is some reasonable if the operator is viewed as
               | a behavior operator. But it is not, it is a type set
               | operator.
        
               | jerf wrote:
               | And the real point I'm making here is that "the type set
               | operator" is _not_ "a sum type". A sum type with, say,
               | three branches is either the first, or the second, or the
               | third, and to do anything with any of them, you have to
               | deconstruct it, at which point you have full access to
               | the deconstructed branch you are in. The | operator in a
               | Go generic is more a declaration of "I want to operate on
               | all of these at once", so, you can put multiple numeric
               | types into it because you can do a + or a - on any of
               | them, but while the syntax permits you to put three
               | struct types into it, and it'll compile, it does not
               | produce a "sum type". Instead you get "I can operate on
               | this value with the intersection of all the operations
               | they can do", which is more or less "nothing". ("Methods"
               | aren't "operations"; methods you can already declare in
               | interfaces.) Some people particularly fool themselves
               | because you can still take that type, cast it into an
               | "any", and then type switch on it, but it turns out you
               | can _always_ do that, the | operator isn 't helping you
               | in any particular way, and if you want to have a closed
               | set of types, a closed interface is a much better way to
               | do it, on many levels.
               | 
               | It also doesn't currently do anything else people may
               | want it to do, like, accept three structs that each have
               | a field "A" of type "int" and allow the generic to
               | operate on at least that field because they all share it.
               | There's a proposal I've seen to enable that, as the
               | current syntax would at least support that, but I don't
               | know what its status is.
        
               | tapirl wrote:
               | There is actually a proposal to make type constraints act
               | as sum types: https://github.com/golang/go/issues/57644
               | 
               | But I doubt sum types will be supported perfectly in Go.
               | The current poor-men's sum type mechanism (type-switch
               | syntax) might be still useful in future Go custom generic
               | age.
        
         | tapirl wrote:
         | The difference between types.Implements and types.Satisfies is
         | mainly caused by a history reason. It is just a tradeoff
         | between keeping backward compatibility and theory perfection.
         | 
         | It is pity that Go didn't support the "comparable" interface
         | from the beginning. If it has been supported since Go 1.0, then
         | this tradeoff can be avoided.
         | 
         | There are more limitations in current Go custom generics, much
         | of them could be removed when this proposal
         | (https://github.com/golang/go/issues/70128) is done.
         | 
         | I recommend people to read Go Generics 101
         | (https://go101.org/generics/101.html, author here) for a
         | thoroughly understanding the status quo of Go custom generics.
        
       | guilhas wrote:
       | I like in Go how the code looks like a execution graph, by
       | avoiding smarts and just copying code, when you have an error in
       | the log you can generally just follow it through the code as
       | there is only one path to get there. In C# I would have mostly to
       | debug to understand where did it came from
       | 
       | Not just because of the language, but of the simplify culture.
       | Let's see how generics will change that
        
       ___________________________________________________________________
       (page generated 2024-11-17 23:00 UTC)