Discussing Go idiosyncrasies (split thread)

Tags: #<Tag:0x00007f1149b77f30>


On the other hand Clojure

This parts continues with difficulties Rich Hickey had most likely faced when implementing Clojure. It says nothing about Clojures simplicity - or I’ve failed to understand your point.

BTW IMO Clojure is not simple at all, while my brain is wired for statically typed labguages (I admit), I could not understand what’s going on in a web project in Clojure (which I’ve created right after the talk to play with Clojure) because of macros and dynamic typing.

On polymorphism, why having more options better? Is it better on it’s own? Or it’s a demand of artifacts generated from Clojure constructs, that have some limitations such as being able to access JVM contructs?

On generics, I stated that I do not like to see them in Go. Actually I see them as counter productive elements of a code base that is meant to be around for a long time.

If Clojure gave me the tooling and practices of Go (native compilation, gofmt, robust runtime) I might be able to use or even like it - this is based on my personal experience and apparently quite some of people has similar experiences.


Just want to add something about the items start with enumerate …. They are correct to a large extent as long as they are happening inside the same os process. The moment a net comes it, both approaches are equally reliable/unreliable and things like preventing double entries, should be handled properly at app level logic.

An even then, trade-offs (both objective and subjective) comes in (as you have mentioned later on that different apps might require different tolling and setup and requirements).


If you’re referring to the repeated checks of if err != nil, I think it’s quite basic, and leads to the promotion of the noise in code.

Here’s a small real-life example. Recently I wrote a small tool in Go. Here’s one function from the code:

func readConfig() config {
	raw, err := ioutil.ReadFile("config.json")
	if err != nil {

	var decoded config
	err = json.Unmarshal(raw, &decoded)
	if err != nil {

	return decoded

Here’s how that same function could be implemented in Elixir:

def read_config() do
  with {:ok, contents} <- File.read("config,json"), do: Jason.decode(contents)

While the Elixir code has the same semantics, i.e. it stops on the first encountered error and returns it, I think it’s more intention revealing. Instead of paranoiacally checking for error after each function invocation, we just provide the condition which has to be met to keep going further. As a result, the code is less ridden with noise, and it’s easier to tell what it’s suppose to do.

This can become even more concise if you reach for concurrency and supervision trees, because then in many cases you can fail fast (aka let-it-crash), and can change the code to something like this:

def read_config!(), do: Jason.decode!(File.read!("config.json"))


I know Elixir a bit and the Go code should return errors (most likely) and it (appears) is not idiomatic Go.

Let’s ignore those two pints.

I can literally read the Go code, 10 years from now, and even without knowing Go, I can tell what’s going on. That’s what I call (IMO) spending the cognitive load, properly.


I think we kind of got on a Go vs Clojure track - which really wasn’t the original point of the Rob Pike vs. Rich Hickey “Simplicity” comparison. Rob Pike always emphasized that they had to solve complex problems in order to deliver “Go’s simplicity” to the programmer. So I’m not sure how the challenges either of them faced during the implementation of their respective languages relate to the “Simplicity” dicsussion.

While my brain is wired for statically typed languages (I admit), I could not understand what’s going on in a web project in Clojure (which I’ve created right after the talk to play with Clojure) because of macros and dynamic typing.

Can’t say I’m surprised - coming from (C/C++/C#/Java)-ville it took me a lot of 4clojure exercises to just start wearing down my own personal unfamiliarity.

On polymorphism, why having more options better?

Rich Hickey said it but he didn’t explain it and you brought it up.

On the spot having to guess I’d start with:

  • While it is possible to define interfaces with a single method, a multi-method convey’s intent of only being about one function by deliberately accepting that constraint. With an interface there is always the temptation to add more methods later.
  • A protocol conveys the intent of describing a set of functions used to interact with an entity - i.e. usually there will be more than one function (or there is the possibility of more being added later).

Again I’m not quite sure how this became about Go vs Clojure. My intent was to contrast Rob Pike’s and Rich Hickey’s notions of “Simplicity” which I feel are far from similar.


My mistake on not elaborating how the context of “Why” about implementing Go relates to simplicity: Go is not the simplest possible; I meant. And has flaws because of that background.

But the trade-offs worth it and Go served well in many occasions.

I have to add this was about explaining things from a Go programmer point of view, not bad-mouthing about other PLs. There will always be different PLs with different concerns and priorities which will result in different practices and toolsets.


That would be unfair for Go, here’s how it can be “shortened and more idiomatic” to be on par with Elixir’s.

func readCfg() (cfg config, err error) {
	f, err := ioutil.ReadFile("config.json")
	if err != nil {
	err = json.Unmarshal(f, &cfg)

Here’s the fail-fast version:

func readCfg() (cfg config) {
	f, _ := ioutil.ReadFile("config.json")
	json.Unmarshal(f, &cfg)


Thanks. I’m not really versatile with go, so it’s good to see these little tips.

I still find that quite noisy compared to Elixir’s oneliner, because you still have to use three extra lines for each possibly failing invocation, except the last one (or more likely four, because it becomes quite hard reading this large chunk of code without vertical separators). So if we need to chain a couple of such invocations, we end up with a lot of noise.

That doesn’t fail fast on my machine. Here’s the example which “successfully” decodes the data even though the file is not present:

package main

import (

type config struct {
	Foo string

func readCfg() (decoded config) {
	f, _ := ioutil.ReadFile("config.json")
	json.Unmarshal(f, &decoded)

func main() {
	config := readCfg()
	fmt.Println("foo = ", config.Foo)
	fmt.Println("done without errors")
$ go run test.go
foo =
done without errors

Perhaps I’m missing something? I’m definitely curious to learn how can I fail fast in go, because that would reduce a lot of noise from my simple tool. I’d like it to crash on the first function which returns non-nil error, and print the error message (with a stacktrace if possible).


I raised up an issue on Github and it got removed immediately

Communication works ^-^

When it comes to Generics, you dont need to guess what Google thinks about it, you can simply ask the wiki :wink:

And about the verbosity of Go’s error handling:

Rich Hickey says in exactly this InfoQ upload above, that this kind of vertical verbosity is ok.

I will skip through the talk and link the line then. :slightly_smiling_face:


There’s no equivalent of exception in Go, but you can get stacktrace from anywhere using runtime package.

Changing from

func readCfg() (cfg config) {
	f, _ := ioutil.ReadFile("config.json")
	json.Unmarshal(f, &cfg)


func readCfg() (cfg *config) {
	f, _ := ioutil.ReadFile("config.json")
	json.Unmarshal(f, &cfg)

(Notice the pointer as return)

Shall give you closer to what your expectation of fail-fast, with one caveat, you need to access the pointer first.
eg. config.Foo


This unfortunately doesn’t fail fast, i.e. it doesn’t fail where the error takes place (e.g. reading of the file, or decoding a json). Instead, the error is silently ignored (i.e. this is the exact opposite of fail-fast), some arbitrary default is returned, and the failure happens later when I try to use the result of readCfg with an unhelpful error panic: runtime error: invalid memory address or nil pointer dereference.

For context, this piece of code is a part of a small tool which we shipped to the client. There are a bunch of these noisy if err != nil checks which I’d like to remove. However, I need to do this check exactly because I want to report what is wrong (and the place where the error happened). It doesn’t have to be anything super-sophisticated, a printed error and a stacktrace is enough. However, I need to have something which gives me a clear pointer as to what went wrong and where.

Again, for comparison, this is how you would fail fast in Elixir:


And this is how you’d return the first encountered error to the caller:

with {:ok, contents} <- File.read("config,json"), do: Jason.decode(contents)



Here he says verbosity can be simple and he says it in another video iirc more concrete about vertical verbosity.

And yeah, I agree, it might get cumbersome to type all these errors.
F# implements this even nicer by its Railway oriented programming, which is applicable to other languages as well. :slightly_smiling_face:

Go simply shows you each single possible error line by line.
It is explicit and the error type is specified as such. Both is not implemented in other languages are both is very helpful.

Which solution is simpler in these two cases?
What is simpler as calling one line and getting a dependency free executable for your platform? What is more simple as reducing the possible way of solving things? One loop? And so on?

All this is simple.


Honestly, I want that feature too. https://github.com/golang/go/issues/21161#issuecomment-353796200


My biggest complaint about Go is still the insanity of the 8 space indentation from go fmt. :slight_smile:


It’s not so much about writing, it’s about reading. Unfortunately, regardless of what Rich Hickey says, I’m having a hard time reading the code where one function call, is obscured by extra 3-4 lines of code. And since code is read much more often than written, and even way more than a programming langue is being learned, I don’t like the trade-off made in Go. In my opinion it’s shaving a bit from the one-off price of learning the language at the cost of constantly putting the extra mental strain while reading the code.

Elixir’s with can be used for that too.

It shows you which functions calls may return error . You can get the the same with Elixir’s with :slight_smile: Adding to that the ability to fail fast elegantly, and to isolate individual failures and recover from them using concurrency, I personally feel that Elixir’s error handling is much more versatile, more concise, and at the same time at least as explicit as Go. YMMV of course :slight_smile:


Not 8 spaces, but 1 tab, which is superior over everything else. Everyone can adjust its editor to display exactly the preferred indentation.

But please don’t let argue about that now… Or at least open a new thread for that :wink:


GopherCon 2016: Dave Cheney - Dont Just Check Errors Handle Them Gracefully

Now none of that deals with the “noise” - judging by this discussion I’d be tempted to wrap each standard library call in my own function that immediately augments the error in the above fashion so that at the top level I could just have

f, err := myown.ReadFile(name)
if err != nil {

… but that still leaves me at tedious - both in the writing and reading department.



func (ew *errWriter) write(buf []byte) {
    if ew.err != nil {
    _, ew.err = ew.w.Write(buf)

… i.e. shove the noise into the function that is also responsible for augmenting the Error.

Right Now still $10.00 @ PacktPub Learning Functional Programming in Go


I’ve programmed in Java for a very long time, I know it quite well, and yes I know how to write code in it that rivals that of C/C++, but the code is always longer, more unreadable, and especially more importantly it is far more unmaintainable than the same code in OCaml or Rust or even C++ (and that’s saying a lot there…). :wink:

Unsure about the F# code, but the 2 sources in OCaml I saw were pretty bad and not scaleable, but then again the debian.org benchmark list has been very well known for not being very good, so… The usual caveats with benchmarks. :wink:

Hmm? Unsure about .NET as I don’t use it, but I’m pretty sure I know the JVM very well (considering I still manipulate the raw bytecode on occasion to this very week, blegh…).

What mistakes? JVM and .NET force conventions such that once learned without much else being known then it makes people think everything can be solved in such ways, and certainly everything can, but that does not mean it is maintainable or pleasant to use in comparison to others.

Oh really? Then do tell how you would do something as trivially simple in almost any other language as having a function that can accept an integer or a float and return the same type doubled?

It’s error handling is VERY half-century-ago style returning of error codes (the usual ‘C’ style), it is easily one of the absolute worst styles to use as it is so easy to forget to handle a given error, and even handling it involves performing a comparison and compare-jump when it should not be required in many other forms (exceptions) thus slowing it down needlessly (unless you ignore it of course…), or if you do want to force it’s usage then at least make it in a form that is not ignorable (like some kind of sum type). It is the worst of both error-handling worlds… o.O

Ah, great examples there. :slight_smile:

Yes, perfect word here. :slight_smile:

That’s… a disturbing link… o.O

Simplicity that Go tries to show off is a simplicity of language, which means almost no features, but if you want the most simple language definition ever then just use a Lisp’y language, everything is a function call, only one form, dead-simple. Go is not simple, go is indeed tedious, it encourages copying code, it discourages re-use…

Uh… but it is NOT explicit… I don’t think this person is the ‘seasoned’ person you claim as any error handling that is trivially ignorable is not a good error handling system (and yes I claim that Elixir’s error handling is very poor as well, as is Erlang’s). I’m not even sure how anyone could even consider ignoring mandatory errors as a strength?! o.O

C follows the same style, it’s error handling is also very poor. Java… well it ‘had’ a decent error handling system with checked exceptions but it’s syntax for them was so abysmal that no one used them and just continues to use unchecked exceptions… >.<

Go’s structs are just keyed prod types, a perfectly reasonable type (though I also like to have optional unkeyed prod types like tuples, which Go has, just not allowed use of in user code outside returns…). However, Go is lacking sum types, which is a massive fault. C has sum types as broken as they are (unions), but it does have them; even Scala/Kotlin/Java/C# has Sum types, yet Go does not.

Also, the lack of a type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java.

Uh, Java objects are heavy sure, but in C++ an object can be packed even better than Go (you can choose efficiency or packing, it defaults to efficiency), and if you don’t use inheritance (which I don’t in my C++ work when possible) then there are not even any type pointers stored anywhere, not even in the global section (thus making it more light-weight than Go).

Go is anything but concise… The amount of duplicate code because of the lack of any form of generics makes that immediately obvious. The language is not small either, it compiles to far larger binaries than C/C++, and as for useful even Brainf**k is useful, that doesn’t mean it is nice to use though.

This very concept of ‘vendoring’ in dependencies into any language is still so very abhorrant to me… >.>

WHY would anyone want to pollute their repository with non-project code?!? o.O

I still find this hilarious! Kubernetes ended up implementing a type system on top of the objects in Go just so they could have a decent language, though even more verbose. ^.^;

I’ve seen fantastic programs written in shell scripts, like really big nice complicated ones. Does not mean that they should have even been written in shell scripts or rather that writing them in anything else would not have made them a tenth the size (as actually happened when I ported one of those horrors to Perl about 10 years ago, yes that recent, no the system did not have python or ocaml or anything more recent than an old perl version…). ^.^;

This is a BIG thing! Simplicity does NOT mean ‘fewer things’, it means more understandable things, which Go is not (interface{} void pointers, nil sometimes does not compare to nil, no generics, etc… etc… etc…).

Yeah, these are ‘simple’ for the language implementer, but usually one of the levels of hell for the language users (whether they get used to these horrors or not is another issue).

Where as these are simple for the language ‘user’, though not necessarily for the language implementer.

Wait what? Why does Go have this when it is CSP/channel based?!?

Yeah erlang was not build with Actors in mind, but I still think it is one of the best actor implementations out. In my opinion an actor should not be required to handle messages immediately as that is not how reality actually works. Delays and latency and all is built in to our very universal reality, and Erlang models this very well.

But go is not coming from system programming, you would not be able to implement even the smallest of kernels in it, the language is just not that expressive or powerful. It looks like it was designed to handle service daemons.

Then why are empty interfaces (void pointers) used in near every project I’ve ever seen in Go? That is quite the opposite of better coding as it throws type checking to runtime (if a check is bothered to be added at all by the programmer, which is of course another manual thing you have to do) instead of compile-time?

Yeah this is a big wart on the language. In my experience over many decades any language that adds in a garbage collector is inherently broken as it ‘tends to’ (though not always) encourage improper resource handling for everything else that is not memory, like files, network handles, whatever else. Language that can generically handle any resource, not just memory, is far superior (Rust does this fantastically, and as it can generically handle any resource, then it doesn’t even need a garbage collector). Languages like C++ invented things like RAII and smart pointers and such, but it is definitely better to bake it into the language itself, but RAII does indeed handle any resource, not just memory. Like in a modern designed language you’d hope that holding/closing a resource, whether it be memory, a file handle, whatever, would be a solved issue, but look at things like https://gobyexample.com/reading-files or so and that is abhorrantly bad! That is in the same vein as malloc/dealloc’ing memory! So they have a GC to handle memory, but everything else you have to handle manually, like that is BAD design of the highest order. And again look at the abhorrent error handling within it. :wink:

Uh… what would you use for a prod type? Not having a sum type is bad enough but not having a prod type?!? How would that even work?!? O.o?

Not really from what I’ve seen? Google barely uses it, and even though only the Go adherents there do…

Go is not made by google, it is made by google employees but it is not a google thing as it is still ‘lab’ state and could be tossed any time from their listings (among other google lab things are quite a huge multitude of dead or dying things…).

Java implemented them by unbounded casting, which is pretty bad… ^.^;

There are still 2 definitions of simple being used in this thread, and it follows one of them (simple to implement), it is not following the other (simple to use). (Though go is not either I’d argue, but it’s closer to simply implemented, and definitely not simple to use.)

So tell me how you would make a, oh, the dead-simple reduce function (or fold or whatever you want to call it)? It should be able to reduce to an integer, a float, a custom struct, anything that the user wants to, from a list, map, array, custom user type, or anything else that is iterable. This is of course trivial (if not always perfectly efficient) in everything from Elixir to Java to OCaml to C++ (‘simple’ for the user to use) and I can show the trivially short implementations there-of if wanted.
Bonus if you do it with compile-time checking of the types as is trivial in most languages (though not Elixir), which Go should be able to do since it is statically typed too (though not strongly typed sadly).

C and C++ both do. :wink:

OCaml’s formatter is just an indenter as it’s of the idea that reformatting code beyond indenting destroys contextual formatting information (like Elixir’s formatter does pretty badly at times), but it can the rest of that as well, among many others.

And encourages people to just do this (telling themselves it’s only temporary, though never actually fixed):

data, _ := doSomething()

Go’s error are both super-noisy, and easily ignorable, a bad and very poorly designed combination.

Or in OCaml’s traditional way:

let read_config() =
  Yojson.Safe.from_file "config.json"

Or sans helpers:

let read_config() =
  Option.apply (File.read_all "config.json") Yojson.Safe.from_string

And you can make it stored at load-time once by just removing the () from read_config() (then it would load it at startup and use that stored result from then on). ^.^

Oh, and OCaml’s handles errors that the user of this function can then choose how to handle whether it was successful or not. ^.^

Unlike the Go example, which looks like returns garbage if it fails (no error code, no anything), OCaml wouldn’t ever allow returning garbage to even compile unless you are performing magic or so (of which you should never do, it’s like Rust’s unsafe).

Two things here, it is obvious in the Elixir and OCaml cases as well, except in the Go case you have to shuffle through all the many lines of error-handling noise (very few lines that actually relate to the problem in that) and you are left wondering what happens if it fails to convert it to json… >.>

That sounds like any language though, doesn’t mean it is actually above par for a given task though, after all look at how many places use Java. :wink:

What does this set cfg as when it returns while failing to parse? o.O

o.O Are you sure? I thought it used tabs, which is definitely superior over spaces…

Everything should only ever use tabs for indentation, spaces for alignment! That is not a hard concept yet practically every language formatters breaks on this horribly, from Rust to Elixir… >.<

Yes this, having to mentally parse through all the error handling to get to the couple nuggets of code that actually does something is painful, it’s like looking at old bad C code… >.>


Definitely one big thing go’s formatter does well!


Because it’s the only way to get reliably reproducible builds with go. Well alternatively you could fork dependencies and their dependencies into other repositories and use those, but that’s even worse compared to vendored dependencies managed by a tool. Sadly all those tools we tried do a clone of the dep and remove .git folder. We would strongly prefer a solution that would allow us to vendor via git submodules.


I mean of course the JVM, you criticize it and .Net Core above about its speed

Improve it?

This programmer is seasoned as you, nice that you suggest I lie ^-^

Go’s syntax is concise, not necessarily Go code.
You can use Interfaces and go generation for some use cases where you would use Generics. :slight_smile:


Well, first of you might retry your tests, which you had done probably a few years ago? Secondly, you might forget that Go contains a runtime, including garbage collection?

Go is used for middleware and its very good there.
C/C++ is used for more low level stuff, you comparing apples with oranges here?