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?