[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)