[HN Gopher] Generics facilitators in Go
___________________________________________________________________
Generics facilitators in Go
Author : throwaway894345
Score : 148 points
Date : 2021-12-22 14:20 UTC (8 hours ago)
(HTM) web link (rakyll.org)
(TXT) w3m dump (rakyll.org)
| mattarm wrote:
| "All problems in computer science can be solved by another level
| of indirection" - Butler Lampson
|
| See https://en.wikipedia.org/wiki/Indirection
| [deleted]
| [deleted]
| infogulch wrote:
| "Except for the problem of too many levels of indirection" - ?
| adgjlsfhk1 wrote:
| No, you solve that problem by adding another layer of
| indirection (aka a caching layer)
| legulere wrote:
| > Go type system is not a traditional type system
|
| How so, I don't see anything special about Go's type system in
| the linked article.
| noah_buddy wrote:
| I believe they're talking about the semi-duck typing used in go
| for interfaces but I'm not certain. Interfaces are implicitly
| implemented with a runtime check determining whether a type x
| assigned to interface y is valid.This is why you'll often see
| an empty declaration in packages for structs intended to
| implement an interface like so:
|
| var _ y = x{} // syntax might be slightly wrong
|
| After writing this, I found the linked article in the OP link
| to what the author meant.
|
| https://rakyll.org/typesystem/
|
| There's really a bunch of small things that are different about
| golang that individually you can find elsewhere but are on the
| whole unique.
| jayd16 wrote:
| Reminds me of Java's use of listener objects because it lacked
| (at the time) lambdas and method references for callbacks.
|
| Hopefully Go generics continue to improve.
| mcculley wrote:
| I don't think I understand what you mean. What would have been
| different about listener interfaces if lambdas and method
| references had been available?
| kubb wrote:
| I wonder if the limitation of not allowing type parameters for
| methods came from wanting to prevent people from using it for
| certain features that would be considered "too complicated for
| Go", or from it being "too complicated to support" or from "we
| won't be needing it anyways".
|
| Another option to circumvent this is to use top-level
| functions, and accept the receiver as the first argument of the
| function. This is effectively the same thing, the only thing
| it's missing is the object.method() syntax that's familiar to
| OO programmers.
| randomdata wrote:
| No need to wonder. The proposal goes into lengthy detail as
| to why they are not included.
|
| There is hope that they will be added in the future, but
| solutions to the problems will need to be found first.
| kubb wrote:
| I wouldn't call it lengthy, but if anyone is wondering, the
| proposal points out that parametrized methods don't have an
| obvious behavior _inside interfaces_. No mention of why
| they left them out in non-interface types.
| Ericson2314 wrote:
| Yeah they are just being ignorant once again. Even
| "static" interface usage, a la Rust Traits, is fine. But
| Go doesn't distinguish between the static stuff and the
| dynamic vtable stuff very well at all.
|
| That's the problem they should fix.
| randomdata wrote:
| _> Yeah they are just being ignorant once again._
|
| That's the core problem. Anyone who truly understands
| generics has shown no willingness to work on Go. The only
| reason it has progressed as far as it has is because a
| third-party consultant was temporarily hired to develop
| what was dubbed Featherweight Go to act as a proof of
| concept.
|
| It turns out that the intersection of people who care
| about generics in Go and those those who aren't armchair
| coders unable to comprehend the domain themselves is
| limited to very few people. As you know well from your
| armchair.
| saghm wrote:
| It seems plausible that some people who might otherwise
| want to have decided not to given the very vocal
| contingent of the Go community that's been opposed to
| generics. After almost a decade of people saying Go
| didn't need generics, it seems like it would be hard to
| convince people who are knowledgeable in that field that
| their efforts would be welcome.
| randomdata wrote:
| The core Go team has been working on generics since the
| pre-1.0 days and have submitted many different generics
| proposals over the years to reflect that. They all, until
| the latest one, failed for one reason or another, but
| shared property of them all was being created by those
| who were not experts in the domain. It is why the latest
| incarnation brought in an external consultant to help
| guide the team in the right direction.
|
| There was clearly always interest from the project
| itself. There may have been some random naysayers on the
| internet who act like they are opposed, but you'll find
| people acting every way imaginable on the internet. I
| really doubt anyone actually refrained from working on
| the project because an internet troll wrote something
| silly.
| philosopher1234 wrote:
| You sound like someone who doesn't know very much about
| Go.
| mseepgood wrote:
| In Go any type can implement interfaces. A type doesn't
| declare which interfaces it implements.
| kubb wrote:
| > Go type system is not a traditional type system and it was not
| possible just to bring an existing generics implementation from
| other language and be done.
|
| I claim that there are no original ideas in Go's type system and
| in the way that Go does generics, and the reason it took so long
| was because it came out of an environment with actors (including
| a part of the community) pushing against the feature.
|
| You might disagree, I'm curious what original features of Go you
| will point out to me.
| throwaway894345 wrote:
| This seems like a straw man. I don't think anyone has claimed
| Go's type system has novel features.
|
| > the reason it took so long was because it came out of an
| environment with actors (including a part of the community)
| pushing against the feature.
|
| The reason it took so long is because it simply wasn't the
| biggest fish to fry. Generics are a massive feature, and there
| were a lot of other opportunities to deliver more value for
| considerably less work. Of course, there was a healthy amount
| of pushback against _rushing_ generics, considering generics
| must interact with every other feature in the type system even
| if those features aren 't novel.
|
| Frankly, people make too big a deal about generics. It will be
| a neat quality of life improvement for certain kinds of code,
| and a lot of additional code will enter the ecosystem which is
| gratuitously abstract. Builds will probably get a fair bit
| slower on average while still being fast relative to most other
| languages. Frankly, no type system feature will ever impact a
| language as significantly as things like tooling quality,
| ecosystem, runtime performance, static+native compilation,
| iteration loop time, learning curve, etc.
| masklinn wrote:
| > I don't think anyone has claimed Go's type system has novel
| features.
|
| The third phrase of the linked essay is:
|
| > Go type system is not a traditional type system
|
| "non-traditional" is generally read as "novel" in some way.
| Vendan wrote:
| That's a bit of a stretch... If someone made a language
| that directly copied Haskell's type system, I'd 100% call
| it a non-traditional type system, but there would be
| nothing novel about it.
| throwaway894345 wrote:
| You're mistaken here. "non-traditional" doesn't mean
| "novel", it just means "something that isn't traditional".
| For example, eating cereal for dinner is non-traditional,
| but that doesn't mean no one has done it before.
| kubb wrote:
| I'm quoting the author directly. I didn't tamper with the
| quote.
| throwaway894345 wrote:
| I agree that you didn't tamper with the quote, but you
| clearly weren't rebutting it either. Rather, you were
| rebutting the idea that Go's type system contains novel
| features, which the author didn't allege.
| eweise wrote:
| I'm currently working on a process that has to manipulate
| collection of data and its really aggravating to invent
| custom functions to do basic manipulations such as groupby,
| maps, filter, etc. It basically feels like java back in the
| early 2000s.
| jerf wrote:
| "Frankly, people make too big a deal about generics."
|
| I think there's a lot of confusion brought in from languages
| where generics are a bigger deal than they are in Go, because
| when generics came in to those languages it was like turning
| on a light.
|
| However, if you map out the _problem space_ of "what did
| generics solve in those languages", rather than obsessing
| about the _solution_ , what you find is that Go's interfaces,
| combined with the structural typing ("static duck typing"
| basically, rather than having to explicitly declare
| conformance at declaration time) covered quite a bit of the
| problem space. And that's what matters, whether the problem
| space is covered, not whether the problem space was covered
| in exactly the same way as some other language.
|
| People who either have never programmed in Go, or only spent
| long enough with it to try to program "(Not Go) in Go" (e.g.,
| "Javascript in Go"), and didn't learn how to use the tools Go
| has to cover the problem space, end up vastly overestimating
| their utility in Go, and vastly overestimating the sort of
| problems you can't use Go to solve. They're like "How can you
| program in a language that doesn't cover the 'generics'
| problem space?", and the answer is, "You're actually right, I
| couldn't! But I can use Go, because it actually does cover
| quite a bit of the problem space, just in a different way
| than what you're demanding." Interfaces DO NOT cover all the
| space, I would never dream of claiming otherwise, but they
| cover a _lot_ of the space. There 's even some problems where
| I've found Go simply outright the best solution because of
| some particular thing I can use interfaces to solve.
|
| I'll actually kind of miss the "How can I do this without
| generics in Go?" questions on /r/golang. They're fun, and
| with the exception of "data structures", generally I didn't
| just come up with "a" solution, but with a _good_ solution.
| phillipcarter wrote:
| > Frankly, people make too big a deal about generics.
|
| Different strokes for different folks, but I think generics
| are a _huge_ deal. They make libraries so much more ergonomic
| and reduce toil generally. It 's a case of dealing with many,
| many small papercuts over a long period of time rather than a
| big-bang sort of thing.
|
| > tooling quality
|
| I'm a huge proponent of tools, so much so that I worked on
| programming language tools for 5 years. My perspective is
| that tools are there to optimize an existing experience, but
| reach a local maxima in the overall experience because
| they're limited by language semantics. When you improve
| language semantics, tools get to improve experience again.
| That's why it's so important to do language design in a way
| that _strongly considers_ the tooling experience, and a big
| reason why you see so much love for languages like TypeScript
| and C#. They work hand in hand. And generics are an example
| of how semantics can push tools to be better.
| throwaway894345 wrote:
| When I think about tooling, I'm thinking about things like
| build systems and dependency managers. In Go, I don't need
| to script my own build system like I would do in C, C++,
| Java, etc. Similarly, reproducible package management works
| out of the box--I don't need to try in vain to get the
| right versions of the right dependencies installed to the
| right locations like I do in C and C++ (and Python, to a
| lesser extent). Moreover, Go produces native static
| binaries out of the box by default--I don't have to cobble
| together a bunch of configuration in every one of my
| projects only to have it broken by the odd transitive
| dependency which can't be statically compiled (well, this
| is probably the case in some Go packages, but I contend
| that it's far rarer than in the Java, Python, Rust, etc
| worlds--certainly I've never run into this in my decade
| with Go). Further still, in Go, I don't have to write CI
| pipelines to build and publish code packages nor
| documentation packages.
|
| And then there is the toil reduced by having good
| performance. I have about 15 years of experience with
| Python, and almost invariably there's some hot path in the
| code which needs something between Python's level of
| performance and Go's level of performance. Unlike Go,
| Python affords relatively little headroom for optimization
| --you can't easily parallelize anything or pull some
| allocation out of a tight loop for a 10x performance gain.
| Usually this looks like rearchitecting your codebase so
| that "rewrite the hotpath in C/multiprocessing/etc" is
| attainable and on top of that you're probably implying
| significant changes to your build system and/or runtime
| environment as well as a load of maintainability problems
| associated with anything that touches the language/process
| boundary.
|
| By comparison, I think the gains from generics are
| relatively small.
| phillipcarter wrote:
| > I'm thinking about things like build systems and
| dependency managers.
|
| Ah, that's a different kind of tooling. This matters, but
| there are loads of developers out there who care just as
| much (if not more) about IDE tooling and other things
| that assist in the writing and refactoring of source code
| itself. Features like generics open up new ways to make
| this tooling better.
|
| It's also worth pointing out that you can have your cake
| and eat it too. .NET and Rust are fine examples of having
| great build/package management tooling, runtime
| performance, compiler performance (.NET far more than
| Rust), artifact management, and so on. And great IDE
| tooling in different environments.
| throwaway894345 wrote:
| Yes, I agree that IDE tooling is nice and that it doesn't
| conflict with having good build tooling; my only point is
| that, given the choice between good tooling and generics
| (or any type system feature), I would choose the tooling
| hands down every single time.
| phillipcarter wrote:
| Why make it an either/or thing? We should demand more of
| corporate-controlled software. The money exists to make
| both a reality.
| verdverm wrote:
| While not necessarily part of the language, but arguably part
| of Go, something novel is the dep mgmt system. I have not
| encountered the MVS algorithm in another language. CUE is
| intending to adopt it.
| jjtheblunt wrote:
| to your point, isn't Go just the Google-funded continuation of
| Bell Labs' Limbo, with essentially the same people?
|
| If so, it's curious how sparsely mentioned the origin at Bell
| Labs pre Google is.
|
| https://en.wikipedia.org/wiki/Limbo_(programming_language)
| skybrian wrote:
| Unusual doesn't mean unique or better, just different enough to
| make doing a straight port harder.
|
| For example, Go's interface types and its lack of constructors
| seem different from most languages you might borrow from.
| [deleted]
| 1_player wrote:
| What is the point of going into Go generics posts on HN and
| making a point to mention that core Go developers were against
| the feature, which is incorrect, but also shows there's some
| axe to grind against that team. Is that to show they're less
| smart then they think they are?
|
| I swear there's at least one top-level comment such as yours in
| every thread, for no reason other than just to discredit
| people's work. I find it disgraceful.
| randomdata wrote:
| Go's generics have been in development (by the same person)
| since the pre-1.0 days, with various proposals submitted for
| their inclusion. Are you suggesting that those earlier
| proposals failed for frivolous reasons rather than real
| technical concerns?
|
| It seems to me that that it was less about active pushback and
| more that people who cared about having generics in Go was
| limited to an _exceedingly small_ group of people who weren 't
| necessarily experts in the subject matter. In fact, the Go team
| hired an external expert to lay the groundwork for what became
| the winning proposal.
| anonymoushn wrote:
| A great innovation of Go is that it includes a built-in
| reference type with both inner nils and outer nils.
| Skunkleton wrote:
| As a non-serious user of Go, this was something I found to be
| a negative. What am I missing?
| masklinn wrote:
| That an innovation is not necessarily positive. For
| instance INTERCAL innovated COMEFROM.
|
| Also that GP was probably sarcastic when they nominated
| typed nils.
| Ericson2314 wrote:
| Well played :)
| kubb wrote:
| It is a bit like void *outer = 0; // outer
| can be nil int val = 5; int *inner = &val; //
| inner can also be nil outer = (void*)(&inner) // can
| have an inner value, inner nil or outer nil
| masklinn wrote:
| It's not really like that, this is two nested but different
| values which can be nil (not exactly independently but
| close).
|
| typed nils are a single value which is both nil and non-nil
| at the same time.
| throwaway894345 wrote:
| You're mistaken. An interface is simply a reference type
| --like other reference types, it may refer to a type
| which is itself a reference type. The outer reference may
| be not-nil while the inner reference may be nil, which
| confuses people who don't understand references into
| believing that there is a single value which is both nil
| and not-nil at once.
|
| It's no different than `var x **int` (a variable called
| "x" whose type is a pointer to a pointer to an int). If
| you want to safely access the underlying integer data,
| it's not sufficient to check that the outermost reference
| is not nil--you must also verify that the inner reference
| is not nil. We run into this in _every_ language with
| nullable reference types, including C, Java, Python,
| JavaScript, etc.
|
| EDIT: `*int` -> `**int`. TIL HN converts two asterisks
| into one.
| masklinn wrote:
| > An interface is simply a reference type--like other
| reference types, it may refer to a type which is itself a
| reference type. The outer reference may be not-nil while
| the inner reference may be nil, which confuses people who
| don't understand references into believing that there is
| a single value which is both nil and not-nil at once.
|
| None of that is correct. The value-part of the fat
| pointer is the exact same in both situations: nil. It
| _is_ a single value which is both nil and non-nil.
|
| > It's no different than `var x **int`
|
| I assume you meant that rather than what you actually
| wrote.
|
| It is in fact quite different.
|
| > If you want to safely access the underlying integer
| data, it's not sufficient to check that the outermost
| reference is not nil--you must also verify that the inner
| reference is not nil.
|
| Something a typed nil does not let you do, unless you
| know (or reflect) the underlying concrete type in order
| to downcast the interface back to a regular pointer.
|
| > We run into this in every language with nullable
| reference types, including C, Java, Python, JavaScript,
| etc.
|
| We really don't. In "every language" you have to actually
| create a double indirection for something within the same
| heavenly realm (but is not at all the same thing) to
| occur.
| throwaway894345 wrote:
| > I assume you meant that rather than what you actually
| wrote.
|
| Yes, today I learned HN replaces double-asterisks with
| single asterisks. Edited my original post.
|
| > None of that is correct. The value-part of the fat
| pointer is the exact same in both situations: nil. It is
| a single value which is both nil and non-nil.
|
| I repeat, an interface isn't "both nil and non-nil", but
| rather the nullity of the interface and the nullity of
| the underlying data are distinct propositions. For
| example, there is nothing analogous to "both nil and non-
| nil" when the runtime type is a value type, such as an
| integer.
|
| An interface behaves just like a reference. When the
| underlying data is also a reference, then it behaves like
| a reference to a reference. This means you can have a nil
| interface or a non-nil interface which references a nil
| pointer.
|
| Under the covers, Go implements this behavior by
| representing interfaces as a tuple where the first field
| contains a pointer to the runtime type information and
| the second field contains a pointer to the data. Go
| encodes a nil interface as (nil, nil) while it encodes an
| interface backed by a nil int pointer as `(<pointer to
| `int` type>, nil)`.
|
| > We really don't. In "every language" you have to
| actually create a double indirection for something within
| the same heavenly realm (but is not at all the same
| thing) to occur.
|
| Go interfaces to pointer data are functionally "double
| indirection". The outer pointer can be either nil or a
| pointer to the data which itself can be nil or valid.
|
| > Something a typed nil does not let you do, unless you
| know (or reflect) the underlying concrete type in order
| to downcast the interface back to a regular pointer.
|
| Well, this would be unsafe as the runtime type of the
| underlying data isn't guaranteed to be a pointer. So by
| default Go requires runtime type checks in this case, but
| you can always opt out of these runtime type checks via
| `unsafe` but this is usually inadvisable.
| jerf wrote:
| They are not the same nil: http://www.jerf.org/iri/post/2957
| azth wrote:
| A negative innovation, sure :P
| throwaway894345 wrote:
| Go has no such feature. Go's interfaces are a reference type
| which point to the runtime implementation. If that
| implementation is itself a pointer type, then that pointer
| may also be nil. Any language that has nullable reference
| types (including languages in which everything is a pointer
| like Java or Python) will have this problem.
|
| EDIT: would really like to know why I'm being downvoted. If
| you disagree with my comment, then best of luck to you in
| rebutting it. :)
| masklinn wrote:
| > Go has no such feature. [...] Any language that has
| nullable reference types (including languages in which
| everything is a pointer like Java or Python) will have this
| problem. var i *int = nil var x
| interface{} = i if x != nil {
| fmt.Printf("%v was not nil", x) }
| <nil> was not nil
|
| By all means, do demonstrate how Java or Python have this
| problem.
| Joker_vD wrote:
| It's much worse, because there is auto-conversion between
| concrete types and interfaces you can't reliably do e.g.
| "if err != nil { SendString(err.Error()) }" -- it can
| panic with SIGSEGV. I've ran into it a month ago in
| actual production code: condensed version is at [0]. Yes,
| it's a bad design on the 3rd-party library's part... or
| perhaps it _is_ a defect of the language: "pit of
| success" and all of that, you know?
|
| [0] https://go.dev/play/p/D_M1geUtJTP
| throwaway894345 wrote:
| I don't think this is a different (and thus not "worse")
| case--it seems like a specific instance of the same case
| we've been discussing. Ultimately it comes down to this:
| error.Error((*CustomErrorType)(nil))
|
| You're putting a nil `*CustomErrorType` into an `error`
| interface.
|
| This definitely causes some people confusion and results
| in bugs, but the question under discussion is whether the
| original claim ("an interface can be simultaneously nil
| and not nil") is true. Of course, it's not true as this
| example illustrates--the interface is not nil but the
| underlying data (of type `*CustomErrorType`) _is_ nil.
| TheDong wrote:
| I think there is a point to be made here.
|
| Let's say I have the following code:
| func open() (*os.File, *os.PathError) { return
| os.NewFile(1, "stdout"), nil } // in main
| // (some lines omitted) fi, err := open()
| if err == nil { fmt.Println("no error")
|
| So, my question is, does that print 'no error' or not?
|
| Normally, you would say "no, it does not, 'err' is a nil
| '*os.PathError' so it does not.
|
| However, I didn't mention what lines were omitted, and
| those lines actually matter! // main
| _, err := os.Executable() fi, err :=
| open() if err == nil { fmt.Println("no error")
|
| Having that line before it changes the output, and the
| error is no longer detected as nil (since it's now a non-
| nil error interface referencing a nil struct).
|
| Why does this matter? Well, it's extremely common in go
| (idiomatic in fact) to reuse the "err" variable name, and
| it's also extremely common to use it in multiple
| assignment with ":="... where despite using the
| declaration syntax, you can re-assign to existing
| variables without their types changing.
|
| I think that's what the parent was pointing out: a
| combination of type inference and automatically
| "converting" a struct into an interface for you (both in
| assignment, as I showed above, and when you return a
| value, as shown in the parents example) means that you
| can end up with this issue without doing anything as
| obviously wrong.
|
| In other languages, returning concrete error types means
| the caller has more type-system information and is good.
| In go, returning concrete error types gives the caller
| more information, but opens them up to accidentally
| implicitly casting it to an interface and blowing their
| foot off.
|
| As such, we have almost completely untyped error returns,
| and any time a gopher thinks "this is dumb, why don't I
| use the type system for this?" they're actually just
| loading a shotgun and pointing it at their foot.
|
| > I don't think this is a different (and thus not
| "worse") case
|
| All the above was just to say, I think it's the same
| case, but it's pointing out that it's "worse" because it
| interacts poorly with other parts of the go language
| spec.
|
| Playground: https://go.dev/play/p/4MYEZr8D5Qv
| anonymoushn wrote:
| I think the original claim is that it can be inner nil or
| outer nil and those are two different things and often
| you should check for both.
| throwaway894345 wrote:
| Sure: class Ptr: def
| __init__(self, x): self.x = x
| x = Ptr(None) if x != None: #
| Go's `%v` print directive unpacks interfaces.
| # We'll use `x.x` to emulate the same here
| print(f"{x.x} was not None")
| phillipcarter wrote:
| > You might disagree, I'm curious what original features of Go
| you will point out to me.
|
| I think only insiders on the Go team during that time period
| can really say for sure what the deal is.
|
| But I will say that retrofitting generics onto a language and
| standard library is a project of enormous scope and incredible
| difficulty. So I'm pretty sympathetic to the idea that it took
| them a while.
| krzat wrote:
| Thoughts and prayers to the Go team. I suspect they got kinda
| forced into implementing this, otherwise generics would be
| available from the start.
| phillipcarter wrote:
| I would say - congratulations to the Go team! It's a huge
| accomplishment and a testament to the quality of their
| software engineering process.
| assbuttbuttass wrote:
| A nice pattern that will probably become common.
|
| I assume the limitation on generic methods is just an
| implementation issue, the vtable for an interface has to have a
| known number of methods, and they can't insert specializations
| for each type on the fly into the vtable.
| jerf wrote:
| There is discussion of this in the proposal:
| https://go.googlesource.com/proposal/+/refs/heads/master/des...
|
| I'd call it a fundamental semantics issue. That discussion is
| mostly framed in terms of the compile-time problems that arise,
| but that brief aside about the reflection package is
| deceptively short, because it means that the entire compile
| time set of problems is also lifted into runtime, where the
| problem is even worse. (To translate for someone who isn't a Go
| expert, the reflection package has enough power that it can
| dynamically instantiate new types that the compiler did not and
| can not even in principle have seen at compile time, which may
| then require new methods to be created. The current Go runtime
| has no run-time code creation capabilities. [1]) Without the
| reflection problem, it's annoying but at least solvable in
| principle, but I don't see any reasonable design (without a
| fundamental overhaul to specify a runtime-available compiler in
| the language spec itself, which is a _huge_ step) that solves
| the reflection issue.
|
| [1] Which _personally_ I consider almost a feature. YMMV.
| Runtime compilation is very cool in a lot of ways but is a hell
| of a subsystem to try to secure, just by its very nature. There
| 's a lot to be said for simply not having that capability
| unless it is integral to some particular program.
| _old_dude_ wrote:
| > it means that the entire compile time set of problems is
| also lifted into runtime
|
| It's what Swift does if inlining is defeated, the type
| arguments are passed at runtime as a classical argument on
| stack.
|
| You don't need full reflection as you suggest, only to be
| able to materialize the types as structs + an interface at
| runtime. Yes, it's a little slower but most of the overhead
| comes from the function not be inlined.
| klodolph wrote:
| Yes, I agree. I think one of the key problems here is that
| any solution would create a distinction between polymorphic
| and monomorphic methods. This is why languages are so much
| more than a bag of features... every feature interacts with
| other features, often in subtle ways. For example, take a
| "simple" feature like operator overloading... if you look at
| how operator overloading _actually_ works in C++ and C#, it
| 's an incredibly complex feature in both languages, with some
| surprising behavior. That surprising behavior arises because
| features interact in weird ways that you wouldn't ordinarily
| think about.
|
| Saying "no generic methods" is a nice out.
| arwineap wrote:
| Not terribly excited for generics.
| sigmonsays wrote:
| I have this concern too. I wish to remain optimistic though.
|
| i've enjoyed go for its simplicity. At times it requires more
| code (or code generation) but that generated code was easier to
| debug and have tooling operate on.
|
| With generics, Some things will suffer. Complexity of code and
| tooling increases, tooling is probably going to suffer and
| become more costly. compile time and performance will probably
| also be affected.
|
| Generics are likely only great for collections of things, ie
| []*T, where, you need to do the same thing on many of T.
| Groxx wrote:
| You can also cast between these "facilitators", since the unused
| type doesn't affect memory layout:
| https://gotipplay.golang.org/p/2w2y1KEjXVE (from
| https://news.ycombinator.com/item?id=29586456 )
|
| I think they'll be pretty common. It's a simple structure, and
| simple to manipulate.
| parhamn wrote:
| I recently saw a go generic data structures library in the wild:
| https://github.com/zyedidia/generic.
|
| Anyone have any more? I'm curious what people come up with for
| goroutines/channels.
| mappu wrote:
| You'll notice the functional iterators under iter/ don't
| include benchmarks.
|
| Go will not inline a function that exceeds complexity metrics,
| and one of those metrics is whether the function contains a
| range statement. You will get a real heap-allocated closure
| invocation on each loop.
|
| Go's function calls are not that cheap, and these will be
| measurably slower than the for-loop equivalent. I see
| functional idioms as being a huge risk factor for Go.
| felixge wrote:
| > Go will not inline a function that exceeds complexity
| metrics
|
| True, but:
|
| > and one of those metrics is whether the function contains a
| range statement. You will get a real heap-allocated closure
| invocation on each loop.
|
| I think this is no longer true in Go 1.18, see https://github
| .com/golang/go/issues/14768#issuecomment-98175... .
| zyedidia wrote:
| Author here. I think there is definitely room for improvement
| in the iterator API. I intend to experiment with more
| implementations in the coming weeks with the goal of settling
| on something better by the time Go 1.18 is released. The main
| alternative I have in mind is a cursor-style iterator (for
| example, with `Next`, `Done`, and `Value` methods). If you
| have something else in mind too, let me know and I might be
| able to try it out.
| kubb wrote:
| This is actually a pretty neat collection of data structures!
| Goes to show what people can do given the opportunity.
| ghughes wrote:
| This has been useful: https://github.com/rakeeb-
| hossain/functools
|
| It contains generic implementations of Any
| All Count Filter ForEach Map
| Reduce ReduceRight Sum Chunk
| nkcmr wrote:
| I made this: https://github.com/nkcmr/async so I can experiment
| with the idea of Promises in Go. Keep in mind it's experimental
| and I've only lightly played around with the pattern.
| jerf wrote:
| Go doesn't have much use for promises, and generics won't
| change that, because channel covers the problem space that
| promises cover.
|
| It is obvious that a channel is not the same thing as a
| promise. They are quite different in many ways. But the
| _problems_ that you solve with promises in some languages are
| solved with channels in Go. There 's pros and cons to each,
| but the cons of channels aren't all that significant in the
| specific context of Go and there are some compelling pros to
| the channels in the specific context of Go, so there isn't a
| very fertile space left over for a promise library. I've
| already seen half-a-dozen go by (non-generic, but missing
| generics aren't the problem) and those are just the ones that
| get posted to reddit.
|
| I advise writing Go in Go, and not Javascript in Go. But it's
| your codebase.
|
| (Promises are basically an inner platform [1] for
| functionality not provided by the base language. Go provides
| the requisite functionality in the base language. I think I'm
| more hostile to inner platforms every year, but at least when
| you're adding capabilities to the base language you can't get
| any other way there's a debate to be had. Adding an inner
| platform to get functionality that already exists is just a
| bad plan.)
|
| [1]: https://en.wikipedia.org/wiki/Inner-platform_effect
| xiaq wrote:
| Promises aren't inherently an "inner platform". You can
| certainly make them part of the "base language", like Rust.
| JavaScript didn't have async functions and promises in the
| beginning, but it integrates with the rest of the language
| and runtime well enough to not feel like something tacked
| on (well, at least by the standard of JavaScript language
| design :). Whether async function + promises is a better
| model than goroutine + channels is another issue.
|
| What you said is accurate in the context of Go though.
| Having promises in Go will create an "inner platform". If
| one wants to nitpick, there may be certain workloads that
| would perform better with async functions and promises, but
| the design philosophy of Go would happily trade a small
| amount of performance for simplicity.
| jerf wrote:
| "Promises aren't inherently an "inner platform". You can
| certainly make them part of the "base language", like
| Rust."
|
| That's fair, and I will try to update my internal mental
| model and stop unconditionally referring to them as such.
| Thank you.
|
| (My primary objection to them is the way they throw away
| the stack and throw away structured programming as a
| result, but they're at least _less_ bad if they aren 't
| _also_ an inner platform. :) )
| Zababa wrote:
| That's interesting, I always found Go's handling of
| asynchronous stuff way better than other languages,
| especially the ones that relies on async.
| cube2222 wrote:
| For those interested to learn more about Go's handling of
| async, I wrote a comment a while ago where I describe why I
| too think the Go model is superior:
| https://news.ycombinator.com/item?id=27545031
|
| EDIT: Superior for most non-performance sensitive use
| cases, that is.
| Zababa wrote:
| I personally prefer Go's handling of async too. I'm not
| sure if I would call it superior, because from what I
| understand monadic async has its uses, especially when
| you don't have a runtime. But for the kind of programming
| that I do, which doesn't need every last bit of
| performance, monadic async is a pain.
|
| For people not familiar with "monadic async", it's the
| formal way to refer to the function coloring problem [1].
| Monads offer you ways to "lift" your functions/values in
| the monadic world (paint the blue values red), but you
| can't do the opposite (paint the red functions/values
| blue).
|
| [1]: http://journal.stuffwithstuff.com/2015/02/01/what-
| color-is-y...
| [deleted]
| svnpenn wrote:
| I really dont like this. It just feels like people are trying to
| ram a square peg into a round hole. Personally I dont get why
| generics were needed in the first place. Every single example Ive
| seen so far, could be pretty easily replaced with `interface{}`.
| And now that we finally have generics, people still arent happy.
| Now that they have their round peg, they are trying to now ram it
| into a star shaped hole, as is being done in this post.
|
| I really wish people would just use the language as it is. Yes,
| it does cause for some repetition, but thats not the worst thing
| in the world. Compared to some Java codebases I have seen, Go is
| extremely simple already, in terms of "flatness" in the
| filesystem, and flatness in the type system. I think people
| should just know when to leave well enough alone. I dont think
| the complexity of generics is worth it for 99/100 use cases.
| jjtheblunt wrote:
| Aren't the language builtins (map[X]Y etc) themselves generic ?
| fsdjkflsjfsoij wrote:
| > Every single example Ive seen so far, could be pretty easily
| replaced with `interface{}`
|
| Of course they could just use interface{}... But by doing that
| they lose both type safety and performance.
| svnpenn wrote:
| > they lose both type safety and performance
|
| I guess you think generics is a magic pixie dust that has the
| exact performance envelope as non-generic code?
| sidlls wrote:
| Not exact in all cases, but in some and very close in most
| others. And it isn't "magic pixie dust"--it is a lot of
| hard work to implement generics in a way that is both
| ergonomic for the programmer and efficient.
| cle wrote:
| I don't think OP was implying that it's not hard work,
| but that many folks act as if there are no perf tradeoffs
| with generics, when there are.
| adambatkin wrote:
| It depends. There is always an increased demand for
| memory (which can impact the whole hierarchy) but other
| than that it shouldn't be bad. And I think the point is
| that compared to interface{}, generics will almost always
| win. And realistically, compared to hand-rolled, you'd
| need to hand-roll (or codegen, whatever) multiple
| implementations which, as machine code, would end up
| exactly the same as the generic implementation.
|
| And what are we optimizing for anyway? Reading a bunch of
| code that uses an off-the-shelf library's Stack or Tree
| or whatever is always going to be easier to read than
| having to also have to inspect the three hand-rolled Tree
| implementations, two of which are probably buggy, in
| addition to the actual client code trying to use the tree
| to do real work. Or a library that implemented it using
| interface{} which is a big middle finger to the whole
| type system, completely discarding the compiler's ability
| to detect otherwise trivial mistakes.
| fsdjkflsjfsoij wrote:
| That depends entirely on the implementation. Almost every
| respectable implementation strategy is going to have better
| performance characteristics than an interface{} though.
___________________________________________________________________
(page generated 2021-12-22 23:00 UTC)