Table of Contents for Programming Languages: a survey
Go (golang)
Because it is moderately well-known and well-liked, Go (also called Golang) gets its own chapter.
Good for:
- writing internet servers (todo cite). efficient, concurrency, statically-linked executable.
Attributes:
- Compiled
- Garbage-collection
Pros:
- Compiles fast
- Simple grammar
- "Compared to other languages in the C family, its grammar is modest in size, with only 25 keywords (C99 has 37; C++11 has 84; the numbers continue to grow). More important, the grammar is regular and therefore easy to parse (mostly; there are a couple of quirks we might have fixed but didn't discover early enough)." -- https://talks.golang.org/2012/splash.article
- "Unlike C and Java and especially C++, Go can be parsed without type information or a symbol table; there is no type-specific context" -- https://talks.golang.org/2012/splash.article
- Separate compilation (todo)
- Gofmt
- Produces a single statically-linked executable.
- Structurally typed interfaces
- Relatively efficient memory usage compared to many high-level languages
- named return values [1]
- M:N mapped goroutines (by 'M:N mapped' i mean that Go can start multiple threads and migrate goroutines among them so that if a goroutine blocks, the other goroutines aren't blocked; this is in contrast to having all Goroutines on one thread, or to 1:1 mapping (where each goroutine would have its own OS thread))
- these are semi-premptive; the programmer doesn't have to explicitly yield, rather the Go runtime will yield at various places, such as function calls; but it is possible to go into a tight loop in which case the Go runtime never yields
Cons:
- Lack of fine-grained control over memory management
- Bills itself as a 'systems programming language' however does not allow the programmer fine-grained control over memory management, instead providing mandatory garbage collection. Many commentators who use C++ instead of Go say that the lack of fine-grained control over memory management is what is keeping them from using Go instead of C++, and prevent Go from being a true 'systems programming language' (see various comments at https://news.ycombinator.com/item?id=6417859 ).
- No generics
- No dynamic linking
- No exceptions (more of a best practice than a language constraint though)
- Compiler error to have unused variables and imports -- this can make it cumbersome to experiment and debug, as you have to comment out unused stuff
- No scheduler preemption as of this writing (Aug 2013), but they're working on it
- Multiprocess Go programs can use data races to gain access to unsafe pointer manipulation, meaning that sandboxed systems (such as App Engine) must restrict their users to single-threaded Go. See http://research.swtch.com/gorace
Tours and tutorials:
Feature lists and discussions and feature tutorials:
Overviews:
Best practices:
Library highlights:
Respected exemplar code:
- the Go standard library [2]
Core types (from https://tour.golang.org/basics/11 ):
- bool
- string
- int int8 int16 int32 int64
- uint uint8 uint16 uint32 uint64 uintptr
- float32 float64
- complex64 complex128
Process:
Retrospectives:
- https://golang.org/doc/faq
- http://golang.org/doc/ sections Articles, Talks
- http://blog.golang.org/
- http://talks.golang.org/2012/splash.article
- http://dotgo.sourcegraph.com/post/99652962343/brad-fitzpatrick-on-the-future-of-the-go-programming
- http://research.swtch.com/generic (a blog post that may shed some light on why Golang doesnt have generics)
- Why Go doesn't have Generics (HN comment by Russ Cox): "The Go team is not against generics per se, only against doing things that are not well understood and/or don't work well with Go. There are deep technical issues that must be solved to fit the idea of generics into Go in a way that works well with the rest of the system, and we don't have solutions to those. I wrote on my blog about one issue years ago (http://research.swtch.com/generic), but there are others too. Even supposing you get past the problem on that page, the next thing you would run into is how to allow programmers to omit type annotations in a useful, easy-to-explain way. As an example, C++ lets you write make_pair(1, "foo") instead of make_pair<int, string>(1, "foo"), but the logic behind inferring the annotations takes pages and pages of specification, which doesn't make for a particularly understandable programming model, nor something the compiler can easily explain when things go wrong. And then there's a princess in another castle after that one I am sure. We have spoken to a few true experts in Java generics and each of them has said roughly the same thing: be very careful, it's not as easy as it looks, and you're stuck with all the mistakes you make. As a demonstration, skim through most of http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.ht... and see how long before you start to think "was this really the best way to do this?" (For example, http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypePa..., but note that the latter page is only one part of the FAQ, not the entire FAQ.). To be very clear, we acknowledge this fact: there are definite disadvantages to not having generics. You either use interface{} and give up compile-time checking or you write code generators and complicate your build process. But there are also definite disadvantages to generics as implemented in existing languages, and there is a very large advantage to not compromising today: it makes adopting a better solution tomorrow that much easier. As I said in the interview at http://www.pl-enthusiast.net/2015/03/25/interview-with-gos-russ-cox-and-sameer-ajmani/ : "Go is more an engineering project than a pure research project. Like most engineering, it is fundamentally conservative, using ideas that are proven and well understood and will work well together. The research literature’s influence comes mainly through experience with its application in earlier languages. For example, the experience with CSP applied in a handful of earlier languages—Promela, Squeak, Newsqueak, Alef, Limbo, even Concurrent ML—was just as crucial as Hoare’s original paper to bringing that idea to practice in Go. Programming language researchers are sometimes disappointed that Go hasn’t picked up more of the recent ideas from the literature, but those ideas simply haven’t had time to pass through the filter of practical experience." I believe generics is one of those ideas. It certainly needs at least one more iteration, possibly a few more than that. "
- https://blog.golang.org/open-source
- https://github.com/golang/go/wiki/ExperienceReports
- GopherCon 2014 Opening Keynote by Rob Pike
- How Go Was Made
- The Evolution of Go
- https://groups.google.com/g/golang-nuts/c/6dKNSN0M_kg/m/Y1yDJRwQBgAJ
- "Another aspect of Go being a useful programming environment is having well-defined semantics for the most common programming mistakes, which aids both understandability and debugging. This idea is hardly new. Quoting Tony Hoare again, this time from his 1972 “Quality of Software” checklist: As well as being very simple to use, a software program must be very difficult to misuse; it must be kind to programming errors, giving clear indication of their occurrence, and never becoming unpredictable in its effects. The common sense of having well-defined semantics for buggy programs is not as common as one might expect. In C/C++, undefined behavior has evolved into a kind of compiler writer's carte blanche to turn slightly buggy programs into very differently buggy programs, in ever more interesting ways. Go does not take this approach: there is no “undefined behavior.” In particular, bugs like null pointer dereferences, integer overflow, and unintentional infinite loops all have defined semantics in Go. " -- [3]
- "The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt. – Rob Pike" via [4]
- "It must be familiar, roughly C-like. Programmers working at Google are early in their careers and are most familiar with procedural languages, particularly from the C family. The need to get programmers productive quickly in a new language means that the language cannot be too radical. – Rob Pike" via [5]
- https://cacm.acm.org/magazines/2022/5/260357-the-go-programming-language-and-environment/fulltext
- "When we first announced Go we called it a systems programming language, and I slightly regret that because a lot of people assumed that meant it was an operating systems writing language. And what we should have called it was a 'server writing language', which is what we really thought of it as." -- Rob Pike via https://video.ch9.ms/ch9/6596/ed57ccc9-2cbb-42b0-b4f0-70909f486596/LangNext2014PanelSystemsProgramming.mp4 via https://news.ycombinator.com/item?id=31440369
" I made a list of significant simplifications in Go over C and C++:
regular syntax (don't need a symbol table to parse)
garbage collection (only)
no header files
explicit dependencies
no circular dependencies
constants are just numbers
int and int32 are distinct types
letter case sets visibility
methods for any type (no classes)
no subtype inheritance (no subclasses)
package-level initialization and well-defined order of initialization
files compiled together in a package
package-level globals presented in any order
no arithmetic conversions (constants help)
interfaces are implicit (no "implements" declaration)
embedding (no promotion to superclass)
methods are declared as functions (no special location)
methods are just functions
interfaces are just methods (no data)
methods match by name only (not by type)
no constructors or destructors
postincrement and postdecrement are statements, not expressions
no preincrement or predecrement
assignment is not an expression
evaluation order defined in assignment, function call (no "sequence point")
no pointer arithmetic
memory is always zeroed
legal to take address of local variable
no "this" in methods
segmented stacks
no const or other type annotations
no templates
no exceptions
builtin string, slice, map
array bounds checking
...
We also added some things that were not in C or C++, like slices and maps, composite literals, expressions at the top level of the file (which is a huge thing that mostly goes unremarked), reflection, garbage collection, and so on. Concurrency, too, naturally.
One thing that is conspicuously absent is of course a type hierarchy. -- " [6]
" Doug McIlroy?, the eventual inventor of Unix pipes, wrote in 1964 (!):
We should have some ways of coupling programs like garden hose--screw in another segment when it becomes necessary to massage data in another way. This is the way of IO also.
That is the way of Go also. Go takes that idea and pushes it very far. It is a language of composition and coupling.
The obvious example is the way interfaces give us the composition of components. It doesn't matter what that thing is, if it implements method M I can just drop it in here.
Another important example is how concurrency gives us the composition of independently executing computations.
And there's even an unusual (and very simple) form of type composition: embedding.
These compositional techniques are what give Go its flavor, which is profoundly different from the flavor of C++ or Java programs. " [7]
Spec
https://golang.org/ref/spec
Features
defer, panic, recover
defer is somewhat similar to 'finally' in languages with try/catch/finally, but one important difference is that the scope of 'defer' is a function, whereas the scope of a 'finally' is a try/catch block, and in those languages there can be many try/catch blocks in one function; so Golang is trying to force you to write small functions that only do one thing [8]. Otoh, the 'finally' in try/catch/finally must be in the same lexical scope as the 'try', whereas the 'defer' can appear in any lexical scope, but its body will not be run until the end of the current function scope. This means that multiple 'defer's may be encountered before any of their bodies is run. In this case, the bodies of the defers are placed onto a LIFO stack. An example, from [9]: this function prints "3210":
func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
This enables many instances of a resource to be acquired within an inner scope, and then not disposed of until the end of the function. This is unlike try/catch/finally or Python's 'with', in which the disposal must be done in the same scope in which the resource was acquired.
Links:
Iota for enumerated constants
OOP
Golang claims not to have "classes", however you can define methods on any type declared in your package [10], including struct types [11]. You cannot declare methods on a type from another package [12] (todo: what is the rationale for that?).
" Go is “OO-ish” with its use of interfaces — interfaces are basically duck typing for your structs (as well as other types, because, well, just because). I had some trouble at first understanding how to get going with interfaces and pointers. You can write methods that act on WhateverYouWant? — and an interface is just an assertion that WhateverYouWant? has methods for X, Y, and Z. It wasn’t really clear to me whether methods should be acting on values or pointers. Go sort of leaves you to your own devices here.
At first I wrote my methods on the values, which seemed like the normal, American thing to do. The problem of course is that when passed to methods, values are copies of the original data, so you can’t do any OO-style mutations on the data. So instead methods should operate on the pointers, right?
This is where things get a little bit tricky if you’re accustomed to subclassing. If your operate on pointers, then your interface applies to the pointer, not to the struct (value). So if in Java you had a Car with RaceCar? and GetawayCar? as subclasses, in Go you’ll have an interface Car — which is implemented not by RaceCar? and GetawayCar?, but instead by their pointers RaceCar?* and GetawayCar?*.
This creates some friction when you’re trying to manage your car collection. For example, if you want an array with values of type Car, you need an array of pointers, which means you have to need separate storage for the actual RaceCar? and GetawayCar? values, either on the stack with a temporary variable or on the heap with calls to new. The design of interfaces is consistent, and I generally like it, but it had me scratching my head for a while as I got up to speed with all the pointers to my expensive and dangerous automobiles. " [13]
Structural typing / duck typing in interfaces
" jerf 4 hours ago
"Take interfaces. In Java you might start with them. In Go - in the best case - they emerge, when it's time for them."
And it's a relatively subtle language feature that does this, the way that any struct that implements a given interface automatically conforms to that interface without having to be declared. Which means you can declare an interface that foreign packages already conform to, and then freely use them. Which means that you can code freely with concrete structs to start with, and then trivially drop in interfaces to your code later, without having to go modify any other packages, which you may not even own.
I completely agree that on paper Java and Go look virtually identical. But the compounding effect of all those little differences makes them substantially different to work with. Good Go code does not look like good Java code. You'd never confuse them.
reply
kasey_junk 3 hours ago
There is a big downside to structural typing as golang implements it though. Refactoring tools. They quite simply cannot do the same kinds of safe refactoring something like a Java refactoring tool can do, because you can't be sure if the function you are trying to rename, add parameter too, etc. is actually the same function in question.
There are times when I love the structural typing aspects of golang (trivial dependency inversion) and there are times when I hate it (nontrivial renames), its one of many trade-offs you have to be prepared for in golang.
reply " [14]
" pcwalton 3 hours ago
> Which means you can declare an interface that foreign packages already conform to, and then freely use them.
But you cannot do the reverse: you cannot make a type from a foreign package conform to your interface by adding new methods to it. This is because, with structural typing, it's not safe to add methods to types in other packages, since if packages B and C both were to add a conflicting method Foo to a type from package A, B and C could not be linked together. This is a major downside of structural typing for interfaces. Swift, for example, has the "extension" feature, and Java's design allows for it in principle, but it's fundamentally incompatible with Go's design.
reply " [15]
"
justinsaccount 3 hours ago
> But you cannot do the reverse: you cannot make a type from a foreign package conform to your interface by adding new methods to it.
I thought you could.
You don't add the methods directly to it, but you can easily embed the foreign type into a new type that confirms to the interface you want.
type FooWrapper struct {
Foo
}
func (fw FooWrapper) SomeFunc() {
...
}
reply
pcwalton 2 hours ago
That's making a new type, which causes a lot of friction. For example, a []Foo array is not castable to a []FooWrapper? array without recreating the entire thing.
reply "
[16]
Opinions
- https://boyter.org/posts/my-personal-complaints-about-golang/
- http://cowlark.com/2009-11-15-go/
- https://www.quora.com/Do-you-feel-that-golang-is-ugly/answer/Tikhon-Jelvis?srid=hMkC&share=1 feels that Golang is ugly, due to error handling (returning both a return value and an error code, returning returnVal, err not as a tuple but in an irregular way, having nullable types instead of option/maybe types, having nullable types almost everywhere but not in primitive types, particularly not having strings be nullable even though arrays are, having a default/zero value for primitive types but not for user-defined types, not having full type inference, not having generics, maps having special-case generics, not having generics leading to a need to cast to interface{} frequently and give up static typing. bt
- -- "People like it because it's fast, the type system gets out of the way, it's not as weird as the lisp-like languages for those used to C syntax, has some cool modern features (from closures, to easy concurrency) and has a big and current standard library. I also happen like the language on its merits." -- [17]
- "fundamentally broken by a couple of ... decisions the designers made: no exceptions, no parametric polymorphism." -- [18]
- https://news.ycombinator.com/item?id=9257626
- "The creators of Go seem to think that simplicity is achieved by removing everything that isn't totally necessary. I'm not sure if I agree philosophically. If I do, then I'm not sure I like that kind of simplicity for writing software. I'd rather have slightly DRYer code than slightly simpler code." -- [19]
- Channels are not enough
- "Golang is not an especially impressive programming language --- as a language. But it seems to me like it is an undeniably impressive programming tool...The compiles are instantaneous...Go's primary goal is: excellent tooling." -- [20], [21]
- " I think the author gets closest to the mark in the Conclusion, but still falls short. Go is very attractive as an "upgrade" from Ruby/Python or Java. It's a good replacement for the interpreted languages when speed/performance matters and it makes the async paradigm feel much more accessible. And compared to Java, the fact that Go compiles to a native binary is a huge benefit. It's not a replacement for C or a good "teaching" language, nor would anyone call it "mature" at this point, but it does fill a niche and fills it pretty well." -- [22]
- "Golang was explicitly engineered to thrive in projects built by large groups of programmers with different skill levels" -- [23]
- "it is a language for teams." -- [24]
- "The goals of the Go project were to eliminate the slowness and clumsiness of software development at Google, and thereby to make the process more productive and scalable. The language was designed by and for people who write—and read and debug and maintain—large software systems" -- [25]
- "While it has great features like compilation speed or easy concurrency, the main feature that makes Go special is its extreme simplicity." -- [26]
- "I appreciate Go because it's fast, small (re: syntax and standard library), garbage collected with nice primitives (pointers and slices), concurrent with high-level primitives (goroutines, channels, select), expressive enough (strings, maps, range, first-class functions, type system, etc.), first-class support for Unicode, and it has a rock-solid standard library." -- [27]
- "I think error handling and documentation of the errors returned by methods could be improved in Go. I dislike having to comb through the Go standard library source to see what types of errors a method returns." -- [28]
- "...two reasons I like Go: 1) It's concurrency model was a revelation compared to anything I did in the C world. 2) It's opinionated....In Go, there's only one way to format your program correctly, there's only one way to document your program correctly, there's only one recommended way to serialize to JSON, it only has one (IMO really awesome package management system) etc. It's remarkable how little thought I now need to put into those things." -- [29]
- toread: https://news.ycombinator.com/item?id=10413861
- problems with errors-are-values (as opposed to exceptions): "Go's simplicity is initially refreshing, then a huge pain once you find yourself writing the same thing over and over again. A good example is errors being values — which is a great idea. But then you realize every single function needs to be littered 1-10 cases of "if err != nil {return nil, err}". It's an extremely common pattern. It's tiring to write, over and over. Tiring to refactor, too: If you change a signature (to add an error, or add another return value, for example), every error check has to be updated. Unfortunately, Go doesn't offer any abstractions that might allow you to avoid such boilerplate. Functions that return errors cannot ever be chained, for example: If you have "func bar() (MyStruct?, error)", you cannot do "foo(bar())". You must always assign the result to an intermediate variable. You could embed the error in the MyStruct? struct instead, but that goes against Go's grain." -- [30] "Chaining does work, but obviously the function needs the same signature as the return values, which limits things since most APIs don't accept errors as input." -- [31]
- "To be fair, I love many aspects of Go: Compilation speed, relative performance, ease of concurrency, static strictness. But after working with Go for a while and being quite productive with it, I'm at the same time seriously pining for a better language to replace it." -- [32]
- "On one hand, it addresses many of the pain points I've experienced with other languages. It's easy to build and deploy, reasonably performant, and has a powerful and consistent standard library. On the other… developing in it feels like a total slog. It manages to be simultaneously far too anal and overly forgiving about syntax. Visibility definition using upper/lowercase is a shite idea. Package names (and packaging generally) are a mess. Magical built-in globals are a huge design smell. The lack of generics, as cliché a complaint as it is, results in loads of duplicated or generated code when building anything more complex than the simplest app. And so on. I find the whole experience of using it to be unpleasant (rather like using a blunt knife), and yet it fits a set of requirements that mean I keep developing new projects using it – particularly high-performance small services that do things like fling JSON around, for which the built-in HTTP libraries + Gin are brilliant." -- [33]
- "Go checks a lot of boxes for my ideal language for developing web services: Static type, C derived, has garbage collection, generates a single binary, supports concurrency very well, is opinionated, is small/simple, its community prefers to just use standard lib for most work, etc. Yes, Generics is an issue and so is debugging. But, overall, I can't think of many other options that check so many boxes." [34]
- Q: "Could you give an example of a program you think Go isn't suited to? " A: "The compiler was straightforward to express in Go, but I wouldn't want to write a static analysis tool in it. I don't especially love writing database-backed web applications in Go." [35]
- "It's really quite a pity that Go's channel syntax treats channels as unique snowflakes, rather just being sugar for calls into an interface that would allow the underlying channel implementation to differ based on software needs." "That's an excellent example (in a long list) of things that would be possible with generics, or even parameterized packages. They could have provided an interface Channel[T] with syntax sugar if desirable. But as it is, everything in Go that can handle multiple types has snowflake status." [36] [37]
- "Go doesn’t provide a way to have arbitrarily sized buffers - you have to allocate the buffer size in advance...Not having arbitrarily buffered channels means that a naive send on any channel could block at any time. You want to send on a channel and update some other bookkeeping under a mutex? Careful! Your channel send might block!" [38]
- If you haven’t yet, please go take a look at...What Color is Your Function. Without being about Go specifically, this blog post...lays out exactly why goroutines are Go’s best feature (and incidentally one of the ways Go is better than Rust for some applications). If you’re still writing code in a programming language that forces keywords like yield on you to get high performance, concurrency, or an event-driven model, you are living in the past.... Go is ...one of the best...of languages that implement an M:N threading model that’s not 1:1, and ...that’s powerful....If I had to pick one other leading feature of Go, it’s interfaces. Statically-typed duck typing makes extending and working with your own or someone else’s project so fun and amazing..." [39]
- http://yager.io/programming/go.html
- " Having just spent the last two months writing Go code, exceptions are the thing I miss most (well, besides the ternary operator and map/reduce operations). Not only are errors painful to debug without stacktraces, but every single method call is followed by three lines of "if err != null {". I am amazed that folks tolerate the sheer amount of repetitive typing required by the language. " [40]
- "Fast build times, small binaries, one file, built in formatting, great tooling, built in test framework, race detector, visual profilers, a nice concurrency model" -- [41]
- "I find reading Go easier than any other language" -- [42]
- "It's not a joy to write but pleasant to read and understand" [43]
- "Go trivially generates static binaries, and cross-compiling is also trivial (as long as you don't use CGo). I can't actually think of a single other language that matches that... " [44]
- "In my experience writing go is very fast and reading go is also very fast. " [45]
- "The legibility and simplicity are my favorite things about Go. I know that I can fearlessly dive into other people's source to figure out what's going on without running into 15 levels of indirection hell and a bunch of language features/idioms that I'm not familiar with for one reason or another." [46]
- "Go is a different type of terse. I think terseness goes beyond the number of lines. When I skim Go code, I can grasp the meaning of it very quickly." [47]
- Q: Why would I want use Go for the backend A: "Fast, built-in concurrency, enough high level concepts and structures to support fast development, and really easy to deploy. The last part is probably the greatest. It supports simple host-based deploys, tiny docker container deploys, and eliminates the typical dependency conflict problems of Python and Node. It's also pragmatic enough to make it easy to develop in for developers across the skill bell curve." [48]
- https://npf.io/2017/03/3.5yrs-500k-lines-of-go/
- https://www.quora.com/Do-you-feel-that-golang-is-ugly
- "...I would err on the side of Go for the majority of commercial projects. The article mentions the impressive worst case pause times of Go's GC. Since then we have performed some additional benchmarking. The conclusion was: it is impressive, but there are still a couple of issues that break the sub 1ms claims. We blogged about this here: https://making.pusher.com/golangs-real-time-gc-in-theory-and.... It's hard to guarantee low latency in all cases... Michael also mentions that there is nothing like ThreadScope?, or at least nothing that's easy to find. The latter is true. There is an impressive runtime system event visualiser which can be opened with `go tool trace` https://golang.org/cmd/trace/. You can see a screenshot of this in the GC blog post I linked to above. Unfortunately the only documentation is this Google Doc: https://docs.google.com/document/d/1FP5apqzBgr7ahCCgFO-yoVhk... which is tricky to find, and could be more in-depth." [49]
- https://www.barrucadu.co.uk/posts/etc/2016-08-25-three-months-of-go.html] :
- The Good:
- "Incredibly easy to pick up"
- "Garbage collector keeps getting better and better"
- "Style wars are a thing of the past" (gofmt)
- The Neutral:
- "Code generation seems to be the accepted solution to a lot of problems
- "Strict, not lazy, evaluation"
- "The standard library is not so great"
- The Bad:
- "I also agree with this Quora answer by Tikhon Jelvis to do you feel that golang is ugly?, so have a look at that once you’ve read this section
- "A culture of “backwards compatibility at all costs”": "In Go, you import packages by URL. If the URL points to, say, GitHub?, then go get downloads HEAD of master and uses that. There is no way to specify a version, unless you have separate URLs for each version of your library. This is just insane. Go has a very strong culture of backwards compatibility, which I think is largely due to this. Even if you have a flaw in the API of your library, you can’t actually fix it because that would break all of your reverse-dependencies, unless they do vendoring, or pin to a specific commit."
- "The type system is really weak": "A common mantra in Haskell is “make illegal states unrepresentable,” which is great. If you’ve never come across it before it means to choose your types such that an illegal value is a static error. Want to avoid nulls? Use an option type. Want to ensure a list has at least one element? Use a nonempty list type. Use proper enums, not just ints. etc etc
- "No generics"
- "No sum types"
- "No separation of pure code from effectful code"
- "The tooling is bad"
- "Godoc groups bindings by type, and then sorts alphabetically. Code is not written like that, code is written with related functions in proximity to each other. The source order is almost always better than how godoc sorts things. Also, godoc doesn’t even support lists: "Previous proposals similar to this have been rejected on grounds that it’s a slippery slope from this to Markdown or worse."
- "There is nothing like GHC’s heap profiling": "Go has a snapshot-based memory profiler. You can take a snapshot at a point in time, and see which functions and types are taking up the heap space. However, there is nothing like this. Being able to see not only a snapshot, but also how things have changed over time, is incredibly useful for spotting memory leaks. If all you have is a snapshot, all you can really say is “well, the number of allocated Foos looks a bit high, is that right?” With a graph you can say “the number of allocated Foos is increasing when it shouldn’t be.”"
- "There is (was?) nothing like ThreadScope?"
- "Zero values are almost never what you want": "Zero values caused so many problems over the summer, because everything would appear to be fine, then it suddenly breaks because the zero value wasn’t sensible for its context of use. Perhaps it’s an unrelated change that causes things to break (like a struct getting an extra field). I would much rather: *Drop the syntax for declaring a variable without giving it a value. * Make it an error to not initialise a struct field."
- "Lots of boilerplate"
- "Because you have to check error values, if you want to perform a sequence of possibly-erroring computations, where the successful result of one feeds into the next, there is a lot of typing. In Haskell, you’d just use the Either monad."
- "If you want to sort a slice, because there are no generics, you need to wrap the slice in another type and implement three methods on that type. So that’s four lines of code to sort a slice of uints, four lines to sort a slice of uint8s, four lines to sort a slice of uint16s, and so on. In Haskell, you’d just use the generic sort."
- "My Overall Feelings... I will probably never choose to use Go for anything ever again, unless I’m being paid for it. Go is just too different to how I think: when I approach a programming problem, I first think about the types and abstractions that will be useful; I think about statically enforcing behaviour; and I don’t worry about the cost of intermediary data structures, because that price is almost never paid in full."
- "The Go GC is performant and predictable, unlike the JVM GC. We do have some very memory-allocation-conscious code patterns to minimize the performance impact of working in a garbage-collected language runtime, but in the end it's not as bad as you might expect if your expectations are coming from the JVM world....Go has many of the necessary libraries, and they're straightforward and very well written...The native support for concurrency in Go is a huge plus. We use thousands of goroutines in CockroachDB?, and that's been a huge blessing." [50]
- someone else on the same team: "The GC is sometimes a performance issue, but it's manageable (and Go gives you better tools to limit the cost of GC than many other garbage-collected languages)" [51]
- "Go provides a smattering of blessed polymorphic types (slices, maps, chans, pointers) and functions (len, append, delete, chan send, chan recv) that go a long way." [52]
- " Go is an example of how to be extremely successful by catering to the needs of the project's core audience (well-rounded stdlib, extremely fast GC, short build times, trivial deployment, etc) while paying much less attention to the vocal minority's complaints (generics, package system, etc). " [53]
- "Dep handling was never considered. Makes sense given Google's monorepo but thats not how the world works." [54]
- "Stdlib just loosely wraps posix features with many C flags copied verbatim. These APIs are old and could use a refresh but Go never bothered." [55]
- "I disagree with these. Go runtime/stdlib is architected to work around many many POSIX headaches and design bugs, hiding them completely from programmers, and to be fully portable. For instance: * Concurrency is completely redesigned (goroutines). * Signal handling is redesigned and doesn't cause bugs when interacting with concurrency. * Forking/Exec'ing is redesigned not to cause fd leaks in subprocesses (all file descriptors are marked as O_CLOEXEC, in a race-free way), nor have races while interacting with concurrency * Sockets are exposed through a higher-level API (Dialer/Listener). * epoll is not exposed but transparently used by a single thread to handle all supported file descriptors without wasting OS threads, to improve performance In fact, I think the only thing that is pretty much low-level is os.File and filesystem access in general, which tends to expose lower-level details." [56]
- "Having experienced a situation where I had to call select() directly, I have to completely disagree. If you run into situations like this, you need the syscall escape hatch. It's really painful to get this working, and it interacts poorly with goroutines. Sockets are actually another good example. If you need to tweak then at all, you get to use the same old horrendous POSIX names for everything. No name aliases?" [57]
- "No easy way to construct arenas/pools. Once you go down this route you have a great headache of releasing in the right places. The GC doesn't cut it, you need pools sometimes" [58]
- "Debugging basically doesn't work on some OSes. No easy way to attach gdb and see what's happening. Doubly so if you use Cgo" [59]
- "Similarly, Go doesnt bother to hide the differences of different OSes. Up to you as the programmer. Again not surprising for Google's all Linux world. If everything is Linux then OS difference doesnt matter. But even Python does a better job here." [60]
- "Logging is poorly thought out as evidenced by multitude of third-party log packages. Anemic compiler means you can't get verbose logging without paying a performance penalty." [61]
- "No RAII. Defers are a lazy attempt at this, but they're not even close to being as good as RAII. This is probably the biggest point where you realize Go can't dethrone C++" [62]
- "Tricky Close() semantics force you to architect entire program around who will close() things at end of their lifetime. Lots of terrible hacks ensue when people build something that works but realize close ownership is ambiguous and rightfully don't want to rebuild it all" [63]
- "Channels don't have a good way to deal with errors. You're forced to come up with an ad hoc solution to deal with your graph of goroutines/channels when one node errors" [64]
- "No supervision tree. Erlang existed far before Go but they didn't learn from this key feature. But it would greatly enhance Go to have it" [65]
- "Hacky reflection semantics that cause subtle runtime bugs when a JSON struct field's name starts with a lowercase letter. And of course, there are no generics, the larger issue here." [66]
- "While It's rarely hard to read a particular piece of code, the combination of duck typing, only structs as receivers, and interfaces can make it as hard to understand as any dynamic language. There are quite a few traps for the unwary, like that a unitialized pointer to a concrete type will not test as null/nil if it was passed as any of its interface types, that channels ( for concurrenct ) isn't easy except in a few special cases, an that a closed listening socket isn't necessarily closed immediately, and all sorts of shenanigans. You could expect all those from a relatively new language, but not one that is ostensibly very simple. If you, however, refrain from using any concurrency, interfaces, slices, and maps ( maps and slices are really reference types made to look superficially like they are passed by value ), and accept that the first element in an iteration over a string should give a different result than indexing the first element in that string. Well, then it's as easy as it looks, for anything where you don't need to actually handle errors, or where you can always handle them where they occur. Also please do remember that log.Fatal() is actually going to kill your process, because calling os.Exit() is obviously the task of the loggin framework! But yes, Go could have been that easy to read, but there are quite a few really odd decisions that make a lot of the apparent simplicity be rather superficial unless you happen to use a specific subset of the language. " [67]
- on the question of whether Go is easy to read: "Until the day where you discover you need to keep track of the difference between a nil interface and a non-nil interface acquired on a nil pointer..." [68]
- "Refactorings are simpler due to the use of consumer-side interfaces. Say you want to inject an in-memory cache above a backing store. To do that you probably have to change the constructor and provide an implementation matching the 2-4 relevant methods. That's it." [69]
- "Worth noting, though, is that D and Go compile really quickly." [70]
- "No ability to do Generics, no ability to do operator overloading, no ability to make your own for each style loop objects, very manual and repetitive error handling" -- [71]
- "It is hard to understand go's criticism if you have not experienced languages leveraging modern datastructures such as, for instance, union types/pattern matching. But if you have, you really feel like the language lacks something." -- [72]" -- [73]
- "By declaring a field/variable []Thing vs []*Thing you get different for loop semantics. Way to easy to think your mutating the array item, but only mutating a local copy or vice versa. If you change the field/variable you need to audit all your code to make sure you haven't broken things." -- [74]
- "gofmt feels way out of date. These days clang-format (c++), prettier (typescript), black (python), scalafmt (scala) take care of wrapping/unwrapping lines such as function definitions or function calls. They basically cover all formatting needs so you never have to manually format anything." -- [75]
- "Scope of element in for-range loop isn't right, so capturing that scope in a lambda does the wrong thing with no warning." -- [76]
- "Encourages use of indexes; which is error prone, most modern languages allow writing most code without needing indexes using map/filter/reduce or comprehensions." -- [77]
- "No help from type-system for use of pointer without nil check." -- [78]
- "Very easy to get nils in places one would hope to be able to prohibit them in. EG Using pointer as a poor man's unique_ptr<> means that I also get optional<> symantics (without the type checking) when I don't want or expect such. Also allows for aliasing when I don't want or expect such." -- [79]
- "Difference between '=' and ':=' is silly, especially since ':=' can be used to reassign values. Even more frustrating that ':=' creates shadowing in nested scopes, so doesn't always do what one would expect it would do, such as accidentally creating a shadowed 'err' that doesn't get checked." -- [80]
- "if/switch should be allowed to be expressions, allowing much safer single-expression initialization of variables, rather then requiring default initialization and mutation, which is much easier to get wrong." -- [81]
- "...Go is outright _primitive_. The most notorious things about the language itself: are Goroutines, channels and the interfaces. This is why there is so much discussion around, for instance, Generics. Noone really "wants" Generics. Instead, they just need to not repeat themselves. There are very few ways to work around issues in the language itself. Another issue is that, if a library implements something in a way that doesn't suit you, or you need to add extra behavior, you are out of luck. Say you want to add distributed tracing to code that's not owned by you (even if writting in your company). You'll have to track down people, you cannot "annotate" your way as other languages can. That said, the fact that it is mind-numbly dumb can be a feature. Everything is damn obvious. Error handlers? Staring at you in the face like the sore thumb they are supposed to be. You can't even get too fancy with 'design patterns', just go write the function you need to solve your problem and stop reading that gang of four book..." -- [82]
- " Okay, so there's definitely simplicity. I agree that this is in many ways a nice change from some of the more popular languages, which can get a bit complex and heavy, with a focus on features that are nice in isolation but add up to a surprisingly difficult architecture to comprehend at the global level. I just don't think Go gets the balance right. There are some parts of the language that are such a joy to use – C interop is simple and elegant, the concurrency story is great, the standard library is great, and in contrast to some other people, I think the error handling is also a nice example of simple and effective design. The wider tooling system is decently usable (with the exception of gofmt, as mentioned in the article, which I think is the single best tool I've ever used in any language). But "simplicity" in Go-land seems sometimes to be the enemy of expressiveness. The lack of abstractions over operations like filtering, mapping, or removing items from a collection is incredibly irritating for such a common operation – instead of immediately obvious actions, there are tedious manual loops where I end up having to read like 10 lines of code to figure out "oh this just removes something from a list". The use of zero values is a crime against nature. The type system is shameful in practice compared to other modern languages, with so much code just being "just slap an interface{} in there and we can all pretend this isn't a disaster waiting to happen". It feels like such a lost opportunity to exploit a richer type system to eliminate whole classes of common errors (which are predictably the ones I keep making in Go code.) I guess it's frustrating that a language—which is relatively well-designed and intended to be simple—too often makes things more complicated overall by making fallible humans do things that computers are better and more reliable at. I'll keep using it, because it fills a really nice niche that no other language is quite suitable for. But I'll keep being a bit annoyed about it, too." [83]
- "...the lack of enums and sum types means it's hard to represent, safely and efficiently, anything that should be the same type but have different values within a strict set of allowed values." [84]
- "After using Go for a couple years, I’ve really come to appreciate its simplicity...what makes Go unique. I think a lot of the real power of Go has come from the philosophy of its designers: There’s a strong emphasis on forward compatibility, the language doesn’t have flashy features that hurt readability, and it comes out-of-the-box with everything you truly need (a basic test library, solid networking and synchronization primitives, and templating, to name a few)...Go is "You Ain't Gonna Need It" taken to the extreme. Need is the operative word here. You won’t need generics, but you will almost assuredly want them. Ditto syntactic sugar for error handling, functional programming, and operator overloading. As the cliche goes, sometimes less is more. With the exception of named return values, I can’t think of any footguns that have been added to the language." [85]
- "...Go is just a better C with coroutines" [86]
- "I'm a Go programmer and I can confirm that Go code (both written by me and by anybody else) is easy to read, debug and refactor. This is thanks to simple yet powerful Go syntax, which doesn't allow constructing implicitly executed code. I.e. it doesn't support the following "features" from other programming languages, which instantly increase code complexity because it becomes impossible to understand the code by just reading it top down:
- Operator overloading
- Function overloading
- Constructors and destructors
- Implicit type conversion
- Implicit types (classes) Go also doesn't support type templates and function templates yet (aka generics or contracts). This also plays significant role in easy-to-read Go code." [87]
- https://web.archive.org/web/20191205031849/https://grimoire.ca/dev/go
- "This must've been quite an old article, because lo and behold, nearly every single complaint on that page is being addressed with gusto by the Go community! Proposals on generics and error handling is well underway, and his complaints regarding GOPATH and the go get has been solved with Go modules. EDIT: As I expected, the linked page is written on Feb 2018[1], things indeed have changed a lot since then." [88]
- "> I believe it was Russ Cox who observed that “the standard library is where good code goes to die”. I find this interesting because IME Go’s standard library is something languages should aspire to: tightly scoped, high quality, and guaranteed to be stable. Want to make an incompatible “v2” or “v3” of your library? Well, that’ll be trickier. Want to add some more experimental interface or support some standard that’s not common (yet)? Probably not in the standard library. Good. I’ve compiled old Go code for work (last built against 1.3 iirc) and thankfully the developers back then made the conscious, documented decision to go stdlib only. My career has given me a few opportunities to build code whose maintenance stopped before my children were born, but only Go makes it relatively pleasant. " -- [89]
- "There are a bunch of packages that are “frozen” and effectively (un)dead, such as net/smtp, encoding/gob, text/tabwriter, archive/tar, log/syslog, net/rpc, net/rpc/jsonrpc, and perhaps some others. In hindsight, adding these to the standard library was a mistake. While these are certainly all useful packages, supporting the full range of SMTP in all its complexity and extensions or all the different tar flavours isn’t really something that’s best done in the standard library. There are also a bunch of “mistakes” in the standard library that are hard to fix. A well-known example is the log package which doesn’t have a logger interface (only a log.Logger struct) and doesn’t offer a lot of flexibility in general, which is why so many people are using logrus or whatnot. There’s a few more mistakes like this, and is all quite hard to change now in a compatible way (although, with modules, there could be a /v2 for stdlib packages too at some point). Things like contexts and the (upcoming) fs.FS changed a number of paradigms, and made a number of methods obsolete (like the Cancel channel on net.Dialer), as well as introducing a bunch of inconsistencies and a whole slew of duplicated functions (I’m reminded by Microsoft’s *Ex() functions). When generics arrive next year it’ll be even worse. Third-party code isn’t going to stop working, and if you vendor the code then you can be guaranteed it’s not going to disappear either. If your Go 1.3 project would have vendored any external code then the project would compile and run today as well without any changes. My point is, all this strong compatibility is great and I love it too, but also comes with some serious drawbacks. You really want to think carefully about where you want to apply it. " -- [90]
- various flaws in Golang's libraries, having to do with filesystem file attributes, filesystem paths, clocks, cross-platform ifdefs; and suggestions about how it should be (usually based on how Rust does the same thing): https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-ride
- "...sql.Result (the return value of Exec) has a LastInsertId?() that's an int64, so if you're using uuids, you can't use that at all and have to call Query instead and manage generated IDs yourself." [91]
- "My most popular project on Github is currently a program I slapped together in Go a long time ago. The `sync/atomic` issue mentioned at the end of the article is THE issue that made me stop considering Go for anything other than trivial things. Lack of decent error handling, a terrible builtin json library, constant `interface{}` to poorly substitute for generics, the package management issues that made Node.js look well-thought-out by comparison, struct field tags, and generators provided by the core team that set off linters provided by the core team with no good way to silence them kind of piled on before that, but the `atomic` issue is the one that made me avoid it. The author is right, all the little things add up. Note that a bunch of these may have been fixed since I last used it, but honestly, I haven't checked because it was frustrating working in it and debugging it. It's a shame, `pprof` and the race detector are pretty cool. " [92]
- https://www.fredrikholmqvist.com/posts/brooks-wirth-go/
- https://github.com/ksimka/go-is-not-good
- For those familiar with Go and (cargo | npm | etc) how is "minimal version selection" working out?
- https://jesseduffield.com/Gos-Shortcomings-1/
- https://jesseduffield.com/Gos-Shortcomings-2/
- https://pingcap.com/blog/early-impressions-of-go-from-a-rust-programmer
- "...when you switch to Go, the development experience feels incredibly fast and great. Autocompletion in VSCode works, instantaneously, all the time. Errors are shown in real time. Compilation is fast. Error messages are human readable. Updating Go or dependencies doesn't break existing code." [93] "
- https://dmitryfrank.com/articles/i_love_go
- https://news.ycombinator.com/item?id=15856111
- " After working with it for a year, these are the things I don't like about Go: 1) No constructors. When I create a struct, without a constructor, I can't be 100% if certain fields are populated or not. With a constructor, I could force this. With a large number of people working on a code base, you can't assume anything, unfortunately, but without a constructor to force exact data in a struct, it makes it more tedious to be sure. 2) Error handling is ugly and tedious. Not being able to get the type of an error makes it a lot more inconvenient. At least in our codebase, errors are almost string parsing, it's ridiculous. 3) Nil map vs empty map is a very strange thing to keep in the code. Why even allow this distinction? 4) Named return parameters are really stupid. This is another thing that doesn't fit the philosophy of what I understand Go to be. It forces a bunch of verbose opinionated rules on us, but then allows named return parameters which causes confusion. 5) It's very hard to tell the difference between package names vs structs. Luckily Gogland allows you to change the color for package names so this helps me out tremendously, but there should be a better way to differentiate without relying on your IDE. Overall it's okay but nowhere near perfect and has some really weird deficiencies. The company I work for uses Go with dependency injection which I hate and I think is very ugly but a paycheck is a paycheck. " [94]
- "This is a language that seemingly insists on breaking the wheel. Notice I didn't say reinventing, because in over a decade they haven't actually gotten a working version of the wheel out yet. I mean seriously, Go was released in 2009: if I'm not mistaken, C# had a pretty good generic system already, and there were a bunch of other workable options to copy from less-mainstream languages. Not that copying has helped: they copied coroutines from other languages, but failed to present a coherent memory sharing model (just copy Erlang FFS), which severely limits the usefulness of coroutines. Every few years someone gets me to try Go out again and I discover, yet again, that it's still struggling with problems that were solved before it existed. It's a shame that this language has stolen so much mind share from more deserving languages." -- [95]
- "Go steals the mindshare because it focuses on the important problems that other languages neglect in part or in full: performance, tooling, ecosystem, simplicity, readability, learning curve, etc." -- [96]
- "But the fact remains that it's a productive language with excellent tooling and fast build times, and a lot of people really like working with it (myself included). I believe the reason it has gotten as far as it has without generics is because it did always have them -- but only for the built-in data types (slices, maps, channels)." -- [97]
- https://typesanitizer.com/blog/go-experience-report.html
- "Many people find how it does concurrency, OO without classes (methods assigned to structs), its simplicity, and compilation speed as refreshing" -- [98]
- "As far as I could see, there was one cool thing Go brought to the table, and it’s select {}. That was (and still is, I think) pretty uncommon, though not unheard of (Erlang’s receive is similar, though it has different capabilities owing to different language semantics)." -- [99]
- vs Rust: "I'd add one more addition to use Rust (speaking from an ex-Go dev): Use Rust if you want a robust std lib. Go is good, i used it for ~5 years, but man Rust was a breath of fresh air with the amount of tooling that helped me solve problems. Iterators, are a great example. I also preferred Crates vs Github Repos, but i think that's just personal preference.. not objective." -- [100]
- "I really like the engineering principles in general that the Go team uses, however, I just don't like Go. That isn't meant as a slight or anything other than simply my opinion. That said, I really like the idea of a simple language based on the sort of principles demonstrated here. The runtime seems really nice, I just wish I liked the language better (IMO: not expressive enough, needs better error handling, needs much better enums, needs pattern matching, has error prone C-style 'for', almost everything is statements not expressions, special comments are error prone, has nil pointers, etc.). In the end I think if someone were to write a slightly simpler version of Rust with the Go runtime it might be pretty neat. That said, I don't know what I'd want to drop from Rust so maybe I'm just fantasizing." -- [101]
- " I haven't found any issues with expression, so far. I wouldn't use it to write a UI, but for writing networking code or automated tasks I find it perfectly suited to the task. I appreciate it's error handling. It's burdensome, sure, but it presents almost no additional cognitive load when attempting to reason about control flow. It essentially has no enums. However, it has a comfortable type system that can wrap primitive types, and you can define methods on those wrapped types. It's serviceable, but not elegant. I have never been a fan of pattern matching outside of functional languages. The phenomenon I notice in languages that do have it, is the majority of uses cases seem to be a pattern match with two outcomes, one a Some() and the other a None(). It really seems like a more annoying way to write if {} else {}. It has for, but it also has 'range.' It makes up for a lot. It has nil pointers, which are just a "zero value." It's not uncommon to make it useful and safer in certain contexts. You can request a lookup in a map that is nil. You can ask for the length of a slice that is nil. You get sensible answers still. There are still footguns, but they're all single barrel.. which is _nicer_. I don't need a perfect language. Good enough with low cognitive load and amazing cross-compile facilities I'll take any day of the week. " -- [102]
- " Love go as a platform.. self contained binaries have been a miracle for ops..but have a few big hangups about using the language full time because of the sucky ergonomics.
- No optional/named parameters. Writing a whole function per parameter for function chaining is excessive. This would not be difficult to add to the compiler (i've done it and have seriously considered using the fork) but it seems like the team is just stubborn about adding stuff like this.
- No default struct values. 0 isn't good enough in real world scenarios.. have fun writing BlahInit?(...) for everything.
- Would be nice to get the question mark syntax for error handling. Error handling shorthand for if err != nil would also be very welcome. " -- [103]
- https://fasterthanli.me/articles/lies-we-tell-ourselves-to-keep-using-golang
- https://kristoff.it/blog/why-go-and-not-rust/
- https://shane.ai/posts/threads-and-goroutines/
- "I really enjoy csp. I wish go had sum types and function overloading." -- [104]
- "Go chose to have append, modelled after libc realloc. It has pervasive multi-threading and non-thread-safe built-in map. So in general, Go is not that focused on preventing footguns." -- [105]
- Q: "What is the footgun with append/realloc?" A: "You have to use it as: slice = append(slice, item); This boilerplatey reassignment is crucial. If you forget about the reassignment, or assign to a variable that is a temporary/scoped copy of a variable, rather than the place that owns the slice: append(slice, item) then it will sometimes seem to work, until it won’t, depending on capacity. It’s a very low-level design that lays bare the reallocation happening. It’s fine to have it in the language for advanced uses, but I’d expect language to have an everyday foolproof alternative that ensures the slice’s data pointer is always updated." -- [106]
- "And the other shoe is that append looks like it copies the slice so some folks will write s2 = append(s1, item) to create a copy-with-append, this is always an error because if s1 has sufficient capacity they will now be sharing backing buffer, and will overwrite one another’s data." -- [107]
Opinionated comparisons / pros and cons
- Concurrency is cleaner than js callbacks (todo cite)
- http://dtrace.org/blogs/wesolows/2014/12/29/golang-is-trash/
- java vs go: even: goroutines are provided by Quasar, Processor provides metaprogramming/templates; pro-Java: Java is faster; pro-Golang: less startup latency; static compilation [108] [109]
- https://www.quora.com/Do-you-feel-that-golang-is-ugly/answer/Tikhon-Jelvis?srid=hMkC&share=1 prefers OCaml to Golang for everything except its concurrency; notes that OCaml has structural subtyping, no nulls, variants, efficient generics, tuples, pattern matching, full type inference (contrast to the complains by the same author, see "opinons", above)
- Go vs. Brand X (actual title is "On go")
- "I've thought for years that it was interesting how yosefk's criticisms of C++ in the "Frequently Questioned Answers" directly correspond to decisions in Golang: "compile times are long" → "use the Plan 9 toolchain", "memory management is difficult and unsafe" → "garbage collection", "templates are a mess" → "no generics", "exceptions interact badly with RAII" → "no traditional exceptions", "header files are a pain" → "use packages and forbid circular dependencies", etc." -- [110]
- Q: Why do exceptions interact badly with RAII? A: "You have to think carefully about how to clean up your object if something throws during its constructor, and throwing an exception in a destructor can lead to your program aborting if that destructor was called as a result of another exception being thrown. Note that I don't think banning exceptions is really the answer; in particular, the destructor issue is just a specific case of "handling errors during finalization is really hard", and you can't get away from finalization in general. Exceptions really put those issues front and center, though." -- [111]
- "an ultra-fast toolchain and a carefully designed standard library that nurtured an idiom of composable APIs defined by nothing more complicated than structs." -- [112]
- "When I jumped ship from C to Java, Java's object orientation wasn't exciting to me, and its exception handling is an acquired taste. But dynamically growable, garbage collected buffers are something I'd missed for a long time. And the simple ability to concatenate strings without first reserving space for the result. Oh, and hash maps! Remarkably versatile data structures, those. Coming from C, Java was a delight. Java would have a lot more trouble dragging me away from Go." -- [113]
- "To me, Go seems to have about the same amount of expressiveness as C and Java. The difference being that it's safer than C and more UNIXy/less OO than Java." -- [114]
- Q: What are you missing in Go compared to Java besides generics?. A: "That's a clever way to attempt to constrain the conversation away from the elephant in the room. "Other than the bullet in your stomach, how are you feeling?"" -- [115]
- ""C++ is too complex, too unsafe, and too hard to maintain, and Python is too slow."...Which is why Java gets used instead....But Java can suffer from similar complexity and maintainability issues too. Go, for all it's short comings and missing features, is closer to the "instant gratification" state that Python and it's ilk enjoy while having performance closer to that of Java." -- https://news.ycombinator.com/item?id=9712928
- "In extremely flexible languages, you can find yourself burning lots of time finding the most "elegant" way to express simple concepts, like "how am I going to write this SQL column to this series of DIVs". Not coincidentally, Golang sucks at this problem. But there is something about writing in Golang that makes it easier to dive into meatier code. For instance: I've written programmable assemblers in Ruby and in Golang. With Ruby, I kept refining the assembler until the DSL looked identical to normal X86 assembly (I even hooked the bracket operators to make them behave like dereferences). With Golang, I quickly moved past the assembler into an IR and a compiler." "Same experience here. I am working on a neural net dependency parser and pretty much started C++ and Go implementations in parallel, because this was one of my first larger Go projects and I had my doubts about using Go for this. In the meanwhile, the Go implementation is a complete and fast parser (the number crunching is done using OpenBLAS? via gonum), which I now use for research. In C++ I am still tweaking the abstractions for the basic stuff (word embeddings, transition systems). Of course, I could make a simple, straightforward implementation in C++, but the language encourages you to finetune the abstractions and you have to the choice between template-heavy/light code (with its respective downsides)." -- [116]
- "Go encourages me more to break stuff up more in different packages. In C++, every separate library means new build machinery, some way to roll it out on systems, etc. In Go it's as simple as creating a new repository and adding imports." -- [117]
- "I agree that the number of possible solutions is often smaller than in other languages. But there are still often multiple ways which are considered idiomatic Go (in the sense that they are all widely used). E.g. if you build some iterable type, you could provide iteration via: 1. Go channels: send every value over a channel. 2. Callbacks: provide an iteration function which takes a callback function. 3. Closures: let a closure return the next value when called. 4. Iterators: create a separate iterator type with e.g. HasNext?()/Next() methods. In this respect C was much simpler, because only (2) and (4) were really possible (and sometimes indexing)." -- [118]
- "I'm a fan of Go and have been using it for a [relatively] long time across various large and small systems. I really like it. To me, what's been abundantly clear is that: it's a horrible choice for types of apps a lot of people are using it for. Specifically, systems where ror/django/express/php have traditionally excelled at. I'd say "CRUD", but that's too narrow. The expressiveness and productivity of dynamic languages continues to awe me. This is particularly true for web systems that, on one end, deal with the object-relational mismatch and on the other transforms and outputs json or html. Go's great, but it feels like we've forgotten some of the pain-driven lessons Hibernate and their ilk taught us. ... We learned that you can't configure your way out of impedance mismatches. You can certainly make your life easier, but only at the cost of also making it harder. You essentially need language-level support for dealing with data at your boundaries. Even with Java's strong reflection (Go's reflection is horrible) capabilities and generics (and C# extending that with lambdas and expression trees), there's still tremendous friction and boilerplate configuration and code. Consider Dependency Injection, an ilk. In most (all?) dynamic languages, DI is a language concern, as opposed to a library concern. By having support for it within the language, the problems DI frameworks attempted to solve in Java (or C#) via messy configurations, legalese-like unit tests and quantities of code dedicated to nothing but the infrastructure of the codebase, simply vanishes. Now people are going to Go and discovering the awesomeness of interfaces. " -- [119], [120]
- "easy concurrency, static builds, fast compiles, static, fast-ish, decent batteries included" -- [121]
- "...it won’t always keep you safe from data races as Erlang will, but Erlang keeps you safe by copying your data over and over, making it significantly slower..." -- https://texlution.com/post/why-go-is-doomed-to-succeed/
- "Sometimes you’ll find the code simple and easy to follow, other times you’ll find layer upon layer to decipher. Go projects tend to overwhelmingly fall on the easy side....in general Go won’t easily let your project become frankensteinian with a thousand ways of doing the same thing." -- [122]
- "There are only a handful of languages which are truly doing concurrency these days. One of them is Go with its goroutines and channels, and this is a large part of its appeal. A few other aspects which have made it a popular alternative to existing languages and ecosystems include raw speed, simple yet dynamic feeling typing, the ability to compile down to a binary, and finally, simplicity....That being said, have you tried writing a web app in Go? You can do it, but it isn't exactly entertaining. All those nice form-handling libraries you are used to in Python and Ruby? Yeah, they aren't nearly as good. You can try writing some validation functions for different form inputs, but you'll probably run into limitations with the type system and find there are certain things you cannot express in the same way you could with the languages you came from. Database handling gets more verbose, models get very ugly with tags for JSON, databases, and whatever else." -- [123]
- "Go doesn't really need interop with C for many systems programming tasks because of its stdlib support for the full POSIX and associated kernel-specific interfaces, without depending on a libc at that. Along with other more generic interfaces. Then the fact that it's practically designed to be used on top of a Unix userland, much like C. Contrast to Erlang where you live in BEAM." -- https://news.ycombinator.com/item?id=9762123
- "Golang implements a custom scheduler in userspace too, and this is why its FFI is not as fast or as well integrated with the OS as the calling conventions of C or C++ are. Heck, Golang's FFI is even less straightforward than that of Java, since the JVM is typically 1:1, not M:N. Once you've gone M:N you're effectively in your own little world. There's a reason Golang's runtime is compiled with the Golang toolchan, and why it invokes syscalls directly as opposed to going through libc. " -- [124] (M:N: "Any situation where you have M userspace jobs running on N system threads," [125])
- "for the problem space the author mentions here, web services, Elixir is clearly superior." -- [126]
- comparing error handling in Go to Haskell's either monad
- "...the error handling turns out to be very solid, precisely because almost every other error handling paradigm strongly encourages you to just lump all the errors together and not think about them individually." -- [127]
- Q: As someone who uses python, what are some reasons for me to switch to Go? A: " The language itself is far simpler (fewer built-in basic complex data types, composition over inheritance, no overloading, no slots) but still batteries-included w.r.t. the standard library. You'll likely get a performance improvement unless you are writing some really zany code. Static types and the compiler will actually make your life much easier when dealing with larger code bases (no more wondering what type a parameter is, if it compiles there is a much better chance that it will just work, fewer worries about unhandled runtime conditions). Less magic (no decorators or deep class hierarchies) which means its easier to trace control flow in someone else's code. Verbose, but still very clean error handling. IMHO, the import system is pretty broken and awful, but importing someone else's packages straight off of github is still much better than pypi / wheelhouses. Compiled static binaries by default means that building somewhere and then shipping a binary is quite easy." -- [128]
- http://jeremymikkola.com/posts/2015_08_28_warming_up_to_go.html
- "Long-time Ruby developer here, relunctant Go developer.... I want performance, concurrency, efficient memory usage, and static typing. Ruby is lacking in all these areas. Go is mildly better, but is regressive in many other respects. For me, Go is a step back in terms of productivity, but a step forward in creating stable, fault-tolerant code. It's a compromise. Go feels like a stopgap solution until something better comes along. For example, I've learned to design the internals of my code around a functional style that makes code composable and the data flow simple. Go doesn't support that style, not really. Go promotes simplicity, but a lot of stuff that should be simple in Go isn't "simple". Like mapping and reducing an array." -- [129]
- vs functional languages: "I now use Go for most of my professional projects. It is just simpler than the languages you listed. The learning curve is smaller, and, I am typically working on projects with other programmers, most who have never used a functional language and many are openly hostile to that paradigm. Also, most of the projects I work on don't need the more power features of functional languages. If the project's complexity is high, for instance, a derivative pricing library, or if I am working with a small team with previous experience with functional languages, or doing a personal project, then I would consider using a different tool."-- [130]
- "For me, I like Go's slim profile. Native compilation, aggressive allocation, low memory usage, static compilation. JVM suffers from slow startup times and tends to eat a lot of RAM, even when the app doesn't technically need it around. It has an object model that spawns a bazillion tiny objects, and much of the JVM's GC design exists to cancel out those tiny object allocations. JVM has many upsides (the portability and pluggable nature of JAR files is a huge one), but I think a lot of people are attracted to Go's bare-bones approach. Closer to the metal, a "better C", smaller overhead." [131]
- " The reason I love Go is that every time I pull it out, I write a small amount of it and it runs beautifully. For example my company has a critical micro-service implemented in ~300 lines of Go, it's been running for six months now without a single hiccup, highly performant, very sexy. The reason I will almost never use Go for web apps is because interaction with databases is limited (almost entirely) to raw queries. Maybe I'm spoiled by the likes of Active Record, Sequelize, Mini-mongo, Sql-alchemy, etc, but it's a huge drop in efficiency to spin my own SQL." [132]
- https://news.ycombinator.com/reply?id=9163497&goto=item%3Fid%3D9161366
- https://talks.golang.org/2016/applicative.slide
- https://news.ycombinator.com/item?id=11855775
- "All of the server backends at my company are written in Go. This was a result of me writing a couple servers in Python a few years back, ending up with lots of problems related to hanging connections, timeouts, etc. I tried a couple different server libraries on Python but they all seemed to struggle with even tiny loads. Not sure what was up with that, but ultimately I gave Go a swing, having heard that it was good for server applications, and I haven't looked back. It has been bullet proof from day one and I am overall happy with the development experience. That was the good. The bad? Garbage collection, dependency management, and lack of first-tier support in various libraries. Garbage collection makes the otherwise lightweight and speedy language a memory hog under heavy loads. Not too bad, but I have to kick the memory up on my servers. Dependency management is a nightmare; honestly the worst part about it. The lack of first-tier support in various libraries is a close second. AWS's API libraries had relentless, undocumented breaking changes when we were using them, all on the master branch of their one repo (breaking Golang's guidelines for dependencies). Google itself doesn't actually have any real API libraries for their cloud services. They autogenerate all API libraries for golang, which means they're not idiomatic, are convoluted to use, and the documentation is a jungle."
- "...coming from Java I appreciated one particularly awesome thing about it, I could compile a single binary, deploy and run it quickly and easily. Honestly, I hadn’t realized how much I missed static binaries from C until I wrote a program in Go. There was no classpath, no monkeying with the default memory settings, no changing the default garbage collector. It was a nice language, but I had some problems. I missed generics, I missed typed and checked exceptions, and I couldn’t help but feel like I was writing C but with a Garbage Collector (ok, easier than C and it has memory safety, big bonuses)." but ultimately this commentator prefers Rust; after learning Rust "I had been shown the light, and there is no going back once you have been to the promised land." -- [133]
- "No map, no flatMap, no fold, no generics, no inheritance… Do we miss them? Perhaps we did, for about two weeks." -- [134]
- https://movio.co/blog/migrate-Scala-to-Go/
- "It took some of us six months including some after hours MOOCs, to be able to get relatively comfortable with Scala. In contrast, we picked up ‘Go’ in two weeks. " -- [135]
- "For the first time ever, we actually read the language spec when we’re unsure of how something works....Part of my frustration with Scala (and Java) was the feeling that I was never able to get the full context on a given problem domain, due to its complexity. " -- [136]
- http://jimplush.com/talk/2015/12/19/moving-a-team-from-scala-to-golang/
- "Do I miss immutable types and some of the great features of Scala? Sure do, but I think the maintainability side of the story is too great to overlook with Go. We’ve seen faster ship times, better stability and better test coverage being written." -- [137]
- "Channels and goroutines have made our job so much easier. It's not just the fact that channels and goroutines are cheaper in terms of resources, compared to threadpool-based Futures and Promises, resources being memory and CPU. They are also easier to reason about when coding." -- [138]
- "I'm 9 months in and still missing map(), flatMap(), let alone more advanced FP. I guess this is a matter of personal preference, but I definitely consider: users.filter(_.active).map(_.email) to be easier to read (and not a pain in the ass to type) than: emails := make([]string, 0) for _, u := range users { if u.active { emails = append(emails, u.email) } } return email " [139]
- "Tail recursion in a language with function head pattern matching (ML family, Erlang) is so much easier to read than any complicated for loop." [140]
- https://blog.codeship.com/comparing-elixir-go/
- "I'm concurrently learning Go and Node, which might not be a fair comparison but I'm going to make the comparison anyway. Node feels like a flesh wound with delicately layered bandaids. The ecosystem is a mess, you have to jerry rig basic packages to work together, the syntax and semantics of closures and context is ridiculous, and despite that callback mania is like this cool-aid that's supposed to feel great once you drink it (but hasn't yet for me). Go just...works. " [141]
- "I have to underline this aspect as npm killed half an hour of my work yesterday, when I redownload some package in a sibling directory (for diff purposes). Never had this experience with go. " [142]
- "Go has also eliminated the ternary operator, and, for reasons that appear to be political, does not have integer Min and integer Max functions. From what I can gather on the mailing list threads, the language designers are against polymorphism, as well as adding letters to function names, so unlike the standard C library which operates on float, double, and long double, as well as int and long where appropriate (e.g. absolute value), the Go standard math library operates only on float64. Since there’s no implicit casting to floats, this is rather annoying if you’re using integers, such as when you are counting things. It also makes Go somewhat less useful for heavy number-crunching where you might want single- or extended-precision versions of floating-point functions." [143]
- "One of Go’s major selling points is its concurrency support. I have not yet played with its concurrency features, cutely called goroutines. My impression from the description is that while goroutines are an advancement over vanilla C and C++, Go lacks a good story for handling programmer errors in a concurrent environment. Normal errors are bubbled up as values, but if there’s a programmer error (e.g., index out of range), the program panics and shuts down. For single-threaded programs, this is a reasonable strategy, but it doesn’t play very well with Go’s concurrency model. If a goroutine panics, either you take down the whole program, or you recover — but then your shared memory may be left in an inconsistent state. That is, Go assumes programmers will not make any mistakes in the recovery process — which is not a very good assumption, since it was programmer error that brought about the panic in the first place. As far as I know, the only language that really gets this right is Erlang, which is designed around shared-nothing processes, and thus programmer errors are properly contained inside the processes where they occur." [144]
- "Go is garbage-collected. I personally think Swift/Objective-C-style Automatic Reference Counting would have been a better choice for a statically typed systems language, since it gives you the brevity benefits without the GC pauses. I’m sure this has been argued to death elsewhere, so I’ll save my GC rant for a very boring dinner party." [145]
- "Reasons to Use Go" -- [146]
- "Performance"
- "Not Getting Too Creative"
- ".Concurrency & Channels"
- "Fast Compile Time"
- "The Ability to Build a Team...Go is a very simple and easy to learn language. It provides the basic features you need and nothing else. The new concepts it introduces are the “defer” statement and built-in management of concurrency with “go routines” and channels....Any Python, Elixir, C++, Scala or Java dev that joins a team can be effective at Go within a month because of its simplicity. We’ve found it easier to build a team of Go developers compared to many other languages"
- "Strong Ecosystem"
- "Gofmt, Enforced Code Formatting"
- "gRPC and Protocol Buffers"
- "Disadvantages of Using Golang" -- [147]
- "Lack of Frameworks. Go doesn’t have a single dominant framework like Rails for Ruby, Django for Python or Laravel for PHP...if someone wants to build a simple CRUD API they will have a much easier time with Django/DJRF, Rails Laravel or Phoenix"
- "Error Handling. Go handles errors by simply returning an error from a function and expecting your calling code to handle the error (or to return it up the calling stack). While this approach works, it’s easy to lose scope of what went wrong to ensure you can provide a meaningful error to your users. The errors package solves this problem by allowing you to add context and a stack trace to your errors. Another issue is that it’s easy to forget to handle an error by accident. Static analysis tools like errcheck and megacheck are handy to avoid making these mistakes. While these workarounds work well it doesn’t feel quite right. You’d expect proper error handling to be supported by the language."
- "Package Management. Go’s package management is by no means perfect. By default, it doesn’t have a way to specify a specific version of a dependency and there’s no way to create reproducible builds. Python, Node and Ruby all have better systems for package management. However, with the right tools, Go’s package management works quite well. You can use Dep to manage your dependencies to allow specifying and pinning versions. Apart from that, we’ve contributed an open-source tool called VirtualGo? which makes it easier to work on multiple projects written in Go."
- "Developing the Python version of the ranking code took roughly 3 days. That includes writing the code, unit tests and documentation. Next, we’ve spent approximately 2 weeks optimizing the code. In contrast, developing the Go version of this code took roughly 4 days. The performance didn’t require any further optimization...As an added benefit, the Go code performed roughly 40 times faster than our highly-optimized Python code." -- [148]
- "Go does have brilliant simplicity! It's the simplicity of Pascal I used in middle school, and of Modula-2 I used on my freshman year, with basically the same syntax. I'm glad Go revived a number of good ideas from Pascal / Modula / Oberon. For writing toy programs to relax (can relate), I personally prefer Python, or maybe a Scheme. While also being simple at the core concepts, they have much more expressive power, an easier way to combine simple things into complex things. They pay for that by higher resource consumption, of course." [149]
- "Sadly it feels more like Oberon-07 than Modula-2, feature wise. " [150]
- "Multithreaded Python is a complete minefield for race conditions. Sure, you might not segfault, but you have to think if you use fork() or spawn() depending on the OS, you have to install libraries to detect races, you have to write special tests. With Go all of that comes out of the box and it makes it _easy_." [151]
- "I worked on a sizeable C++ codebase, and it had a home-grown, buggy thread-pooling & task cancellation (similar to Go's context.Context) engine. Go's builtin goroutines were a breeze afterwards. Also, I debugged a race condition in this codebase. Once, and it took me a few weeks full steam digging, thinking and mental construction. I didn't even know I have one at the beginning, just had this nagging feeling. (Valgrind would take the app to a crawl, and it was speed-critical.) In Go, I can just run tests with -race flag, and it finds me truckload races at a blink, pointing to the exact place where they happened, with a stack trace sprinkled on top." [152]
- "Go is, from a theoretical perspective, a very bad language, even when compared to an old language like C++ or Ada. It lacks a lot of compile time checks and compile time logic. Its lack of destruction logic leads to a lot of boilerplate and runtime errors. Its interfaces aren’t very expressive. It has first-class-citizen data structures (slices and maps) that can’t be replicated as a library using the language itself. It forces mutability upon the user in many situations where mutability isn’t needed. It comes with a pseudo dependency manager that lacks independent versioning for separate projects. It’s very slow compared to most other popular system programming language, namely C, C++, Ada, Fortran and Rust....In spite of that, as anyone who used Go can tell you, it’s a very good language. I count myself among those people, if I were to be stranded on an island with only 3 programming languages, I’d want Go to be one of them...Go had a few central features, which are so amazing, they out-weighted all the bad. Go utilities allow you to easily download and use packages. Static compilation makes porting code between various environments and setting up a development environment really easy. Native asynchronous I/O mechanisms allow you to easily write high performance networking code. Built-in channels allow for easy to implement and relatively safe data transfer between [g
The standard library and package ecosystem contains most libraries a developer could ask for.
It’s “fast enough” for almost all usecases. Seemingly hitting a sweet spot between easy-to-use single threaded language like Python and Node and blow-your-brains-out ancient but fast behemoths like C++ and C.Or, to put it plainly. Go is a language designed for the age of open source libraries, large-scale parallelism and networking. ... All the remaining issues with Go stem from three design choices: It’s garbage collected, rather than having compile time defined lifetimes for all its resources. This harms performance, removes useful concepts (like move semantics and destructors) and makes compile-time error checking less powerful. It lacks immutability for all but a few (native) types. It lacks generics " -- [153]
- https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
- "...easy to learn, it is fast to compile and run, it has goroutines and simple concurrency using CSP. It has multicore support and you can compile a static binary that will run on a plain Linux system with fast startup time. It has type inference for variable declarations, but not function definitions. It has interfaces and it looks like it has a professional community with good foundations." [154]
- "lack of generics (that should be added in the next major version) feels weird to me...the language itself isn’t pretty...It’s just not fun, it’s simple, boring and good. I’m sure that eventually, I’ll end up using it for a production system and I’ll love it" [155]
- "Go uses capitalization to determine identifier visibility. Those that start with a lower case letter are package-private, and those that start with a capital are package-public. The purpose presumably is to cut down on the public and private keywords, but capitalization was already used to mean other things: Classes are capitalized and constants are entirely in upper case. It’s a recurring source of discomfort to me to name global constants entirely in lower case." "Things get worse if you want to have a private struct, since it must be in lower case. For example you might have a user struct. What do you name the variable? Normally I’d call it user, but not only does that look confusing, but can cause parse errors when the compiler confuses them" "There are practical considerations, too. Frequently I start out with a private field or struct name and later decide to make it public, forcing me to fix all usage of the identifier" "Capitalization also restricts visiblity to two levels (package and completely public). I frequently want file-private identifiers for functions and constants, but there isn’t even a nice way for Go to introduce such a thing now."[156]
- "Structs do not explicitly declare which interfaces they implement. This is done implicitly by matching the method signatures." [157]
- "Go doesn’t have exceptions. It uses multiple return values to return errors. It’s far too easy to forget to check errors" "Additionally, the idiom of returning either a value or an error: user, err := getUserById(userId) invites bugs because there’s nothing to enforce the fact that exactly one of user and err contain valid values. With exceptions the user variable is never assigned-to (so reading from it will generate a warning), and with algebraic sum types (unions) the compiler will ensure that only the correct one can be accessed." [158]
- "There’s far too much magical behavior. For example, if I name my source file i_love_linux.go, it won’t get compiled on my Mac. If I accidentally name a function init() it’ll get run automatically. This is all part of the “convention over configuration” movement." [159]
- "Partly because of the capitalization problem, it’s easy to end up with several identically-named identifiers. It’s actually quite easy to have a package, struct, and variable all called item. In Java the package would be fully-qualified and the class would be capitalized. Sometimes I find it hard to read Go because I can’t always tell at a glance what scope an identifier belongs to." [160]
- "It’s difficult to generate Go code automatically. The compiler is strict about warnings, meaning that unused imports and variables cause the build to fail. But when generating a large file it may not be initially clear which packages need importing. Furthermore you may have two packages whose names clash, and it’s not easy to resolve this automatically because you can’t even know the imported symbol if you only know the package name. (The package imported as github.com/lkesteloot/foo is permitted to actually be package bar, and some open source libraries do this.) Even if you could figure that out, the generating program would be forced to alias the imports to avoid the conflict. In Java all these problems are solved by importing nothing and always fully-qualifying all class references, something not permitted in Go." [161]
- "There’s no ternary (?:) operator. Every C-like language has had this, and I miss it every day that I program in Go."
- "The sort.Interface approach is clumsy. If you have 10 different structs that you want to sort (in arrays), you have to write 30 functions, and 20 of those are trivially similar (length and swap). Also, they’re hard to compose: You can maybe delegate to another Less(), but you have to trust that its Len() and Swap() are compatible. And finally it just looks weird, because the cast looks like a function call: sort.Sort(sort.Reverse(UsersByLastSignedInAt?(users))) The tried and true approach of providing a compare method works great and has none of these drawbacks." [162]
- "Import versioning and vendoring is terrible. This is well-covered ground elsewhere, but frankly it’s 2016 and it’s not acceptable to release a new language without a solution for this. And not only does Go not have a solution, but its import system is actively hostile to vendoring." [163]
- "No generics. Also well covered elsewhere, but again I can’t really use a language that doesn’t let me implement a generic Stack class" [164]
- "Okay a bonus reason! This is very minor, but points to a failure on the part of the designers to understand how programmers work. The append() function extends an array, returning the new array: users = append(users, newUser) The problem is that the following code will nearly always work: append(users, newUser) The append() function modifies the array in-place when it can, and only returns a different array if it has no place left....Update: They later modified the compiler to generate an error if the append() function’s result is not assigned to anything." [165]
- "Here’s a summary of my recommendations for Go usage: If your program is small and can mostly be described by what it does, and if it doesn’t interact much with data outside itself (databases, the web), then Go is fine. If it’s large, if it has non-trivial data structures (even something simple like a tree), or if it will be dealing with a lot of data from the outside, then the type system will fight you for no benefit and you’re better off using a different static language (where the type system helps) or a dynamic language (where it doesn’t get in your way)." [166]
- "They throw every convention out the window and act like they have a true greenfield audience. They don't even allow [variable, Class, CONSTANT] convention, they don't follow the convention of letting devs choose their own project location, and a lot more." [167]
- vs Ruby : "Ruby is faster to write than Go, and the REPL is a big part of that." [168]
- "Golang is ok, the runtime is pretty weak compared to modern JVMs and tight C++ is faster, Clojure and Haskell are denser. It’s ok but I wouldn’t stop checking out other stuff." [169]
- "Go as a language is just fine, but Go as a framework for building software is just fantastic. The tooling is comprehensive and easy to use, testing is built in, and the happy path is almost always highlighted for you. No need to figure out which documentation generator, style guideline/formatter, test framework, build system, package manager, etc to use and these are easy to use (with the notable exception of package management, which has been rocky but is improving)." [170]
- "The Go standard library’s consistency makes it easier ((compared to Python)) to predict how any given module will function, and the source code is extremely well documented....there’s enough in the standard library that you don’t need a web framework like Django. It’s possible to build a real, usable web service in Go without any dependencies. Python’s claim is that it’s batteries-included, but Go does it better, in my opinion." [171]
- vs Haskell: "goroutines is way easier to use than forkIO & MVar and others. Way easier to write a performant code." [172]
- vs Haskell and OCaml: "Go had the best bang for the buck best performance per hour spent out of the box. If you have a team of engineers of varying level of expertise by using Go you could expect that a) the result would be good-to-great and b) anyone on the team would be able to maintain it...I found tooling being very good, easy to use, fast...Documentation for the language, the standard library, tools, third-party packages is extremely good...Go was easy to set up, easy to use...it is a good language that anyone can start hacking without having to read a Category Theory book...There is no a standard way (at time of this writing at least) to lock dependencies. It is not a problem for small one-off projects, but I can’t image how people are able to develop/maintain larger code bases without falling into cabal hell. Not dissimilar to Haskell, NixOS? could be an answer to that problem...Another observation, and it does stand out, especially after Haskell experience, is that language-wise it feels like a “can I have a C (or AWK) but without this and that annoyances please?“. Which is not necessarily a bad thing. I’ve got mixed feelings about it and will probably write a post or two to get into more details." [173]
- "Go has to be compared with alternatives. Rust? Awesome language, but slightly different niche with a far higher learning curve. Java? Please. C or C++? They are the opposite of simple (C is a larger language, but with far more baggage, don't get me started on C++). I can't include Python and ilk as their purpose is completely different, although for things like backends both could be used." -- [174]
- "The best way to think about Go is to think about all the things that it doesn't have. Go doesn’t have a virtual machine or an LLVM-based compiler. Go doesn’t have exceptions. Go doesn’t have user-defined implementation inheritance. Go doesn’t have overloading for functions, methods, or operators. Go doesn’t have immutables. Go doesn’t have enumerations. Go doesn’t have generics. And Go hasn’t added any major features since Go 1 was released in 2012. " -- https://www.capitalone.com/tech/software-engineering/go-is-boring/
- https://www.evanmiller.org/four-days-of-go.html
- "I used to write backend services in Node.js but have preferred Go recently because it offers a better operations / deployment experience." -- [175]
- "I've settled into sort of a hierarchy. If I really want to hack out a small prototype quickly, I use NodeJS?. Not having types helps me change things around quickly. If I already have a pretty good idea of my data model but still want to develop quickly, I'll use Go. If it's something 1.0.0+ and I want to make it as reliable as possible, I'd use Rust. My problem seems to be few of my projects ever get to that stage, so I'm mostly writing Go these days... " [176]
- "Given that Go is essentially a safer, smarter C, it boggles my mind to see it being used for applications where getting the business logic right is much, much more important than performance or complicated bit-twiddling." [177]
- "I echo this. I've been exposed to some Go at work recently, and Rust on my free time. I've been learning both over last few months. Go is like C. More like C than any other mainstream language I use. IMO this is bad. C is very verbose and hard to read. Rust is more like C++. Close to hardware but many high level language features. I could rant a lot about what I don't like about Go, but it boils down to this. No generics, bad package system, bad error handling, bad code generation, bad GC, limiting syntax encourages copy paste and bad abstractions, defying common conventions to be cute (like capitalization in variables), mediocre standard library. I love Rust more as I get better. I already hate Go for some of the above reasons, I wouldn't even use it if it wasn't for work. Google hypes Go like crazy, I'm convinced that's the only reason it's popular. I think there's many better options, even Java, that don't have many of the shortcomings listed above " [178]
- "I have written a _lot_ of business logic in go, and I can say that imo it is a superb language for doing so. Vastly better than C, and I’m coming around to it being better even than my beloved python." [179]
- "> Go has the advantage of mediocrity. This is true in many aspects. Go shares many common feelings with other popular languages. However, there are also something in Go those are not mediocrity. Being an almost static language but also as flexible as (sometimes even more flexible than) many dynamic languages is one of those not-mediocrity things in Go." [180]
- https://blog.logrocket.com/when-to-use-rust-and-when-to-use-golang/
- "> I'd be interested to hear your thoughts on why Go might be better than Python for business logic. Mainly that it’s just so aggressively boring (which I love). No exceptions means you have to deal with your errors all the time, and it’s just generally very easy, when dropped into a random spot in the code, to figure out what’s happening. Very little magic." -- [181]
- "The standard library is not so great. If you know me in person, it might seem a little odd that I specifically comment on this. Normally I am all for languages having a small, really well-written, stdlib and everything else provided through libraries. I am picking on Go here a bit because the standard library seems to get a lot of praise, but I was unimpressed. Parts of it are good, a lot of it is mediocre, and some of it is downright bad (like the go/ast package documentation). It seems a lot of Go’s use is in webdev, so perhaps those bits of the stdlib (which I haven’t touched at all) are consistently good." -- [182]
- vs OCaml: "An interesting language to contrast with Go is OCaml. Despite its wonky syntax, OCaml is actually quite nice. It has many of the same features as Go but, with the exception of concurrency stuff, does all of them much better. It has structural subtyping, no nulls, variants, efficient generics, tuples, pattern matching, full type inference... And it predates Go by 13 years." -- Tikhon Jelvis
- "My main overall impression of Go is that its design feels arbitrary: its features are just whatever the designers felt like adding, however they felt like making them. It lacks the underlying simplicity, elegance and holistic design that I really look for in a programming language." -- Tikhon Jelvis
- "Error Handling. The first place that stands out as ugly and arbitrary is error handling. They decided to forgo exceptions in favor of passing errors through return values. And that's a great decision! It simplifies the language and makes error handling less of a special case, unlike exceptions. So, if you're going to pass errors through a function's return value, there's a very logical way to do it: a function can return either a correct result or an error. After all, if there's an error, no correct result can make sense. And if there was no error, well, there was no error. So what does Go do? It returns both. If you had an error, the real result is garbage; if you don't have an error, it's nil. And, instead of returning both as a tuple, it does it in a magical, built-in way that cannot be reused for anything else. " -- Tikhon Jelvis
- "Zero Values and Nil Go's error handling fracas also forced them to add two more ugly, arbitrary warts to the language: nil and, for a Java-esque set of primitive types, zero values. Nil is just their incarnation of Tony Hoare's Billion Dollar Mistake. Because I guess we still don't know any better. Having nil everywhere is adding an extra vector for mistakes, bugs and confusion to every part of your program for very little gain. It's ugly because nil often doesn't make any sense but you still always have to worry about the possibility! But if having nil everywhere is bad, having nil almost everywhere is even worse. It makes the language inconsistent. You have to remember that a handful of built-in "primitive" types behaves differently from everything else. More special cases! It also means that any types the programmer creates are going to be second-class citizens compared to these built-in types....given Go's error handling—and uninitialized memory, which is ugly all on its own—we need to have some default garbage value for primitive types. So Go just arbitrarily defines "zero" values for these built-in primitive types. The problem is that there is no single natural default value for things like integers, so Go's choice of 0 is relatively arbitrary—why not 1? Zero values are another example of Go forcing any type you define to be a second-class citizen: only built-in types can have them. The fact that strings are primitive values and cannot be nil is also odd. If everything else, like arrays/slices, have nils, why not strings? I could see ints/floats sort of making sense because they're probably unboxed, but strings aren't...." -- Tikhon Jelvis
- "Type Inference. Go doesn't have type inference. And why? I really don't know. It has := which is better than nothing, but I don't see why it couldn't have full type inference. OCaml has similar type system features—and more—and has no problems at all with full inference." -- Tikhon Jelvis
- "Generics. Go doesn't have generics. This, by itself, makes the language pretty ugly. It's not just a little thing they left out, it's a major problem. For one, it means you can't really create your own data types. Which, you know, is kind of important. This is the biggest contributor to the pervasive second-class citizen feel of Go: you can never write something like the built in map type! What if you want a tree-based map? Or a set? Or one of the many trees that are so useful? Queues? You get the idea... Maps magically have generics, but in a weird way that is not reusable at all. Yet another special case. The lack of generics also forces you to cast to interface{} all over the place. This is ugly by itself but also throws away a lot of the advantages of static typing by explicitly turning otherwise static errors into runtime errors. So you get more verbose code that's also less safe. Wow." -- Tikhon Jelvis
- "Because you have to check error values, if you want to perform a sequence of possibly-erroring computations, where the successful result of one feeds into the next, there is a lot of typing. In Haskell, you’d just use the Either monad." -- [183]
- "If you want to sort a slice, because there are no generics, you need to wrap the slice in another type and implement three methods on that type. So that’s four lines of code to sort a slice of uints, four lines to sort a slice of uint8s, four lines to sort a slice of uint16s, and so on. In Haskell, you’d just use the generic sort." -- [184]
- "Zero values are almost never what you want...I would much rather: Drop the syntax for declaring a variable without giving it a value. Make it an error to not initialise a struct field. " -- [185]
- vs Rust: https://bitfieldconsulting.com/golang/rust-vs-go
- vs C++, Rust, Zig: https://zserge.com/posts/better-c-benchmark/
- vs D, Nim, Crystal: "D is nice. I could see myself picking it instead of go. It's much more expressive than go and it's mature enough. Cross compilation story isn't as great though. Haven't tried asBetterC. I was under the impression this corner of D isn't as mature as D proper...I'm partial to Nim and Crystal (and V). They aren't as appealing to me personally because they feel complex compared to C and go (and zig). If the appeal of these languages is "a better golang", I think D has beat them to the punch. Also, again, cross compilation tends to be a bit of a deal breaker for me." [186]
- vs Elixir: "...after almost 4 years of working with Elixir and watching Go from arms’ length, I literally see NONE of the criticisms regularly leveled at Go" [187]
- vs Python: https://yosefk.com/blog/things-from-python-id-miss-in-go.html
- vs Rust: "Imo, Go does performant concurrency right. Rust would be smart to adopt what Go offers." -- [188]
- vs Rust: "...Rust is a better option when you also need one or more of the following: a) tight control over allocations b) deterministic performance c) strong data race protection d) zero overhead C interop (e.g., processing audio using ffmpeg) " -- [189]
- vs Rust: https://news.ycombinator.com/item?id=31435739
- "Go is a systems programming language? " [190]
- vs C: "Depends if one considers compilers, linkers, networking stacks, kernel emulation, unikernels, userspace drivers, databases, GPGPU debugging systems programming or not. I personally consider better use Go than C for such purposes, even if they aren't "systems programming". -- [191]
- "Yes, as it's used for that a lot. Eg many databases (CockroachDB?, Prometheus, InfluxDB?, dgraph etc), gVisor, Kubernetes, Fuchsia, etcd, and so on. And also in the origin story it was aiming to compete with C++ for many use cases IIRC." [192]
- "That's tricky to answer, because it depends a lot on what you count as "system software". If you mean literally "the operating system", then arguably not. But if you include middleware, databases and other "infrastructure" stuff, then arguably yes." [193]
- " Yes it is, but not a a good low level systems language mainly due to garbage collection and runtime requirements. It still is used for writing keyword here systems. " [194]
- " No, due to its runtime. You can write a database with it but that makes it an application language, not system. Otherwise you could call every language a "system language" and the distinction would lose all meaning. " -- https://news.ycombinator.com/item?id=31446238
- "I can’t stand programming in Go. Everything feels painful for no reason. Error handling, scoping rules, hostile CLIs, testing, tooling, etc. My greatest hope for Zig is that I can use it to replace Go, not just to replace C." -- [195]
- "Zig has some nice quality of life improvements over Go (try, sane defer, unions, enums, optionals, …), which can be enough for me to want to switch, but I also had an interest in learning lower level programming. If you really don’t want to learn anything about that, I don’t think Zig can really be a comfortable replacement, as it doesn’t have 100% fool-proof guard rails to protect you from lower level programming issues." -- [196]
- "How does Zig’s defer work differently than Go’s?" -- [197]
- "In Go, deferred function calls inside loops will execute at the end of the function rather than the end of the scope."
- "Oh I didn’t realize Zig had block scoped defer. I assumed they were like Go. Awesome! Yeah that’s a huge pain with Go."
- vs Rust: on Go: "if you value utmost simplicity more than sum types, that is a splendid choice. I think Go is the single language which is in the same class as Rust when it comes to “quality of implementation”, and, arguably, it’s even better than Rust in this respect. If only it had enums and null-checking..."
- vs Python: "To be honest, I’d still probably reach for Python first for throwaway scripts, because of its terser syntax, list (and other) comprehensions, and exception handling by default. However, for anything more than a throwaway script, I’d quickly move to Go. Its standard library is better-designed, its io.Reader and io.Writer interfaces are excellent, and its lightweight static typing helps catch bugs without getting in the way." -- https://benhoyt.com/writings/gogit/
- vs Rust: https://www.shuttle.rs/blog/2023/09/27/rust-vs-go-comparison
Gotchas
- Go puts compiler directives inside comments, rather than having a separate syntax for them [198] . As of this writng, Go will even run directives from 'comments' within a multiline string literal [199]
- http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/index.html
- "slicing a unicode string slices at byte boundaries, not characters" [200]
- " Go's "channels" do a lot more sharing than the Go sales materials claim. If you send a slice across a channel, you're now sharing unlocked data across a concurrency boundary." -- [201]
- the possiblity of "the semantics around nil and closed channels resulting in deadlocks" -- [202]
- the possiblity of "failing to recover from an unexpected panic." -- [203]
- "I'm also up in arms about ":=" assignment behaviour. I've run into several bugs caused by shadowing inside blocks. You have to be careful when refactoring so as not to cause shadowing issues; really, editors should have their syntax highlighting set to highlight ":=" in blinking bright yellow or something. It's so hard to miss. It's also inconsistent with how you (or, at least, I) want it to behave. It will shadow existing variables by default if there is at least one new variable on the left-hand side, but that's the least conservative behaviour, and feels contrary to Go's strictness — a language, after all, that considers unused imports to be a compilation error. Recently I've started avoiding ":=" in favour of vars, just to avoid falling into that trap by accident." -- [204]
- "Too many ways to declare a variable....shadowing rules, too subtle" [205] "The short declaration syntax can be used inside for loops and if statements whereas var cannot. On the other hand var can be used for global variables but short declaration cannot." [206]
- "I wish that in for x := range v { wg.Add(1) go func() { defer wg.Done() foo(x) }() } x would be scoped to the block, rather than to the whole for statement, so it would Just Work. This is the most common mistake I still regularly make in quick one-off programs." [207]
- "I would like the opportunity to rewrite the lower-level, earliest-written libraries to be more Go-like. There are a number of early decisions I regret. One easy one to explain is that os.Stdin and os.Stdout are concrete types, *os.File. It would be much nicer if they were io.Reader and io.Writer. Similarly, I would like the File type itself to be more abstract so it can be implemented by other packages transparently." -- [208]
- "...every 'type' declaration creates a new type incompatible with the type it derived from in function calls without explicit casting." [209]
- Using most things that are nil causes a panic. But sending to or receiving from a nil channel blocks forever.
- "The author points out that channel teardown is hard. He's right. Figuring out how to shut down your Go program cleanly can be difficult, especially since calling "close" on a closed channel causes a panic. You have to send an EOF on each channel so the receiver knows to stop. When you have a pair of channels going in opposite directions between two goroutines, and either end can potentially initiate shutdown, it gets messy." [210]
- "For concreteness, at least from what I've experienced, the "messiness" is that if you close one of these channels, you may have to "drain" the other channel lest you let the other side block. If the other side is only using the channel in a "select" block with other options you may not need to but if it ever does a "bare" send you need to wait for the other end to send its close." [211]
- http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/
- Although the scheduler is sometimes said to be preemptive, actually a process can only be interrupted when it does certain things. A tight busy loop won't be preempted. [212] [213][214]
- "Even worse, reliably testing for nil (`if (foo == nil)`) doesn't even save you because go's nils are typed. So `foo == nil` can actually return false even when foo is nil. Utter madness. "
- "var foo *Foo = nil; var bar Bar = foo; foo == nil # true; bar == nil # false"
- "Go code will look at least a little familiar to C programmers. It carries over C’s primitive types, as well as its semantics with regard to values and pointers. From the start I had a decent mental model of how things are passed to functions in Go, and under what circumstances the caller can expect data to change. Like C, Go shuns classes in favor of plain structs, but then lets you make code more object-oriented with interfaces, which I’ll discuss in a minute." [215]
- "Go is garbage-collected. I personally think Swift/Objective-C-style Automatic Reference Counting would have been a better choice for a statically typed systems language, since it gives you the brevity benefits without the GC pauses. I’m sure this has been argued to death elsewhere, so I’ll save my GC rant for a very boring dinner party." [216]
- "One of Go’s major selling points is its concurrency support. I have not yet played with its concurrency features, cutely called goroutines. My impression from the description is that while goroutines are an advancement over vanilla C and C++, Go lacks a good story for handling programmer errors in a concurrent environment. Normal errors are bubbled up as values, but if there’s a programmer error (e.g., index out of range), the program panics and shuts down. For single-threaded programs, this is a reasonable strategy, but it doesn’t play very well with Go’s concurrency model. If a goroutine panics, either you take down the whole program, or you recover — but then your shared memory may be left in an inconsistent state. That is, Go assumes programmers will not make any mistakes in the recovery process — which is not a very good assumption, since it was programmer error that brought about the panic in the first place. As far as I know, the only language that really gets this right is Erlang, which is designed around shared-nothing processes, and thus programmer errors are properly contained inside the processes where they occur." [217]
- "Despite my misgivings over the absence of Sloppy Go...on the whole I’ve been enjoying my initial experiences with the Go language." [218]
- "I was surprised at how idiot-proof it was to build things — you just type “go build” and almost instantly have a self-contained executable. This does make me wonder how things went so badly with make, makemaker, autoconf, aclocal, and the rest of the Texas Toolchain Massacre." [219]
- "I had previously read about Go’s refusal to compile programs with unused import statements...I prefer to try things out and get them working, then go back later and clean things up. Go basically forces you to have clean code all along, which is a bit like forcing a scientist to wipe down the workbench and rinse all the beakers between every experiment, or forcing a writer to run the spell checker after every cigarette. It sounds like good practice, but it comes with a cost, and it’s a decision that’s probably best left to the person it immediately affects, rather than to the tool designers." [220]
- "...you cannot specify which interface you're implementing in the code (beside from comments). I think that is so because Go hates circular imports. If you had to import a file to be able to use an interface, you'd soon run into compile errors due to circular dependencies." [221]
- "...take for example interface slices, you start by learning that you can assign any type to interface{}, so intuitively you'd think that you could assign any type slice to []interface{} but you find out soon enough that doesn't work. If you dig a bit it is completely understandable why it doesn't given how interfaces are laid out in memory, but from an intuitive standpoint you'd think that since you can do it in the scalar case you should be able to do it in the slice case too... This is a very minor nitpick, I do agree that compared to many other languages golang is very easy to mentally parse, at the expense of some expressivity at times." [222]
- the 'typed nil' problem: "Nil is the one case where you can seriously screw your self up in Go because it’s an untyped literal that gets typed on assignment. Interfaces are fat pointers and a “true” nil pointer is nil interface, nil implementation. If your function is going to return a known subtype and you store that nil in a temporary you now return a known interface and nil implementation. Then your == nil check on the interface pointer will fail. I royally screwed up a deploy once because my functions were returning nil error implementations that were != nil and triggered failure paths. " [223]. See [224] for context.
- https://kristoff.it/blog/why-go-and-not-rust/
- "I see junior programmers screw up Go catastrophically. Thinking green threads are magical, they forget the existence of mutexes and the resulting code has data races left and right. Languages like Haskell combine green threads with carefully controlled mutation (be it IORef or STM) to avoid this. Go doesn't, so you still need low-level knowledge like how to avoid deadlocks. As I've said before, Go gives you the veneer of an easy language when it's in fact full of traps for those who aren't experts. " [225]
- "Go needs synchronized and immutable collections, or at least to start trusting users to roll their own. Concurrent slice appends will overwrite each other unpredictably, and concurrent map writes and reads can panic." [226]
- " Arrays vs. slices and standard functions dealing with slices are full of weird behaviour, unnecessarily complex syntax and obscure features. Like, why are there even arrays at all? Which slice functions return a copy and which ones don't? Why is append() so braindamaged to make a copy sometimes? Why do I need make() as often as I do when mostly the language knows all make() would do? Go still has lots of footguns, even right at the trivial basics. " [227]
- " As someone who reads a lot more Go than I write, Go is awful to read. Reading Go means that I keep having to read idioms (as posted in the article) and parse them back into the actual intent, rather than just reading the intent directly. But I can't even trust that, since the nth rewrite of the idiom might have screwed it up subtly. Reading Go means that I can't easily navigate to the definition of a function, because it implicitly merges folders into a single namespace, so I have to try them one by one. Reading Go means that I usually don't have tooling that can jump to definition, because there are umpteen different dependency management tools, and Gopls only supports one of them. Reading Go means that even when I have tooling available, and it feels like working today, jump-to-definition becomes impossible as soon as interfaces are involved, because structural typing makes it impossible to know what is an intentional implementation of that interface. ... In Rust, if I see a call to `foo::bar::baz()`, I instantly know that `foo/bar.rs` or `foo/bar/mod.rs` (and only one will ever exist) contains either a `fn baz()` (in which case I'm done) or a `pub use spam::baz;` (in which case I can follow the same algorithm again). In Go, if I see a call to `import ("foo/bar"); bar.baz()`, all I know is that ONE of the files in the folder `foo/bar` contains a function `baz`, with no direction on which it is. " [228]
" simiones 8 hours ago [–]
Of course Go has hidden behaviors and overly complex syntax, like any programming language before or after.
For example:
xrefs := []*struct{field1 int}{}
for i := range longArrayName {
longArrayName[i].field = value
xrefs = append(xrefs, &longArrayName[i])
}
Perfectly fine code. Now after a simple refactor:
xrefs := []*struct{field1 int}{}
for _,x := range longArrayName {
x.field = value
xrefs = append(xrefs, &x)
}
Much better looking! But also completely wrong now, unfortunately. `defer` is also likely to cause similar problems with refactoring, given its scope-breaking function border.
And Go also has several ways of doing most things. Sure, C# has more, but as long as there is more than one the problems are similar.
And I'm sure in time Go will develop more ways of doing things, because the advantage of having a clean way to put your intent into code usually trumps the disadvantage of having to learn another abstraction. This is still part of Go's philosophy, even though Go's designers have valued it slightly less than others. If it weren't, they wouldn't have added Channels to the language, as they are trivial to implement using mutexes, which are strictly more powerful.
reply
feffe 7 hours ago [–]
I don't think this is a good example of proving the point that Go is difficult to read. Isn't it expected to internalize the semantics of basic language primitives when learning a new language, or does people just jump in guessing what different language constructs do? IMHO you learn this the first week when reading up on the language features.
range returns index and elements by value. The last example does what you asks of it, it's like complaining something is not returned by reference when it's not. Your mistake. Perhaps some linter could give warnings for it.
reply
pizza234 5 hours ago [–]
I think this is actually a very good example of the inverse relationship between logical complexity and language complexity.
A language that has implicit copy/move semantics is easier to write (since it's less constrained), and more difficult to read (since in order to understand the code, one needs to know the rules).
A language that has explicit copy/move semantics, is more difficult to write (since the rules will need to be adhered to), but easier to read (because the constraints are explicit).
Although I don't program in Golang, another example that pops into my mind is that slices may refer to old versions of an array. This makes working with arrays easier when writing (as in "typing"), but more difficult when reading (as in understanding/designing), because one needs to track an array references (slices). (correct me if I'm wrong on this).
In this perspective, I do think that a language that is simpler to write can be more difficult to read, and this is one (two) case where this principle applies.
(note that I don't imply with that one philosophy is inherently better than the other)
EDIT: added slices case.
reply
simiones 6 hours ago [–]
I don't think you can truly internalize the varying semantics of which operations return lvalues and which return rvalues. At least, I haven't yet been able to in ~2 years of Go programming, and it's a constant source of bugs when it comes up.
The lack of any kind of syntactic difference between vastly different semantic operations is at the very least a major impediment to readability. After all, lvalues vs rvalues are one of the biggest complexities of C's semantics, and they have been transplanted as-is into Go.
As a much more minor gripe, I'd also argue that the choice of making the iteration variable a copy of the array/slice value instead of a reference to it is the least useful choice. I expect it has been done because of the choice to make map access be an rvalue unlike array access which is an lvalue, which in turn would have given different semantics to slice iteration vs map iteration. Why they chose to have different semantics for map access vs array access, but to have the same semantics for map iteration vs array iteration is also a question I have no answer to.
reply
marcosdumay 26 minutes ago [–]
> and they have been transplanted as-is into Go
Hum... I don't think anything can return an lvalue in C.
Your first paragraph is not a huge concern when programming in C, declarations create lvalues, and that's it. I imagine you are thinking about C++, and yes, it's a constant source of problems there... So, Go made the concept much more complex than on the source.
reply
simiones 8 minutes ago [–]
I think Go has exactly C's semantics here. The following is valid syntax with the same semantics in both:
array[i] = 9
*pointer = 9
array[i].fieldName = 9
(*pointer).fieldName = 9
structValue.fieldName = 9
pointer->fieldName = 9 // Go equivalent: pointer.fieldName = 9
// you can also create a pointer to any of these lvalues
// in either language with &
Go has some additional syntax in map[key], but that behaves more strangely (it's an lvalue in that you can assign to it - map[key] = value - but you can't create a pointer to it - &(map[key]) is invalid syntax).
reply
Measter 3 hours ago [–]
What exactly is the difference here? Is the `x` variable being updated in the second sample so that the stored references all point to the same item?
reply
simiones 1 minute ago [–]
Yes, that code is equivalent to
xrefs := []*struct{field1 int}{}
var x struct{field1 int}
for i := range longArrayName {
x = longArrayName[i] //overwrite x with the copy
x.field = value //modify the copy in x
xrefs = append(xrefs, &x) //&x has the same value regardless of i
}
The desired refactoring would have been to this:
xrefs := []*struct{field1 int}{}
for i := range longArrayName {
x := &longArrayName[i] //this also declares x to be a *struct{field1 int}
x.field = value
xrefs = append(xrefs, x)
}" -- [229]
" Go has plenty of annoyances too though. Not having any dev vs prod build distinction is annoying. Giving maps a runtime penalty of random iteration in order just to punish devs who don't read docs is annoying. It's annoying to have crappy implementations of things like html templating in the stdlib which steal thunder from better 3rd party libs. Not being able to shadow vars is annoying. `val, err :=` vs `val, err =` is annoying when it swivels on whether `err` has been assigned yet or not, a footgun related to the inability to shadow vars. etc. etc. " [230]
" Here's an example of why Go's simplicity is complicated:
Say I want to take a uuid.UUID [1] and use it as my id type for some database structs.
At first, I just use naked UUIDs as the struct field types, but as my project grows, I find that it would be nice to give them all unique types to both avoid mixups and to make all my query functions clearer as to which id they are using.
type DogId uuid.UUID
type CatId uuid.UUID
I go to run my tests (thank goodness I have tests for my queries) and everything breaks! Postgres is complaining that I'm trying to use bytes as a UUID. What gives? When I remove the type definition and use naked UUIDs, it works fine!
The issue is Go encourages reflection for this use-case. The Scan() and Value() methods of a type tell the sql driver how to (de)serialize the type. uuid.UUID has those methods, but when I use a type definition around UUID, it loses those methods.
So the correct way to wrap a UUID to use in your DB is this:
type DogId struct { uuid.UUID }
type CatId struct { uuid.UUID }
Go promised me that I wouldn't have to deal with such weird specific knowledge of its semantics. But alas I always do.
[1] https://github.com/google/uuid
EDIT: This issue also affects encoding/json. You can see it in this playground for yourself! https://play.golang.org/p/erfcSIe-Z7b
EDIT: I wrongly used type aliases in the original example, but my issue is with type definitions (`type X Y` instead of `type X = Y`). So all you commenters saying that I did the wrong thing, have another look! " -- [231]
Best practices
- Throwing exceptions across module boundaries is discouraged (Go has an exception-like mechanism involving panic/defer/recover, although they call them 'panics' instead of 'exceptions'). Instead, Go encourages you to return an error code, in an effort to encourage program correctness by making error handling explicit (see http://golang.org/doc/faq#exceptions ).
- Name single-method interfaces with words ending in 'er'
'Go' is hard to search for on the web so the tag 'golang' is often used.
"APIs should be designed synchronously, and the callers should orchestrate concurrency if they choose....Channels are useful in some circumstances, but if you just want to synchronize access to shared memory...then you should just use a mutex...Novices to the language have a tendency to overuse channels...One does have to think carefully about ownership hierarchies: only one goroutine gets to close the channel. And if it's in a hot loop, a channel will always perform worse than a mutex: channels use mutexes internally. But plenty of problems are solved very elegantly with channel-based CSP-style message passing." [232]
"in Go is that it is an antipattern for your library to provide something like "a method that makes an HTTP request in a goroutine". In Go, you should simply provide code that "makes an HTTP request", and it's up to the user to decide whether they want to run that in a goroutine....Channels are smelly in an API. IIRC in the entire standard library there's less than 10 functions/methods that return a channel. But the use case does occasionally arise." [233]
- "But no one said Go was easy. It's just simple and easy to reason about. There's a huge benefit to something doing exactly what it says. No hidden behaviours, no overly complex syntax, nothing like that. I came from C# where I have gotten increasingly frustrated with the amount of additions to the language. Like we now have classes, structs and records. We now have async/await but we also have channels. This is what having a huge toolbox causes. Some people are going to write code using channels, me others will use async. Some people will use features that others don't care about. I think there's huge benefit in a simple language. In Go I know that there's only 2 ways to do concurrency. You either use go routines and mutexes or you use go routines and channels. Generics will bring a lot of helper functions that weren't possible before which will remove a lot of repetitive code. But otherwise I am super happy with writing Go code. All my pet peeves of something like modern Java or C# are gone. " [234]
- https://blog.khanacademy.org/half-a-million-lines-of-go/
- "There are features that could be added to the language that would make it more readable and less error-prone without compromising the simplicity of the core language. Generics are the famous example, but the one that really gets me is the the lack of nullable type signatures. This is a great way to avoid an entire class of bugs that nearly every modern language I've used has evolved a solution for except Go. Another issue I have is the reliance on reflection. In general, I think if you have to rely on reflection to do something, that usually means you're working around some inherent limitation in the normal language - and the resulting code is often far less readable than the code would be in a more expressive language. Lots of Go libraries and frameworks are forced to use it in a lot of cases because there's just no other way to express some really basic things without it. I really want to like Go. There's a lot I like - the "only one way to do something" approach means that code always feels consistent. Errors as values is a far superior approach to exceptions. I had to write some for a job interview project a while back and it felt really refreshing, but every time I try to use it for a personal project, I don't feel like I'm getting anything out of it that I couldn't get out of say, Rust, or modern, typed Python. " -- Paul E. Warner
- "I think you are right about explicit nullability. A language like Go with this feature, as well as named arguments and limited ADT's could be very compelling for Go's use case." -- https://news.ycombinator.com/item?id=27182690
- "One of the reasons I love Go is that it's just so easy to read Go code written by other people on my team or third party libraries. There have been tons of cases where I'm working with another library and I just step into their code in my editor to understand how something works. And the code is always so easy to follow. It's very rare that I come across weird patterns or someone trying to be very clever. Its always straightforward code. " [235]
- " I have written hundreds of thousands of lines of plain old C, as well hundreds of thousands of lines of Go over a career spanning three decades. I've used Go in earnest since 2013. I would pick Go for any large project in a heartbeat. I have to concur that the haters who post to HN probably have never used this language in earnest. Here's what you get out of the box:
- Extensive standard library * Cheap concurrency * Fast compilation * Built-in test/benchmark framework, including coverage and race detection * Built-in heap and CPU profiling Of all the complaints the one I understand the least is the reservations about performance. It is within 10-20% of C, and callouts to C and assembly are possible. (My 8-way multiplexed MD5 routine was written in AVX assembler and lives on in minio.) Extracting that last 10-20% is two or three sigmas away from the norm of how programming languages are used these days. The objection to generics is similar. The lack of generics shows up -- once in a long while. It doesn't prevent me from being immensely productive with this language. Looking back at the last few years I've wondered if I could have accomplished what I did in Go by using a different language (C in particular) and the answer, for me, is "not a chance". " [236]
- "To me, there was something to the fact that things were going essentially to plan with no big surprises...I don’t think learning Go has been much of a stumbling block for folks. We have had discussions around things like “when do you wrap errors” or “do you return a NotFound? error or just nil if something is missing from the database”, but I’m not sure that most of those sorts of details are interesting." [237]
- Why Google's S4TF (Swift for Tensorflow) project used Swift instead of Golang, as interpreted by a third party: " Go: In the document, they state that Go relies too heavily on the dynamic dispatching that its interfaces provide, and that they would have had to make big changes to the language in order to implement the features they wanted. This goes against Go’s philosophy of being simple and having a small surface area. Conversely, Swift’s protocols & extensions afford a lot of freedom in terms of how static you want your dispatch to be. Also, the language is quite complex (and getting more complex every day), so making it a bit more complex to accommodate the features that Google wanted wouldn’t pose as big of a problem. " -- [238]
- vs Rust: "Many years ago when I was learning Go I wrote a blog post about what I liked and disliked as a Rustacean learning Go. Since then, I’ve spent a lot more time with Go, and I’ve learned to like each Go design decision that I initially disliked, except for the lack of sum types." [239]
Popularity
- Beloved by those who use it, but not popular
Misc
Internals and implementations
Core data structures: todo
note: it would be nice if this book had a nice diagrams, such as is found on the page [240], for the in-memory representations of each core data type
Number representations
Integers
Floating points todo
[241]
array representation
'arrays' are of constant length (the length is in the type) (like Python tuples) 'slices' are pointers to arrays. However, practically, slices operate as variable-sized arrays.
multidimensional arrays: todo
limits on sizes of the above
string representation
Golang strings are immutable [242]. They are "a 2-word structure containing a pointer to the string data and a length." [243].
memory model
"Programs that modify data being simultaneously accessed by multiple goroutines must serialize such access. To serialize access, protect the data with channel operations or other synchronization primitives such as those in the sync and sync/atomic packages. If you must read the rest of this document to understand the behavior of your program, you are being too clever. Don't be clever." -- https://golang.org/ref/mem
Links:
Goroutines
Links:
Closures
"...represent a closure as a pair of pointers, one to static code and one to a dynamic context pointer giving access to the captured variables" -- [244]
Links:
Garbage Collection and memory allocation
"Go’s garbage collector is a concurrent mark-sweep with very short stop-the-world pauses" [247]
Links:
libc
https://github.com/golang/go/blob/c95464f0ea3f87232b1f3937d1b37da6f335f336/src/runtime/stack.go#L899
This is a different approach from dedicating a big chunk of address space to the stack, and then lazily mapping & allocating the pages. In this latter approach, stack is never moved and is always continuous, it requires neither scanning the stack for pointers nor support for segmented stacks.
1
orib 3 months ago | link |
Go defaults to well below a single page of memory for the thread stack.
1
matklad 3 months ago | link |
“Well-bellow” is 2kb of memory, half a page. " -- [249]
Toolchain
Go has it's own toolchain, it's own ABI, it's own file formats. Here's a link explaining why. They are written in Go.
Included is an assembly IR, sort of [250] [251].
Go has it's own ABI calling convention:
"
- All registers are caller-saved
- All parameters are passed on the stack
- Return values are also returned on the stack, in space reserved below (stack-wise; higher addresses on amd64) the arguments. " -- [252]
(note: i don't quite understand this; if registers are caller-saved, then why not pass arguments and return values in registers? Is it for platform-independence of a sort?)
See also:
Compiler and toolchain in Go presentation
motivations:
" All made easier by owning the tools and/or moving to Go:
linker rearchitecture
new garbage collector
stack maps
contiguous stacks
write barriers
The last three are all but impossible in C:
C is not type safe; don't always know what's a pointer
aliasing of stack slots caused by optimization" -- http://talks.golang.org/2015/gogo.slide
" Goroutine stacks
Until 1.2: Stacks were segmented.
1.3: Stacks were contiguous unless executing C code (runtime).
1.4: Stacks made contiguous by restricting C to system stack.
1.5: Stacks made contiguous by eliminating C." -- http://talks.golang.org/2015/gogo.slide
initial performance problems (were solved): "
Performance problems
Output from translator was poor Go, and ran about 10X slower. Most of that slowdown has been recovered.
Problems with C to Go:
C patterns can be poor Go; e.g.: complex for loops
C stack variables never escape; Go compiler isn't as sure
interfaces such as fmt.Stringer vs. C's varargs
no unions in Go, so use structs instead: bloat
variable declarations in wrong place
C compiler didn't free much memory, but Go has a GC. Adds CPU and memory overhead. "
and fixes:
"
Performance fixes
Profile! (Never done before!)
move vars closer to first use
split vars into multiple
replace code in the compiler with code in the library: e.g. math/big
use interface or other tricks to combine struct fields
better escape analysis (drchase@).
hand tuning code and data layout
Use tools like grind, gofmt -r and eg for much of this.
Removing interface argument from a debugging print library got 15% overall!
More remains to be done.
"
Compiler: About 50K lines of portable code + about 10% architecture-specific code
Assembler: Less than 4000 lines, <10% machine-dependent
Linker: 27000 lines summed across 4 architectures, mostly tables (plus some ugliness).
Links:
Generics
Extensions
Variant implementations
ARM implementation
GoLite subset (pedagogical)
"...I poured in more than 35 per week to design, document, and implement a subset of Go...t was a subset, and much of what makes Go interesting was removed (concurrency, interfaces, GC)..."
emgo
https://github.com/ziutek/emgo
" Emgo follows Go specification with exception for memory allocation.
Think about Emgo as C with: Go syntax, Go packages, Go building philosophy.
In Go, variable declared in function can be allocated on the stack or on the heap - escaping analisis is used for decision. In Emgo (like in C), all local variables are stack allocated. Dynamic allocation and garbage collection can occurs only when:
new or make builtin function is used.
Non-empty strings are concatanated.
Builtin append function is called and there is not enough space in destination.
An element to map is added or removed.
Current allocator is trivial and doesn't contain GC. This isn't big disadventage for many embeded applications that need allocation only during startup. They often run on MCU that has only few kilobytes of SRAM and in many cases they must respond in realtime. The simple "stop the world GC" can't be used and much sophisticated one consumes to much Flash/SRAM.
The target is to allow chose between multiple allocators (simply import it as package) and use one that best fits application needs.
Examples:
The following function is correct Go function:
func F() ([]byte, *int) { i := 4 return []byte{1, 2, 3}, &i }
but it isn't correct Emgo function - you need to rewrite it this way:
func F() ([]byte, *int) { i := new(int) *i = 4 b := append([]byte{}, []byte{1, 2, 3}...) return b, i }
...
Standard library.
Emgo standard library doesn't follow Go standard library....
Not yet implemented:
Maps. Defer. String concatanation. Append. Unnamed structs. Closures. " -- [253]
tinygo
https://tinygo.org/ https://github.com/aykevl/tinygo https://aykevl.nl/2019/02/tinygo-goroutines
"similar to emgo but a major difference is that I want to keep the Go memory model (which implies garbage collection of some sort). Another difference is that TinyGo? uses LLVM internally instead of emitting C, which hopefully leads to smaller and more efficient code and certainly leads to more flexibility."
" Currently supported features:
control flow
many (but not all) basic types: most ints, strings, structs
function calling
interfaces for basic types (with type switches and asserts)
goroutines (very initial support)
function pointers (non-blocking)
interface methods
standard library (but most packages won't work due to missing language features)
slices (partially)
Not yet supported:
float, complex, etc.
maps
garbage collection
defer
closures
channels
introspection (if it ever gets implemented)
..."
" aykevl 2 days ago [-]
Yeah the heavyweight standard library is a big problem. I'm trying to solve it (in part) with smart analysis that tries much harder to remove dead code and dead global data. Maybe even try to run some initializers at compile time so the result can be put in flash instead of RAM.
In my experience, it's not the unicode package that's big but the run-time typing information and the reflect package used by packages like fmt. The fmt package doesn't know what types it will need to accept so it literally supports everything (including unused types).
And as you mention WASM: I've been thinking about supporting that too, exactly because WASM and microcontrollers have some surprising similarities (small code size, fast startup, little OS-support).
reply "
" There are 3 LLVM-based Go compilers that I'm aware of: gollvm, llgo, and tinygo. Of those, only TinyGo? reimplements the runtime that causes lots of (code size) overhead in the other compilers.
There is more to a toolchain than translating source code to machine code. I'm sure the others do that job just as well, but only TinyGo? combines that with a reimplemented runtime that optimizes size over speed and allows it to be used directly on bare metal hardware: binaries of just a few kB are not uncommon. " -- aykevl
" Go uses goroutines for mostly-cooperative multitasking. In general, each goroutine has a separate stack where it can store things like temporary values and return addresses. At the moment the main Go compiler starts out with a 2kB stack for each goroutine and grows it as needed.
TinyGo? is different. The system it uses for goroutines is based on the async/await model like in C#, JavaScript? and now also C++. In fact, we're borrowing the C++ implementation that's used in Clang/LLVM. The big difference here is that TinyGo? inserts async/await keywords automatically. ... Luckily, you don't have to worry about the color of your function in Go, but I've chosen to use this as an implementation strategy for TinyGo?. The reason is that TinyGo? also wants to support WebAssembly? and WebAssembly? does not support efficient stack switching like basically every other ISA does: the stack has been hidden entirely for security reasons. So I've decided to use coroutines1 instead of actually separate stacks. " [254]
golang on wasm
- " Finally :) My coworker back in 2017 implemented the WebAssembly? backend for the Go compiler[255], and noted at the time that WASM doesn't have any equivalent to setjmp/longjmp. As a result, Go's WebAssembly? implementation actually emulates a register machine on top of the WASM stack machine. Quoting him (some parts omitted): > For example its architecture is a stack machine instead of a register machine. This means that it isn't immediately suitable as simply yet another target at the last stage of the Go compiler next to x86 and friends. > There might be an alternative: Emulate what we need. We may use the stack machine to emulate a register machine and hopefully do so in a reasonably performant way. > WebAssembly? has linear memory with load and store instructions, which is good. We would not use WebAssembly?'s call instruction at all and instead roll our own stack management and call mechanism. Stacks would live on that linear memory and be managed by the Go runtime. The stackpointer would be a global variable. All code would live in a single WebAssembly? function. The toplevel would be a giant switch statement (or WebAssembly?'s br_table based equivalent) with one branch for each function. Each function would have another switch statement with one branch per SSA basic block. > There are some details that I'm omitting here, but in the big picture this looks like a decent register machine to me. Of course the performance of this heavily depends on how well WebAssembly? can transform these constructs into actual machine code. " -- [256]
- https://github.com/golang/go/issues/18892
- https://github.com/neelance/go/pull/7/files
- https://github.com/golang/go/blob/9012996a9a127fd566c72baac5e7d8ba45b4865e/src/cmd/compile/internal/ssa/gen/WasmOps.go
- wasm syscalls(?) in golang-on-wasm implementation, from https://github.com/neelance/go/blob/13b0931dc3fa8c8a6ab403dbdae348978a53c014/src/runtime/sys_wasm.s : wasmMove, wasmZero, wasmDiv, wasmTruncS, wasmTruncU, exitThread, osyield, usleep, currentMemory, growMemory, wasmExit, wasmWrite, nanotime, walltime, scheduleCallback, clearScheduledCallback, getRandomData
- some interesting lists from https://github.com/neelance/go/blob/13b0931dc3fa8c8a6ab403dbdae348978a53c014/src/cmd/link/internal/wasm/asm.go :
- I32, I64, F32, F64
- sectionCustom , sectionType , sectionImport , sectionFunction, sectionTable, sectionMemory , sectionGlobal , sectionExport , sectionStart, sectionElement, sectionCode , sectionData
- " var wasmFuncTypes = map[string]*wasmFuncType{ "_rt0_wasm_js": &wasmFuncType{Params: []byte{I32, I32}}, argc, argv "runtime.wasmMove": &wasmFuncType{Params: []byte{I32, I32, I32}}, dst, src, len "runtime.wasmZero": &wasmFuncType{Params: []byte{I32, I32}}, ptr, len "runtime.wasmDiv": &wasmFuncType{Params: []byte{I64, I64}, Results: []byte{I64}}, x, y -> x/y "runtime.wasmTruncS": &wasmFuncType{Params: []byte{F64}, Results: []byte{I64}}, x -> int(x) "runtime.wasmTruncU": &wasmFuncType{Params: []byte{F64}, Results: []byte{I64}}, x -> uint(x) "runtime.gcWriteBarrier": &wasmFuncType{Params: []byte{I64, I64}}, ptr, val "cmpbody": &wasmFuncType{Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, a, alen, b, blen -> -1/0/1 "memeqbody": &wasmFuncType{Params: []byte{I64, I64, I64}, Results: []byte{I64}}, a, b, len -> 0/1 "memcmp": &wasmFuncType{Params: []byte{I32, I32, I32}, Results: []byte{I32}}, a, b, len -> <0/0/>0 "memchr": &wasmFuncType{Params: []byte{I32, I32, I32}, Results: []byte{I32}}, s, c, len -> index } "
- " writeTypeSec(ctxt, types) writeImportSec(ctxt, hostImports) writeFunctionSec(ctxt, fns) writeTableSec(ctxt, fns) writeMemorySec(ctxt) writeGlobalSec(ctxt) writeExportSec(ctxt, rt0) writeElementSec(ctxt, uint64(len(hostImports)), uint64(len(fns))) writeCodeSec(ctxt, fns) writeDataSec(ctxt) if !*ld.FlagS? { writeNameSec(ctxt, len(hostImports), fns) } "
- " globalRegs := []byte{ I32, 0: PC_F I32, 1: PC_B I32, 2: SP I64, 3: CTXT I64, 4: g I64, 5: RET0 I64, 6: RET1 I64, 7: RET2 I64, 8: RET3 I32, 9: RUN } "
- " sections := []*sym.Section{ ctxt.Syms.Lookup("runtime.rodata", 0).Sect, ctxt.Syms.Lookup("runtime.typelink", 0).Sect, ctxt.Syms.Lookup("runtime.itablink", 0).Sect, ctxt.Syms.Lookup("runtime.symtab", 0).Sect, ctxt.Syms.Lookup("runtime.pclntab", 0).Sect, ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect, ctxt.Syms.Lookup("runtime.data", 0).Sect, } "
- https://github.com/neelance/go/blob/13b0931dc3fa8c8a6ab403dbdae348978a53c014/src/cmd/compile/internal/ssa/rewriteWasm.go appears to rewrite a basis set of "golang assembly" ops into wasm. The basis set appears to be in an enormous switch statement in rewriteValueWasm, the branches of which are (grouped into similar subsets):
- OpAdd?16, OpAdd?32, OpAdd?32F, OpAdd?64, OpAdd?64F, OpAdd?8
- OpAddPtr?
- OpAddr?
- OpAnd?16, OpAnd?32, OpAnd?64, OpAnd?8, OpAndB?
- OpClosureCall?
- OpCom?16, OpCom?32, OpCom?64, OpCom?8
- OpConst?16, OpConst?32, OpConst?32F, OpConst?64, OpConst?64F, OpConst?8, OpConstBool?, OpConstNil?
- OpConvert?, OpCvt?32Fto32, OpCvt?32Fto32U, OpCvt?32Fto64, OpCvt?32Fto64F, OpCvt?32Fto64U, OpCvt?32Uto32F, OpCvt?32Uto64F, OpCvt?32to32F, OpCvt?32to64F, OpCvt?64Fto32, OpCvt?64Fto32F, OpCvt?64Fto32U, OpCvt?64Fto64, OpCvt?64Fto64U, OpCvt?64Uto32F, OpCvt?64Uto64F, OpCvt?64to32F, OpCvt?64to64F
- OpDiv?16, OpDiv?16u, OpDiv?32, OpDiv?32F, OpDiv?32u, OpDiv?64, OpDiv?64F, OpDiv?64u, OpDiv?8, OpDiv?8u
- OpEq?16, OpEq?32, OpEq?32F, OpEq?64, OpEq?64F, OpEq?8, OpEqB?, OpEqPtr?
- OpGeq?16, OpGeq?16U, OpGeq?32, OpGeq?32F, OpGeq?32U, OpGeq?64, OpGeq?64F, OpGeq?64U, OpGeq?8, OpGeq?8U
- OpGetCallerPC?
- OpGetCallerSP?
- OpGetClosurePtr?
- OpGreater?16, OpGreater?16U, OpGreater?32, OpGreater?32F, OpGreater?32U, OpGreater?64, OpGreater?64F, OpGreater?64U, OpGreater?8, OpGreater?8U
- OpInterCall?
- OpIsInBounds?
- OpIsNonNil?
- OpIsSliceInBounds?
- OpLeq?16, OpLeq?16U, OpLeq?32, OpLeq?32F, OpLeq?32U, OpLeq?64, OpLeq?64F, OpLeq?64U, OpLeq?8, OpLeq?8U
- OpLess?16, OpLess?16U, OpLess?32, OpLess?32F, OpLess?32U, OpLess?64, OpLess?64F, OpLess?64U, OpLess?8, OpLess?8U
- OpLoad?
- OpLsh?16x16, OpLsh?16x32, OpLsh?16x64, OpLsh?16x8, OpLsh?32x16, OpLsh?32x32, OpLsh?32x64, OpLsh?32x8, OpLsh?64x16, OpLsh?64x32, OpLsh?64x64, OpLsh?64x8, OpLsh?8x16, OpLsh?8x32, OpLsh?8x64, OpLsh?8x8
- OpMod?16, OpMod?16u, OpMod?32, OpMod?32u, OpMod?64, OpMod?64u, OpMod?8, OpMod?8u
- OpMove?
- OpMul?16, OpMul?32, OpMul?32F, OpMul?64, OpMul?64F, OpMul?8
- OpNeg?16, OpNeg?32, OpNeg?32F, OpNeg?64, OpNeg?64F, OpNeg?8
- OpNeq?16, OpNeq?32, OpNeq?32F, OpNeq?64, OpNeq?64F, OpNeq?8, OpNeqB?, OpNeqPtr?
- OpNilCheck?
- OpNot?
- OpOffPtr?
- OpOr?16, OpOr?32, OpOr?64, OpOr?8, OpOrB?
- OpRound?32F, OpRound?64F
- OpRsh?16Ux16, OpRsh?16Ux32, OpRsh?16Ux64, OpRsh?16Ux8, OpRsh?16x16, OpRsh?16x32, OpRsh?16x64, OpRsh?16x8, OpRsh?32Ux16, OpRsh?32Ux32, OpRsh?32Ux64, OpRsh?32Ux8, OpRsh?32x16, OpRsh?32x32, OpRsh?32x64, OpRsh?32x8, OpRsh?64Ux16, OpRsh?64Ux32, OpRsh?64Ux64, OpRsh?64Ux8, OpRsh?64x16, OpRsh?64x32, OpRsh?64x64, OpRsh?64x8, OpRsh?8Ux16, OpRsh?8Ux32, OpRsh?8Ux64, OpRsh?8Ux8, OpRsh?8x16, OpRsh?8x32, OpRsh?8x64, OpRsh?8x8
- OpSignExt?16to32, OpSignExt?16to64, OpSignExt?32to64, OpSignExt?8to16, OpSignExt?8to32, OpSignExt?8to64
- OpSlicemask?
- OpStaticCall?
- OpStore?
- OpSub?16, OpSub?32, OpSub?32F, OpSub?64, OpSub?64F, OpSub?8
- OpSubPtr?
- OpTrunc?16to8, OpTrunc?32to16, OpTrunc?32to8, OpTrunc?64to16, OpTrunc?64to32, OpTrunc?64to8
- OpWB?
- OpWasmF?64Add, OpWasmF?64Mul, OpWasmI?64Add, OpWasmI?64AddConst?, OpWasmI?64And, OpWasmI?64Eq, OpWasmI?64Eqz, OpWasmI?64Load, OpWasmI?64Load16S, OpWasmI?64Load16U, OpWasmI?64Load32S, OpWasmI?64Load32U, OpWasmI?64Load8S, OpWasmI?64Load8U, OpWasmI?64Mul, OpWasmI?64Ne, OpWasmI?64Or, OpWasmI?64Shl, OpWasmI?64ShrS?, OpWasmI?64ShrU?, OpWasmI?64Store, OpWasmI?64Store16, OpWasmI?64Store32, OpWasmI?64Store8, OpWasmI?64Xor
- OpXor?16, OpXor?32, OpXor?64, OpXor?8
- OpZero?
- OpZeroExt?16to32, OpZeroExt?16to64, OpZeroExt?32to64, OpZeroExt?8to16, OpZeroExt?8to32, OpZeroExt?8to64
- lowering in https://github.com/neelance/go/blob/13b0931dc3fa8c8a6ab403dbdae348978a53c014/src/cmd/compile/internal/ssa/gen/Wasm.rules
- https://github.com/neelance/go/blob/13b0931dc3fa8c8a6ab403dbdae348978a53c014/misc/wasm/wasm_exec.js
- some global.Go functions in https://github.com/neelance/go/blob/13b0931dc3fa8c8a6ab403dbdae348978a53c014/misc/wasm/wasm_exec.js
- mem, setInt64, getInt64, loadValue, storeValue, loadSlice, loadSliceOfValues, loadString, timeOrigin
- and stuff in global.Go.importObject:
- wasmExit(code int32)
- wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
- nanotime() int64
- walltime() (sec int64, nsec int32)
- scheduleCallback(delay int64) int32
- clearScheduledCallback(id int32)
- getRandomData(r []byte)
- boolVal(value bool) ref
- intVal(value int) ref
- floatVal(value float64) ref
- stringVal(value string) ref
- valueGet(v ref, p string) ref
- valueSet(v ref, p string, x ref)
- valueIndex(v ref, i int) ref
- valueSetIndex(v ref, i int, x ref)
- valueCall(v ref, m string, args []ref) (ref, bool)
- valueInvoke(v ref, args []ref) (ref, bool)
- valueNew(v ref, args []ref) (ref, bool)
- valueFloat(v ref) float64
- valueInt(v ref) int
- valueBool(v ref) bool
- valueLength(v ref) int
- valuePrepareString(v ref) (ref, int)
- valueLoadString(v ref, b []byte)
- debug
- https://github.com/golang/go/blob/0d0193409492b96881be6407ad50123e3557fdfb/src/runtime/asm_wasm.s
---
---
llgo
An LLVM Golang implementation
GoLLVM
https://go.googlesource.com/gollvm/
Links