loading...

What aspect of Go were you at odds with, coming from a different tech stack?

preslavrachev profile image Preslav Rachev ・2 min read

Recently, I added the Go language to my programming tool belt. Yet, as much as I want to give the language more and more chances, it hasn't really been a shiny success story so far. A lot of things in how one writes Go programs are just different. And I mean, not only syntax-wise. I have spent quite a lot of time digging into the philosophy of the language, and guiding principles of the founding team, and I have to say, for the most part, I do agree with them on 100%. Go isn't exactly a beautiful and fluent language in terms of its level of syntax sugar and this is supposed to be a feature and not a bug. Having witnessed the evolution of Java over the past close to a decade, I have quite learned to like the verbosity and ceremony. As much as it hurts to write code, it makes its long-term comprehension and maintenance much easier.

And yet, I can't make myself appreciate Go as much as I want to. There are some things that cross my principles, and I can't decide whether to believe and accept that this is an ingenious way to do things, or cross them out as utterly stupid. Like, when you'd pass an argument to a function and expect the return to be a modified version, but instead, it was the argument itself that got modified. I know that these are just things that I could simply avoid doing, but it is difficult not to stumble upon them everywhere, because they are part of what is known as "idiomatic Go".

Maybe it's just me, or maybe not. I did not want to open up an argument, but just to sort of figure out what struck others as odd, when approaching the Go language from a different domain.

Posted on by:

preslavrachev profile

Preslav Rachev

@preslavrachev

A software engineer turning entrepreneur. Might see me writing advocating about strong developer culture, or rambling about Java / Python / Go

Discussion

markdown guide
 

I like Go a lot and I think that it has helped me understand C and C++ a bit better. I really hate the verbosity of Java and C++ generally, and I don't see any advantage to it and ceremony.

As for the cons, there are cases where closures in loops and typed vs non-typed nil values can get you.

Otherwise it's definitely my current favorite language.

 

C/C++ have their place, mostly for extremely high performance scenarios like operating systems and video games. For web...pretty much not worth the effort. Go is high enough performance in most cases, and even then something like nodejs will be suitable in most web circumstances.

 

I don't do a lot of web stuff, mostly command-line utilities and such. But with Go's lack of a good GUI, I've considered using a Go webserver (very easy to implement!) as an interface.

 

The thing that got me was the extreme verbosity of sql queries, at least with the standard library solution. Take this example:

var (
    id int
    name string
)
rows, err := db.Query("select id, name from users where id = ?", 1)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
    err := rows.Scan(&id, &name)
    if err != nil {
        log.Fatal(err)
    }
    log.Println(id, name)
}
err = rows.Err()
if err != nil {
    log.Fatal(err)
}

Imagine how much you need to do if you have 30 columns to account for!

Your statement:

Like, when you'd pass an argument to a function and expect the return to be a modified version, but instead, it was the argument itself that got modified

This is only true for maps, because the value of a map is an address. Example: play.golang.org/p/KpngA5_5Te4

Notice only for maps does the original value change.

 

Slices behave that way too. The function has to be written to specifically make a copy of maps and slices if you don't want to mutate the original.

 
 

It came down to performance for me. Early on when I was learning Go, it was a bit touch and go for me as I ended up resorting back to my core languages. I started to spend more time around the Go scheduler, memory allocator, and garbage collector and was just floored at the engineering prowess that Google and the community has put into the language. If you haven't already, check out these resources:

Getting to Go: The Journey of Go's Garbage Collector
Channels
Concurrency Patterns in Go

I started to realize Go's expressive power and ended up building several tools and microservices and have had great success so far.

 

Aleksei had some reasonable critique about Go, if you can handle his argumentative style. It got many people very defensive about Go, but TBH I don't think they did a great job defending it. Nevertheless you might find some interesting opinions there.

For me personally, if a language uses products/conjunctive type to represent errors AND return values, rather than sums/disjunctive types to represent errors OR return values, I'm out. That's failing silently.

 

Quoting a response of mine from December, hoping it answers your question.

TLDR; about the odd parts: syntax sometimes, error handling, interface{}, code generation as a standard way of living inside Go's universe.

My experience with Go:

  • I didn't like it in the beginning, I read some snippets around and was balking at pointers, C like constructs, error handling and stuff about the syntax. I ignored it for sometime
  • I started using Go beginning of 2018 because it made sense for the server I was writing but I wasn't and I'm still not totally in love with it
  • I don't like either that they call package modules and viceversa, but it's just naming
  • Signed and unsigned types are there because Go was supposed to be a system level language (and some people use it as such), there are cases where you need uint8 instead of the generic int like you can use 99% of the time. I'm sure that writing a parser for protobuf or something like that, they come in handy. Also remember Go has a FFI towards C
  • the tutorial talks about for because that's what you end up using the most, in addition to if and such
  • the if name, ok := elements["Un"]; ok pattern is annoying but it's a shortcut for two lines. There are no exceptions nor there is pattern matching so you end up with almost everything returning a value and an error
  • Regarding interface I don't have much to say, it's not the perfect solution, everyone knows that, they are probably going to fix it with generics in Go 2. You don't have to deal with it everytime you write code though. A good thing about interfaces in Go is the sort of "duck typing" where if you implement all methods then you're compatible with the interface, so everything is compatible with the empty interface. This mechanism is sometimes abused by people who write massive interfaces, but that's another problem
  • I agree between var a and a :=. The latter also leads to shadowing variables. I don't like it, other people here seem to like it

Syntax is not much of a counter argument because it's subjective and that subjectivity changes with time. You list R as the languages you know, that has a hideous syntax but still, it does what it has to do.

I agree syntax is important but we underestimate our adaptability or overestimate how much important it actually is. I hate PHP syntax yet there are hundreds of thousands of people that are fine with it. I hated Go syntax by looking at it yet I spent a couple of days with tutorial and Go by Example and now it's etched in my brain and I'm okay with it. I have a vague idea about what that Elixir code is doing (because I picked up Erlang years ago, and forgotten most of it) and I honestly don't like the syntax you pasted but I'm 100% sure that if I were to spend a day with the tutorial I would be fine with it as well. Pattern matching is one of the greatest things about Erlang (and Elixir), it's just that we're not used to the declarative syntax. I think we yap too much about syntax and not enough about actual problems of this or that language (lately I'm starting to think async/await is a bad pattern but that's for 2019 :D)

If we want to talk trash about Go there are plenty of arguments: no generics (and your criticism about interface is valid, it's just a well known error in design), the error handling still hasn't convinced me 100% and it can get annoying (they are thinking of tweaking it), channels are thrown around as a panacea but they are still subject to race condition (at least the more recent articles are mindful of that), there aren't that many data structures in the standard lib and the over-relying on code generation for some libs.

In return your get a language simple to pick up (a pro or a con, ymmv), reasonably simple to use concurrently, with small footprint and easy binary deployment and fast (cross) compilation.

The author, Pike, regretted having called it a systems language in the beginning and as you noticed it was birthed as an equalizer to help experienced and less experienced developers work on the same code base. Google has that famed mono-repo taking ages to compile, they needed something for everybody which means compromise.

I don't think Go is the perfect language but it's good enough for lots of server side software. I wouldn't use it on the client side for systems development, and it seems that Rust is slowly taking off there.

Thanks for the laughs, for once I mostly agree with you ;-)

 

I guess I just don't understand the need for it. I haven't found anything that compels me to explore it. Rust seems like it would be more interesting and useful.

 

Looking at the types of tool that Go is used for on GitHub is a decent way to understand how you might "need" it. But no, you don't really need it as you probably don't need most languages :D

A few popular types of projects taken by GitHub's Go topic page: containers, static website generator, self hosted git server, monitoring server tools, web servers, distributed key value store, reverse proxy, routers, time series database, messaging platforms.

Most projects are either web or network servers of some kind :D