Hacker News new | past | comments | ask | show | jobs | submit login
The ups and downs of porting 50k lines of C++ to Go (2015) (togototo.wordpress.com)
178 points by aparashk on March 14, 2019 | hide | past | favorite | 268 comments



> Readability. I always found the Go code I encountered quite easy to read and understand, both our code and external code. Some of the C++ I encountered, in contrast, took hours to fully comprehend. Go also forced me to write readable code: the language makes it impossible to think something like “hey, the >8=3 operator in this obscure paper on ouroboromorphic sapphotriplets could save me 10 lines of code, I’d better include it. My coworkers won’t have trouble understanding it as the meaning is clearly expressed in the type signature: (PrimMonad W, PoshFunctor Y, ReichsLens S) => W Y S ((I -> W) -> Y) -> G -> Bool”.

This sentence is gold J


I don't get the "Go is easy to read" notion. Here's the recommended way to work with slices: [1]:

Delete from a slice: `a = append(a[:i], a[i+1:]...)`

Copy a slice: `b = append([]T(nil), a...)`

Pop from a slice: `x, a = a[len(a)-1], a[:len(a)-1]`

And some of these are discouraged because they leave you vulnerable to memory leaks (?)

These are fundamental building blocks and they're really quite bad. I mean when you're using `append` to delete...

1: https://github.com/golang/go/wiki/SliceTricks


Claiming that Go should excels in readability or ergonomics is pure, Koolaid fever-dream delusion.

Is it cleaner than C/C++? Sure. It's still a painfully verbose language full of boilerplate and with zero concern for expressivity or the suppression of detail [0].

Which isn't to say it's overall a bad language. It prioritizes other things over readability -- like speed, enforcing consistency, keeping its primitive set small.

[0]: https://golang.org/doc/faq#Does_Go_have_a_ternary_form

(no further evidence is needed)


Speed of compilation, not necessarily speed of compiled code. Missing generics: deadly. Passing around just interfaces? At times clearly not sufficient. Their idea of closures? Looks messy to me. Having a special syntax just for threading stuff? Totally beyond lame!

Golang is absolutely not a replacement for c or c++. It's a better replacement for the other languages out there requiring a VM to run anything. In short golang is better at cross platform than these other platforms and is IMHO good for writing good tooling. One of my colleagues said that golang now is sort of like java was in the old, old days. Making the same mistakes and needing necessary features added to become truly productive.

In my opinion ternary stuff is short handed eye candy and not a great reason to discount a language.

c++'s collections and <algorithm> fundamentally and utterly thrash anything golang claims to have, not to mention the whole stupidity around building garbage collection into a language that wants to be low level.


> In my opinion ternary stuff is short handed eye candy

Granted

> not a great reason to discount a language

The ternary quote exemplifies an attitude at the heart of the language designer's priorities. That is, the lack of ternary is, in fact, much more than a lack of ternary.


> special syntax for threading

You mean the word go?


coroutines aren't threads, fwiw


The main rationale for the lack of ternary, "We don't do control flow with symbols" should also disallow this:

    t && a() || b()
Admittedly tho, nested ternaries are tedious to parse by eye.


They're nothing against nested applications of the above, though. :-)

    t && a() || (u && b() || c())
Personally, I enjoy Rust's choice of letting `if` be an expression.


> I enjoy Rust's choice

As a Scala user, I agree it really is the best

   val a = if (t) a() else b()


That is not the recommended way to work with slices. These are novelties, aka tricks. The following is more representative.

Delete:

    copy(a[i:],a[i+1:])
    a = a[:len(a)-1]
Copy:

    b := make([]T, len(a))
    copy(b,a)
Push:

    a = append(a, x)
Pop:

    x := a[len(a)-1]
    a = a[:len(a)-1]
Enqueue:

    a = append([]T{x}, a...)
Dequeue:

    x := a[0]
    a = a[1:]
Idiomatic Go would have you not put on one line what is more clear on two.


I don’t see how that is clearer than giving operations a name.

Convince me?

I mean, I could decompose a lot of things into their more primitive representations from named functions, but when scanning a code review, I don’t want to miss a bug because someone transposed a colon in a slice operation.

It’s elegant in a minimalist way that all these things can be implemented in terms of slice operations, but why force everyone to reimplement these whenever they need to do something common that in other languages you just use the stdlib for and forget about?


There are pros and cons to be sure, but mainly I was just pointing out that the parent to my post was a bit of a straw man.

Of course you can give these operations names if you like, it's all up to you. In the form given you can account for every operation. It is easy to understand where the allocations are and where O(n) operations. There are no hidden temporaries which are quite common in other grammers.

I do often create stacks, dequeues, heaps, and other collections inline as needed. But I find this to be a lot cleaner then some other languages. It has the advantage of brevity and clarity. I can say I like this a million times over c++ template semantics, though I too am not a fan of boilerplate, so I can understand some people's resistance to this.

  var stack []T
  push := func(x T) {
    stack = append(stack, x)
  }
  pop := func() T {
    x := stack[len(stack)-1]
    stack = stack[:len(stack)-1]
    return x
  }
  
  
  push(value)
  // ...
  value = pop()


In fairness, my biggest day to day pain point of using Go isn't the oft discussed lack of generics nor it's unpopular error returns. What bothers me the most when writing Go regularly is that I'm constantly having to reimplement basic functions for working with slices. The lack of a push or pop. Even selecting the last element of the slice via the following really becomes bothersome when you start dealing with multi-dimensional arrays:

  slice[len(slice)-1]
The lack of a push and pop is a small inconvenience but it still disrupts my flow - not to mention adds unnecessary burden on testing to ensure I haven't cocked up something that should be elementary (and those kinds of errors are often a pain in the arse debug too).

The latter isn't so much a productivity problem - I just think it looks ugly as sin. Which is why I sometimes add a .Last() method to any slice types I define where I need to read the last item regularly.


Python supports slice[-1] for the last element... Pretty reasonable. Also slice[0:-1] etc. Swift also has a lot of array slice sugar, like array.last, array.endIndex, array.removeLast ... Obviously there is a trade-off to adding a lot of sugar to a language, in terms of learning the language, but leaving too little sugar in the language just leads to developers / library designers implementing the sugar themselves which leads to a whole mess of inconsistent code.


> Python supports slice[-1] for the last element... Pretty reasonable.

Lots of languages offer something akin to that. If not `[-1]` then something more OO like `.last`. Even the language I wrote supports it. This is why I made the complaint about Go.

> Also slice[0:-1] etc.

In fairness, Go can do that. You just omit the value for where you want the last (or first) element of the slice or array. eg

  b := []byte("Hello, World")
  world := b[7:]
(this would work with strings too but I'm using a byte to make it clear we're working with slices)

edit: just for clarity I mean Go has the syntactic sugar for stating the start or end of a slice - rather than a negative number of elements from the end of the slice. My point being that there is already some syntactic sugar in specifying slices so it's a shame Go doesn't take that further.


In python slice[0:-1] drops the last element. To get the same effect in Go you need world := b[:len(b)-1]. The len() needs to be there (your example is a different case).


Yes, I was the one who made the comment about Go's lack of a -1 for slices in the first place. ;)

I was actually referencing the 0 part and thus saying Go does already have some syntactic sugar when dealing with slices. ie you can do:

   slice[5:]   // slice[:len(slice)]
but not

   slice[5:-1] // slice[:len(slice)-1]
Which is a great pity.

However I'll update my previous comment to make that clearer because I can totally understand how it reads confusingly.


For comparison the c++ looks like this:

    #include <stack>

    stack<T> st;
    st.push(value);
    auto value = st.pop();
I'm struggling to see what's a million times more appealing about the go.


Marketing and hype are the only things it seems. I came to this conclusion after using golang at my current employer, and reading discussions by people online, as well as talking to them in person.


It just goes to show how golang is poorly designed as opposed to them just putting this stuff in a library, instead of polluting the global namespace with things like "copy" and "append".


> Idiomatic Go would have you not put on one line what is more clear on two.

This is the kind of silly aphorism that sounds enlightened until you think about it, and is emblematic of the bad parts of Go dogma.

The problem is that excessive LOC actually correlates strongly with poor readability, and all the cleverness in the world won't change that.


Reducing lines of code is not a free exercise since you make up for it with the length of the line and there have been plenty of studies that have shown how longer lines in written text are harder to read then shorter lines.

That said, I'm not going to argue that developer will find shorter lines easier to read than longer lines but I certainly do. However it's also not as black and white as "shorter lines are always easier for me to read" either because there are some examples where it makes more sense to have it run on as one line. for example if you're calling a function who's return is only used as the input for another function. eg

  fmt.Println(strings.TrimSpace("   some string   "))
makes more sense (in my opinion) than

  s := strings.TrimSpace("   some string   ")
  fmt.Println(s)
This is why the Go idiom says "what is more clear on two," rather than "use multiple lines even when it doesn't make sense."


> studies that have shown how longer lines in written text are harder to read then shorter lines.

As are studies showing that bugs are correlated with more lines of code.

   final var a = if foo() { bar() } else { baz() }
is much more readable and strictly less error prone than

    var a int
    if foo() {
        a = bar()
    } else {
        a = baz()
    }

> fmt.Println(strings.TrimSpace(" some string ")) makes more sense (in my opinion) than

What makes more sense is

    "    some string   ".TrimSpace()
as it allows for easier reading due to call chaining. Of course the golang standard lib decides something else just for the sake of being different, and without providing a proper reason.


> As are studies showing that bugs are correlated with more lines of code.

Sure but do those same studies look at code with the same character count (ie longer but fewer lines) or are they just subtracting characters via subtracting lines. The ones I've seen were the latter in that they basically just said "less code == fewer bugs" - which shouldn't be a surprise to anyone.

> [single line code] is much more readable and strictly less error prone than [multi-line code]

You know, had you said that 10 years ago I'd have completely agreed with you. But these days I've read so much Go code that I now find the multi-line equivalent more readable simply because that's what I'm more used to reading these days. But we're not really talking about significant margins in either case. I mean you'd expect any developer to parse either statements with ease.

I will say that I have always hated reading the ternary operator - which is the preferred way of doing single line if statements in some languages. I'd take the multi-line approach over the :? syntax any day. However from a strictly artistic perspective, the single line version with bracketed blocks is the nicest looking. So I can completely understand why people would prefer that method. Honestly, if gofmt let me, I almost certainly would write code that way too.

> What makes more sense is: " some string ".TrimSpace()

Your now arguing the benefits of OOP over other paradigms and while I agree that Go's implementation of OOP is half-arsed at best(!!!) I still have no intention of entering into that kind of ideological debate. Sorry.


> Sure but do those same studies look at code with the same character count (ie longer but fewer lines) or are they just subtracting characters via subtracting lines.

For most practical languages that are used day to day, this doesn't really matter. How often do you see Java or C# programs that have very long FP style lines?

As a matter of fact, I keep running into cases where golang code is both longer in terms of line length, and line count compared to code written in Java with map/filter/takeWhile/etc.

You can have a multi line approach and use if expressions, and get both readability (which is kind of subjective), and not needing to assign to the same variable in each branch. golang just chose to go with the most verbose and most error prone option.

    var a = if foo() {
      bar()
    } else if baz() {
      qux()
    } else {
      corge()
    }


I totally agree Go has its warts but you talk as if this is a unique property to Go. If we are going to handpick our examples then I could easily cite instances where Go requires less boilerplate code than Java and C#. So ultimately it does balance out.

I agree it would be nice if Go was more expressive but I dont agree with your points about it being more error prone nor less readable. In your example there isnt any errors that you could do in Go that you couldn’t accidentally do in the subjectively better code aside forgetting to assign to the variable, which the Go compiler would catch that anyway.

Plus all the examples of readability in this discussion are really just a few small percentages either way. If a developer can’t competently read either example then they shouldn’t be allowed near your code anyway.

Having written a lot of Go and Java (admittedly very little C# though), I’ve definitely found myself far more productive in Go. Not just writing code, but compiling, testing and deploying said code too. And at the end of the day productivity matters more to me than creating works of art in my source code. Which is why I don’t mind some of Go’s uglier syntax.

I’m sure other people might argue that they’re more productive in $LANGUAGE and I’m sure they’re also right too. The funny thing about productivity is it’s a case by case thing. Sometimes Go makes sense, sometimes it’s Java or C# or Rust or even C. Sometimes it’s a JIT language like JavaScript or Python. And some times I just need to hack something together quickly in Perl. This is why I think it pays to be agnostic rather than spending weeks of your life coming up with “reasons” to dislike programming languages.


> So ultimately it does balance out.

That's not what I'm seeing from code bases of different projects I've been working on at my current employer. It's almost fully the case that any code base for a medium sized project would end up being shorter in Java or C#, and we didn't even discuss things like annotations for validation and similar things.

> In your example there isnt any errors that you could do in Go that you couldn’t accidentally do in the subjectively better code

It wouldn't catch this

    var a int
    if foo() {
        a = bar()
    } else {
        baz()
    }
Or even assigning it to a different variable `b`.

> in this discussion are really just a few small percentages either way

They add up quite quickly in any sizeable project. It's not just one offs here and there, you end up with functions littered in the code base that distract from quickly getting to understand the code when debugging a bug or an outage.

I found compile times for golang (building the entire project + unit tests) to be about the same as Java, meaning all this hype about fast compile times in golang to be just that. Not to mention that exceptions are far superior when debugging issues.

I agree, depending on the situation some languages would do better than others depending on the task. The thing is, golang seems to be used just because it has Google as a name behind it, not for its technical merit (and the vast majority of critical infrastructure at Google almost surely is still C++ and Java). This shows up time and again, as people are not able to defend the technical choices behind it in any proper way, more like "the golang authors are smart and they decided to have it that way", which has already been shown several times that the golang authors are quite bad at defending their decisions, and they're not experienced at language design to begin with (C is a bad language by modern standards). Golang would probably be fine for some small tools here and there (even for that, Rust or AOT Java would be a consideration), for anything larger, a static JVM or .NET language is strictly superior.


> It wouldn't catch this

True but you could forget to do that in your alternative example as well (albeit you'd get no instances of a assigned rather than sporadic instances - however the point of testing frameworks is they should catch that kind of human error regardless of the language or it's syntax)

> Or even assigning it to a different variable `b`.

Actually the compiler would catch that too.

> They add up quite quickly in any sizeable project. It's not just one offs here and there, you end up with functions littered in the code base that distract from quickly getting to understand the code when debugging a bug or an outage.

I guess it depends on what you're used to but I've always preferred code that abstracted away longer or more complex functions into smaller ones. I guess that's the FP habits in me transferring over to imperative languages. In any case, I've never had any issue debugging such code when under pressure during an outage.

> The thing is, golang seems to be used just because it has Google as a name behind it,

That's complete and utter BS. In fact I had already cited reasons I like Go. so please don't assume that because you don't like something then it's automatically a bad language that other people can't like either.

> This shows up time and again, as people are not able to defend the technical choices behind it in any proper way,

People have and they do though. The issue is that there is a hell of a lot of language elitism in the the different programming language cults so it doesn't matter what reasons we might give - you've already decided you don't like feature x and thus our reasons are wrong by default. You demonstrated just this kind of mentality when you argued that people use Go just because of Google in a reply to a post that lists why someone likes Go.

> which has already been shown several times that the golang authors are quite bad at defending their decisions, and they're not experienced at language design to begin with

The arrogance in that comment is overwhelming. I look forward to the day you create something worthy of recognition and the inevitable backlash on HN from kids who assume they're smarter then you are in every way. This isn't me putting Russ Cox et al on a pedestal - this is me pointing out that talk is cheap and most of the critics on HN are just average engineers at best.

> Golang would probably be fine for some small tools here and there (even for that, Rust or AOT Java would be a consideration), for anything larger, a static JVM or .NET language is strictly superior.

Clearly not everyone agrees with you there because a lot of successful important projects are now based on Go.

You need to learn that features you prioritise are not going to be the same as features others might prioritise - and that having different priorities doesn't make one party wrong and another right.


> True but you could forget to do that in your alternative example as well

Not really, unless I'm misunderstanding you.

    var a = if foo() { bar() } else { }
will (depending on the language) either not compile, or the type of `a` will be inferred to a common type of `typeof(bar())` and `None` (perhaps some `Any` type). And this would almost surely cause a compile time issue the moment you use `a` as the type you expect. The other way is to simply write

    final int a = if foo() { bar() } else { }
and that would immediately not compile.

> Actually the compiler would catch that too.

Not quite:

    b := someFunc()
    var a int
    if foo() {
        a = bar()
    } else {
        b = baz() // meant to assign to a
    }
> In fact I had already cited reasons I like Go.

I was talking about why the language became somewhat popular, not about why some people may like it or not. Some of the golang authors already worked on a predecessor to it, but it never got anywhere, because they were't at Google at the time, so it didn't have the Google brand behind it.

> People have and they do though.

Like the "arguments" about how "null pointers are part of the computer" or similar nonsense about why they included nullable by default pointers? Or that compile checked enums are not really useful because you can just check them yourself, etc.

> this is me pointing out that talk is cheap and most of the critics on HN are just average engineers at best.

This is just diverting now. You don't have to be a brilliant PL person to see what's good and not in the area, after using different languages in production and finding out what features are useful, and which aren't, and seeing others struggling with issues already solved in different programming languages and environments, or people writing hacks to get around the limitations of the language, ending up with more complexity than if the language supported those features to begin with.

> Clearly not everyone agrees with you there because a lot of successful important projects are now based on Go.

There are large projects based in C and assembly as well. This is a non-argument. I think it was kubernetes that was notorious for using large amounts of auto generated code because golang doesn't have generics. Not to mention that if you want to do unit testing you end up with a large mess of auto generated code to do mocking, an issue that's solved in other languages a long time ago.

It's just that people repeat mistakes instead of learning from them, and easily follow fads and are prone to claims that are not strongly backed. golang is another manifestation of this.


> Not really, unless I'm misunderstanding you.

You are misunderstand me. I'm saying you could type the following by mistake:

  if foo() { bar() } else { baz() }
Which is the equivalent of the human error you were demonstrating in Go.

> Not quite: b = baz() // meant to assign to a

Actually yes. You'd get the following error:

  b declared and not used
The go compiler checks for variables declared and, well as the error message literally says.

Go's compiler is actually very good. The error messages are descriptive and it's strict enough (though some say too strict) to catch a whole plethora of silly user errors.

Sure, other compilers have this too. But the only one I've used (at least recently) that betters Go is Rust.

But of course you should already know this because you're an expert in Go. ;)

> I was talking about why the language became somewhat popular, not about why some people may like it or not.

The two are mutually inclusive. The language is popular because people like it. Again I'm forced to reiterate the question: "Why do you find it so hard to accept that other people might like something you don't?"

> Like the "arguments" about how "null pointers are part of the computer" or similar nonsense about why they included nullable by default pointers? Or that compile checked enums are not really useful because you can just check them yourself, etc.

Clearly I'm not going to agree with every defensive point ever made about Go but just because you can cherry pick a few of the more absurd examples doesn't mean that the well reasoned ones aren't valid.

> This is just diverting now. You don't have to be a brilliant PL person to see what's good and not in the area, after using different languages in production and finding out what features are useful, and which aren't, and seeing others struggling with issues already solved in different programming languages and environments, or people writing hacks to get around the limitations of the language, ending up with more complexity than if the language supported those features to begin with.

That wasn't what I was commenting on. It was the comments about how people who are clearly more experienced than you seemingly know jack shit in your outspoken opinion.

You're just letting your arrogance get the better of you by making defamatory and clearly untrue comments about your own peers and developers with likely decades more experience in designing programming languages. I'm just pointing that out.

You don't have to agree with the design of Go but calling them incompetent and stupid is clearly taking things too far. In fact it completely undermines your own credibility.

> There are large projects based in C and assembly as well. This is a non-argument.

And C and assembly are well suited for some specific domains as well.

> This is a non-argument.

Your argument is that Go isn't suitable and I was evidencing how it is used in the very fields you assumed it couldn't. That - therefore - is the very definition of a valid counterargument.

> It's just that people repeat mistakes instead of learning from them, and easily follow fads and are prone to claims that are not strongly backed. golang is another manifestation of this.

"Mistakes" as defined by your own binary view of programming languages?

"fad" defines something as short lived and Go has long since surpassed that stage.

I don't know if your comments are driven by insecurity or over confidence but, honestly, your attitude here is pathetic. I never set out to try and convince you to use Go - just to try and help you understand why some people like to use it. But since this is clearly just an exercise for you to assert your own arrogance onto the entire Go community - I'm going to quit the debate.


> You are misunderstand me. I'm saying you could type the following by mistake:

  if foo() { bar() } else { baz() }
Then there wouldn't be a variable assigned to use in the first place. This is not the issue at hand here.

> Actually yes. You'd get the following error:

  b declared and not used
The first line I wrote declared and assigned to b, but in the if/else statement, b was overridden, so the error you mentioned won't happen, it will simply get overridden and a bug would happen.

> "Why do you find it so hard to accept that other people might like something you don't?"

It seems most people don't realize the other advances in languages designed to reduce errors, which golang discards on purpose and without good reason, and they just accept the hype and marketing around golang. You keep hearing that golang's gc is the best gc around and things to that extent, clearly incorrect nonsense.

> Your argument is that Go isn't suitable and I was evidencing how it is used in the very fields you assumed it couldn't.

The argument is that you can use any (badly) designed language to write large software, it will just end up costing more effort and resources (e.g. see javascript or large programs written in ruby and python, they're doable, but with a very large cost).

Using things better suited for the task at hand is important, especially seeing how software these days is not up to par. Then you have another badly designed language (golang) that decides on purpose to ignore research done to improve software reliability, it's not a surprise things the way they are.

> It was the comments about how people who are clearly more experienced than you seemingly know jack shit in your outspoken opinion.

Being more experienced doesn't mean not falling into a closed way of thinking, which clearly shows if you follow the golang authors and their history. This is an appeal to authority fallacy.

> And C and assembly are well suited for some specific domains as well.

That's not the point of discussion. The point is that for practically anything other than some small tool type work (even there it's arguable), there are other strictly superior offerings.

> to try and convince you to use Go

I already use it at work, that's how I'm aware of it's enormous shortcomings, by using it myself, and hearing and seeing how others use it and complain about it as well. That's why it's a fad, the company is quite invested in it now so it's difficult for them to switch, so they have to find hacks to deal with its shortcomings.


oh geez, this whole conversation is a depressing read.

@apta I've noticed every single comment you've posted on this topic has been a rant at Go. It really sounds like the issue is you rather than the programming language. If you hate your job that much then go work elsewhere. If you're the expert developer you claim to be then finding other work shouldn't be hard for you and the upside (aside better job satisfaction and potentially more money) is you then feel less compelled to shout at people on Internet forums.


It's also not even true, given that `if foo, err := bar(); err != nil {` is considered idiomatic over writing that over two lines.


Both are idiomatic. I use the two line version.

I don't like this form both for legibility, and the fact that it closes over the return value.


They are not the same though. I got bitten by this recently (Go beginner).

In the single line form, both foo and err are scope restricted to the if block. Found that out the hard way.


This. Code density matters!

Excessively low or high density are equally injurious to readability.


I disagree strongly, at least as far as my intuition is concerned.

it's not code density that matters, it's "context density". Long drawn out functions are usually not hard to read because they are long, but because it's not clear what they are about. It's lack of conciseness on part of the programs logic that makes programmers lose focus, not code density.

As an example, think of a few fairly long switch statements or pattern matching logic. It's non-dense code depending on your language, but not hard to understand even if long. In contrast, some convoluted Haskell syntax with weird operators you've never seen is almost mentally incomprehensible.

On these terms it's true that Golang is not 'text dense' but it doesn't matter, it stops semantics from being overly complicated, and it encourages simple structures that don't go overboard on condensing information.


No cramming code into one line is never a good thing when you can make it clearer and easier to read by using 2, 3 or 4 lines of shorter more readable code.


LOC also correlates with bugs. This has been shown in studies on more than one occasion.


> Idiomatic Go would have you not put on one line what is more clear on two.

Each operation was just clearly described with a single word; I think it's possible to describe them clearly with a single line


Whatever the "proper" implementation of these slice primitives is, for me the real problem is that, due to the lack of generics, one can't write a utility library that provides simple names for these primitives.


that's still not great.


Deleting from a slice is ugly and awkward because it's slow. Go, like C, confronts you with the real cost of what you want to achieve. A builtin like 'delete(a, i)' looks like an O(1) operation, when in reality it's O(n).


In what way does delete(a, i) look like an O(1) operation? It looks like a function call. You can never just look at a function call and see its complexity.

You make it sound like deleting an element being ugly is a design choice, but it can't be a choice since there is no alternative. Swap-and-pop is O(1), but it's ugly too.


> A builtin like 'delete(a, i)'

Ironically, "delete" is built in in golang (how else do you delete an entry from a map)? Yet it says nothing about its cost.


Yet copy is available.


A more developer-friendly language like Swift would give you an ArraySlice type with performant views on arrays, and developer-friendly syntax.


I find Go hard to read because it is so monotonous (and a bit ugly) that I cannot concentrate.

For me, aesthetics matter, so my readability preference goes something like {Lisp,ML,Haskell} > C/C++ > Java > Python > Go.

C is actually quite high on the elegance level. C++ is only that high when written by people with self-restraint.

Rating Java above Python is not a mistake if you consider larger code bases. Also, Python suffers from some of the monotony problems, especially when there are many conditional statements.


But then:

I’m confident that a C++ translation of the new Go code would be less than half the LOC of the Go version and more type-safe, due to C++’s polymorphic functions and types. A Haskell rewrite would need even fewer LOC, and if I’d been allowed to write it in Clojure I suspect the whole thing could have been expressed in fewer than 1000 lines of macros


Sounds like there's both ups and downs.


> My coworkers won’t have trouble understanding it

This very much depends on your coworkers, and on the problems you solve.


Indeed if you actually are using Haskell in production, there's no way your coworkers won't be able to understand a simple signature with three classes. They probably aren't Haskell beginners. Please, have some confidence in your coworkers' intelligence and reasoning ability.


The issue isn't understanding the signature per se. The point is that type signatures aren't always enough to figure out what a function does.


That’s a good point, but often a type signature constrains the possible programs to such a degree that there is only one nonsensical program that can be written with that signature.


That's rarely the case for effectful code, which is where you'd typically see multiple monadic type class constraints.


Coming from JVM languages, it drives me nuts that I can't .map/.filter/.foreach/.reduce etc. my collections, especially with something like streams/sequences. That's where you really notice the lack of generics if you're used to this sort of programming.


Agreed. I was just bitching about that on twitter.

I was just working through some code to check for the intersection of two lists and use the result to iterate though. Creating new slices to put values in with loop in loops is not a productive use of my time.

I don't really want generics personally, I would just like some magic methods on top of the existing slice/maps in Go. They already are magic in the way they work so why not provide those few helper methods and solve a massive pain point I have with Go.


What magic? Go is very unmagical, and that’s the way it should be. If you need to merge two lists you can just write a method that does that. Publish it as a package and reuse it. In Go algorithms should be apparent. Go is a code heavy language, not data heavy. You should see and understand the real cost of your solution.


Well, if you had generics, you could do that. Without generics, you get to either (a) write an efficient method that merges 2 lists of Foos, which is not really package-and-reuse material, or (b) write a horribly inefficient method that merges 2 lists of some interface type, forcing your users to copy their data to new slices every time (since an []x is never an []y).

Faced with these 2 wonderful options, most people simply decide to copy or write again the same piece of code...


There’s also option C which is better than b (but still awful) which is to write a method that takes two interface{}s and uses reflection to figure out their concrete types at runtime and merged them. No type safety, unidiomatic, decently ergonomic (assuming you panic on type errors), and probably reasonably performance for large slices.


Copying is encouraged. Like I said, having the algorithm there is a good way to make it apparent what the code is actually going to do and the associated cost of the solution.

interface{} could be avoided by having an interface that reflects the behavior you actually need from the types involved in the algorithm. If you need to only test for equality, you could have an algorithm that works on []Eq. That is one of the downsides to Go, unlike Haskell, interfaces are usually defined by the consumer, which makes negotiating common interfaces like Ord and Eq difficult.


>Copying is encouraged. Like I said, having the algorithm there is a good way to make it apparent what the code is actually going to do and the associated cost of the solution.

In 2019, having a generic implementation of the algorithm is also "a good way to make it apparent what the code is actually going to do and the associated cost of the solution" in most languages.

On top of that, it helps you have less code (easier to check, code is a liability), skip manual text-template-generated code pre-process steps, let's you code what you know you want without a context switch to copy something that you've already written for another type, and stop bugs stemming from e.g. changing things in one implementation of the same algorithm and not another (because you're forced to have 10 implementations for different types).

And copying has been an anti-pattern since the times of Algol. Except if it's replaced by the "wrong abstraction" prematurely. But nobody ever called having a generic version of an algorithm "the wrong abstraction".


I know Go encourages copying code, but that is a well known anti-pattern in all other language communities,one of the few with some research behind it (as someone else mentioned, basically the only quantitative science we have on software engineering says that more code implies more bugs).

The idea of []Eq sounds nice, but again, the problem is that I can't pass an []X to a function that wants an []Eq, I need to build a new array and copy all my elements over. This is correct from the type-theory point of view (as slices are not covariant) , but of course Go doesn't offer anything else that would be more user friendly and still correct, such as a covariant read-only slice that we could use instead.


> Copying is encouraged. Like I said, having the algorithm there is a good way to make it apparent what the code is actually going to do and the associated cost of the solution.

It's also a good way to make bugs proliferate throughout your code base. IIRC bug count correlates well with line count regardless of the language(!).


Copying does wonders for cyclomatic complexity.


>Go is very unmagical, and that’s the way it should be.

Go is full of special blessed functions and ad-hoc handling because of the lack of generics.

>If you need to merge two lists you can just write a method that does that. Publish it as a package and reuse it.

Only it wont work in any other type of list contents (or it will revert to interface{} and lose type safety and speed). Which is the parent's point.


He means the magic where built-in containers magically get to be generic, where custom containers do not (because the language didn't support it).

Contrast that with C++ where all containers are implemented in C++ without cheating.


> You should see and understand the real cost of your solution.

Then write it in assembly and damn all the abstractions. /S


Yes, go seems to come from a mentality that “excess abstraction is evil” where “excess abstraction” is defined as “any pattern enabled by a language feature developed since the ‘80s”.

It’s madness. When writing application software in any language, you’re already umpteen levels of abstraction away from what’s really happening under the hood. You call a library that calls a library that calls another library over FFI that itself dynamically links to a function written in another language that’s all compiled down to object code that runs in little tiny timeslices chosen by a scheduler in an operating system that abstracts away the hardware running on a CPU that actually rewrites instructions using microcode to actually run on an internal RISC core but one more level of abstraction on top of all this is apparently where everything will go wrong.


Go isn’t Java. It’s easy to bring our biases in from other languages, but when you use a language you should be buying into the philosophy, conventions, and style of the language as much as the rules of the language.

Go focuses on code, not data and concrete solutions, not generic ones. It’s a language designed for the 90%. If that doesnt fit your requirements, it’s ok to use C++, Haskell, or Java, or some other equally good language. But Go is really, really good when you give it a fair chance.


>Go isn’t Java. It’s easy to bring our biases in from other languages, but when you use a language you should be buying into the philosophy, conventions, and style of the language as much as the rules of the language

Only if those make sense. "The philosophy, conventions, and style of the language" could very well be BS.

Take Java for example: the mid-2000s J2EE code with the endless abstractions, XML everywhere, patterns galore, etc, was accepted and used as the default Java "idiomatic style", but it was understood later to be cargo cult BS.

Similarly, Java didn't have Generics either pre 1.5 iirc. One could say like you now, writing without generics is part of the "philosophy, conventions, and style of the language".

But it wasn't. It was just a missing feature. Now that Java added it, nobody feels it was better before, and nobody feels like they're sacrificing some part of the "style of the language" for using it.


Java has had that feature for.. 3 years? out of 20?

This is not about Java, this is about a multitude of languages having these features and Go not having this is a valid criticism.

I'm not saying you're wrong that there might be good reasons for it to be focused on different things, but I didn't read OP as "meh this is not Java" at all.


No, since 2004. So, for 15 years out of 20 or so.


.map/.reduce etc? Really? Either we had a misunderstanding there or I am plain wrong :)

I was talking about streams in Java 8, so ok - 2014 is 5 years ago.


He’s talking about generics, you’re talking about streams.

Though ML was invented in 1973, so the idea of map/fold is pretty old.

The generalization into monoids/monads/functors didn’t happen until the early 90’s.

If we want to talk about the JVM, Scala was created 15 years ago and had those features.

I respect K&R, Rob Pike, C, and Unix a lot, but you have to understand that even when they were in their prime, they were considered a backwards. This can be traced toward the New Jersey style vs MIT style [http://dreamsongs.com/WorseIsBetter.html]

Not surprisingly, the New Jersey style originated at a company, Bell Labs, and while MIT style, not surprisingly, originated at... MIT.

But at this point, Go just feels antiquated, designed by a guy who strikes me as a “get off my lawn” kind of person.

Personally having had to transition from Scala to Go for one project at my old company, it felt like I was programming “blind, deaf, and dumb.”

Things I could do in 2 lines of Scala (though those lines were admittedly dense), would literally take hundreds of lines of Go. Sure, maybe it would have taken even more lines of C, but not much.

Go is probably just a tad more expressive than C with boehm GC. And frankly, that’s unexcusable for a language created in the 00’s, much less 2009.


I worked with Scala and just the compile time / deployment / SBT nightmare was really a bad experience, I'm glad I don't have that kind of problems with Go.


Really, it was implemented with anonymous inner classes, and popularized in libraries like Guava.


You're right - I forgot about this. I still don't think it's a valid point in this part of the discussion. Guava was/is widespread as far as I know but it's far from "everybody uses this" and while I've not worked in many Java projects, I've not seen much of this functional style. The people who wanted this badly switched to Scala. The features don't seem to be inherent, or at least not readily available to the language itself.


Aha, I was talking about generics.


This is a cheap way out. I don't think most people are putting down Go as an outright terrible language.

So, when so many engineers with lots of experience say that, for instance, no-generics is a problem with Go, it's worth considering that may be it really is a problem.

I wonder what you will say if Golang introduces generics in a future release.


> I don't think most people are putting down Go as an outright terrible language.

Beware of evaporative cooling. All the people who can't put up with a lack of generics will wander off, and you'll only be left with people who are okay with the status quo. That doesn't mean you don't have a problem.


Well, they are about to introduce generics, so...


No, not quite. Rob Pike said in a video about a year or so ago that now they have enough experience to start thinking about generics. That the process is now just starting and you shouldn't expect them any time soon.

The Go core team has to decide if there's a way to add them to the language in a "Go way." If they can't they won't be added to Go at all, despite what other people want or don't want.


They started making early proposals, with syntax and the associated semantics. It will take a long time, but I'd be very surprised if they changed their mind now.


> I wonder what you will say

"People were right in a wrong way"


Map, filter and reduce are for the 90% though.


Map filter and reduce are generic tools that can be accomplished through constructs Go already has. The point of Go is that most applications do not need a high level of abstraction, they are bounded problems. Go is also reflecting the Unix philosophy, no surprise given its authors, of building small focused tools. Therefore framing problems in terms of generic data transformations facilitated by mapping and filtering is drawing attention away from the concrete solution and making the code less clear.


I would actually argue that map and filter are perfect examples of unix-style, focused tools. They are the programmatic equivalents to sed and grep. Of course more specific tools can be used, such as cut or tr, but in most cases sed is fine and you just go with that. I definitely understand people wanting the same ease of use, even as a Gopher myself.

Reduce is a bit different though because the closest equivalent on the cli is awk with a BEGIN and END block, so it's a bit more high-level. But there is still value in it.

The thing is most people who complain about the lack of those functions are not looking for fancy transformations: in most cases you want something as simple as "extract a field in each struct" or "keep if this field has this value". There even is sort.Search in the stdlib that has something in the same vein; surely something like

    filter.Filter(n int, f func(I int) bool) []int
could be useful?


>Map filter and reduce are generic tools that can be accomplished through constructs Go already has.

They can also be accomplished through constructs Brainfuck already has. Actually any Turing complete language will do.

>Map filter and reduce are generic tools that can be accomplished through constructs Go already has. The point of Go is that most applications do not need a high level of abstraction, they are bounded problems.

map, filter and reduce are not "high levels of abstraction" any more than for or channels are.

>Therefore framing problems in terms of generic data transformations facilitated by mapping and filtering is drawing attention away from the concrete solution and making the code less clear.

The "concrete solution" being copy pasting code with a different type to filter a list for your types, and using a for loop instead of filter or map? Sounds more like Stockholm Syndrome.


It is a pretty common feature of languages since smalltalk. IMO when people say go is missing generics, this is what they are referring to.


I can’t really see how it can fit anyone requirements given that you said in another comment that it encourages copying code around. That is the only sure way to ensure that your code is full of bugs and multiplies the hurdle of the maintenance for each of the copied implementations.


And yet in practice Go is delightfully robust. It’s not THE most robust—it’s not Rust or Haskell[^1]—but (in my experience) it’s certainly less buggy than Java and friends, C/C++, or virtually any dynamic language. It’s quite possible that copying is a liability as you claim, and that OOP, generics, and/or dynamic typing are simply greater liabilities, but I think that means you should evaluate languages on balance and not on the presence or absence of one feature or idiom.

[^1]: Rust and Haskell pay for their marginal increase in robustness with a massive decrease in productivity.


I come from JVM as well.

https://play.golang.org/p/kbmrIvoQBXs

If you want true streams and don't mind the performance hit, you can certainly design a library to implement those semantics on top of Go channel types.


<deleted as I misunderstood the parent, thanks for pointing it out>


You can do it in Java. I think GP is talking about the inability to do this in Go.


funny thing I wrote this on the morning commute to work - https://twitter.com/chewxy/status/1105962162021756928

Not that I'm saying that Go should not have functional features - it does, but it also forces you to think more deeply on things.


Check out my experiment with adding map/filter/reduce to Go: https://github.com/lukechampine/ply

Ultimately I concluded that map/filter/reduce really need to be paired with a concise lambda syntax in order to be ergonomic. Writing out a full func() literal doesn't feel much cleaner than just writing a traditional for loop. However, adding a new lambda syntax would be a much larger undertaking than I can manage right now.

The other thing I concluded is that you can highly optimize map/filter/reduce chains if they're first-class citizens of the language, rather than living in a userspace library. I was able to compile such chains into for loops that are more efficient than what a novice might write, and no worse than what an expert would write. The only way to improve would be to inline more aggressively, i.e. inline "x += 2" instead of calling out to "addTwo()" on each iteration of the loop.

I think there's an unfilled niche in programming languages that's basically "Go plus some FP niceties." I love Go's simplicity, tooling, and performance, but writing endless for loops and error-handling blocks gets tiring and can obscure the operation you're actually trying to achieve. At the same time, jumping to full-blown generics, macros, etc. like Rust introduces too much "magic," hurts readability, and bloats compile times.


> I think there's an unfilled niche in programming languages that's basically "Go plus some FP niceties."

Nim?


Rust actually has a lot of Gos spirit in terms of being practical and consistently engineered, but also embraces FP. The issue is that Go is philosophically tied to concreteness. You solve your specific problem each time from scratch. FP is about raising your problem to a higher level of abstraction and solving it there. In Haskell this is you taking your domain and raising it into a problem of category theory, and then using all the nice things we know about transformations in category theory to solve the abstract problem and lift it back down to earth, such that we can take advantage of any sort of mathematical, general solution. so in a sense Go + FP is like adding two diametrically apposed philosophies.


I get paid for haskell and I don't use category theory. No one in my team does and neither do a significant share of the people in the industry.

I don't know what outsiders think about us, but 90% of the value derives from the simple things : constructing simple datatypes easily, a world-class type checker and some great libs that require little knowledge to be used, like Servant or Aeson.

Sure, there are fancier things if you want to go beyond that, but you can be productive with the bare minimum of Haskell, as Elm shows.


> FP is about raising your problem to a higher level of abstraction and solving it there

Haskell and lisp are like that for sure, but more blue collar FP languages like erlang ate "basically FP so that you get memory safety and easy multithreading".


F#? Runs on .net, interops with the entire .net ecosystem, has high performance garbage collector, if you need high performance you can use stack-allocated data structures.


Nim is much better than "Go plus some FP niceties.". Faster compile and run speeds, clean Python-like syntax, lisp-like macros, no runtime required, can compile to JS.


I personally love Rust and think Rust is the future for C and C++, and use it professionally. But Go is simple, fun, fast, and easy to read and frankly more approachable by the people many companies can afford to hire.

I view language choice as a line. If I need to build big, or build niche, or build fast, I use Rust. But if it’s general purpose I use Go. I can write Go faster than I can write Python because the type system gives me a fast feedback loop and the language is simple and builds in the common case building blocks. I can get better performance and understand my app better than with the JVM because it’s compiled ahead of time and I already know the ins and outs of Linux. There is no black box JVM that consumes resources while idling.

Go has advanced the state of formatting, documentation, and revitalized the Unix philosophy of building small, focused pieces without getting caught in the clouds over abstracting.

If you are currently using a Python, Java, C#, or JavaScript, Go is better. Just don’t post angrily to your blog until you’ve written Go while also embracing its philisophy and taking the time to understand the authors intentions.


> using a Python, Java, C#, or JavaScript, Go is better.

I’m not sure about C#.

If I saw that comment couple years ago, I would mostly agree. But with modern .NET, first-party Linux and OSX support has arrived. Also better performance, because spans, value tuples, and other modern stuff that was the main reason why .NET core diverged from slower evolving Windows-only .NET framework. The runtime is deployed with a single tar -xvf command and is quite small, e.g. for ARM Linux the gzip is 12MB.

Async-await style IO is comparable to goroutines. Multithreading is similar.

GC is not as bad as in JVM, in part because value types don’t use GC.

The language is higher-level and more expressive, with generics, LINQ, metaprogramming, etc. The amount of third-party libraries useful for network services and similar things is quite comparable.


If you are currently using Go, Kotlin is better. Just don’t post angrily to your blog until you’ve written Kotlin while also embracing its philosophy and taking the time to understand the authors intentions.


Where does Windows development fit into the Go way of doing things? Is Windows a first class citizen for Go?

Python, Java, C#, C, C++ are all very well supported in Windows.

P.S. I don't have any hidden agenda with this comment - genuinely curious.


Windows is very much a first class citizen - just so long as you're writing CLI tools or web services.

Unfortunately writing native GUIs in Go is something that isn't well supported - irrespective of the host platform you're targeting.


There's no API for GUI in the stdlib.

There are several third party options.


Agreed. It is probably also the perfect introductory language to introduce someone to programming with while also being extremely useful even for complex programs.

I think an intro to CS course would be a lot more fun for everyone involved if it were taught in Go rather than Java like it did in back in my college just half a decade ago.


> I can get better performance and understand my app better than with the JVM because it’s compiled ahead of time

Ahead-of-time compilation does not mean better performance.

Java HotSpot has a history of outperforming .Net, despite the latter using ahead-of-time compilation.

Similarly, various JVMs use ahead-of-time compilation, but are generally unable to outperform HotSpot.

Java's performance pitfalls are in areas like its slow reflection (or rather, in the way reflection is overused in Java libraries), its costly runtime checks for null and out-of-bounds array access, and boxing of primitive types.


Do you/anybody know of any solid project that aims at porting/re-developing in Go at least some of the statistical learning tools that we have in python? I did not find much when I looked last time, but it might have been more than a year ago.


There's gonum: https://www.gonum.org/

However, I endorse the view that Go in its current form is a poor choice for that task, and that even after generics are added to Go, that will merely make it an at-best-average choice for that task, and personally, if that's my primary goal, I'd want to choose something excellent at that task. The design decisions made in Go tend to cut actively against heavily mathematical usage. For instance, I agree that no operator overloading is the right choice for a systems language, but it's a terrible choice for a math language.


Go is at this point used for tools and containers. Maybe back-ends.

Trying to use it for data science is a waste of ones time, the language is particularly poor at expressiveness. Any of the allegedly worse languages above (C#, Java, Python) are better at this.


Go is definitely worse both in expressiveness and in numerical manipulation, so I'd say data science is the last field you want to use Go in. And I sat that as a practitioner of the language.


Switched from Python to Go about 2 years ago. If you need to build performant APIs with a small team there is nothing that comes close. You get the productivity of something like Python with the performance of C++. Or at least very close to it in both cases. (https://getstream.io/blog/switched-python-go/)

The lack of generics hasn't bothered me so far. You just find different solutions to the problem. Composition, interfaces, or if all else fails an empty interface{}. It's rare to really not be able to work around it. It does make it near impossible to build reusable and performant data structures in Go though. (say that you want to build your own heap or list, hashtable etc.) So that's annoying, but something that you rarely run into.

Bigger issue is that GO modules are still not ready for production since the ecosystem didn't catch up: https://github.com/golang/go/issues/24661


You don't get the performance of C++, and it's irresponsible for saying things like that. Any benchmark that's not cherry-picked will show c++ is faster.


Go map vs C++ maps, they pretty much the same for Go 1.9. In Go, once you instantiate a map with a length, you can avoid GC and memory allocs as it grows. https://medium.com/@griffinish/c-and-golang-neck-and-neck-on...


Did you read the article?

>Then of course I remembered that std::map is a red-black tree, i.e. ordered, whereas the Go map is not. Change out std::map for std::unordered_map and C++ pulled ahead, 20% faster than the Go code.


Even that's surprising, since std::unordered_map is notoriously slow (and impossible to optimize given weirdly exposed implementation details in it's API)


So I created a Gist: https://gist.github.com/AshKash/ebf1f70949e76439d6ffed9ccde3...

upped it to 10M ops and used .reserve to avoid unwanted allocs.

Go 1.12 is 2X faster than C++.

This was totally unexpected as I feel the basic algorithms are simple and there is not much you can do to make it faster.

If you have know what is going on, please share.


And Java is 2x faster than golang in this benchmark when I ran it. Many benchmarks are to be taken with a grain of salt.

golang: 7.76 real 8.67 user 0.08 sys

Java: 3.74 real 3.58 user 0.46 sys

    public class Test {
      public static void main(String[] args) {
        final int numOfStrings = 100_000;
        final int numIters  = 1_000;
        var numStrings = new String[numOfStrings];
        for (int i = 0; i < numStrings.length; i++) {
          numStrings[i] = Integer.toString(i);
        }

        for (int i = 0; i < numIters; i++) {
          var strToInt = new java.util.HashMap<String, Integer>(numOfStrings);
          for (final var s : numStrings) {
            strToInt.put(s, s.length());
          }
        }
      }
    }


What's happening is that the std::unordered_map was a mistake. Many parts of it's API prevent implementing it efficiently (For example, every insertion requires allocation, even if you reserve). It's widely regarded, within and outside the standards committee, as a very unfortunate mistake.

swisstable (absl::flat_hash_map) is a popular choice right now for the hash table to use for C++ code that cares about performance. There are other choices as well (dense_hash_map, F14FastMap, etc), But honestly most choices these days are worse than std::unordered_map (well, boost::unordered_map is probably just as bad, since std's implementation was taken wholesale from boost without any thought!)


Did you read the update on Go 1.9? It is on par, I tried the benchmark on my machine why not try it yourself instead of downvoting?


You aren't reading it correctly. The OP was saying it's not fair unless you compare against an unordered map. The update is referring to an ordered map which is much slower than an unordered. The GO math implementation, which is an unordered map, is still significantly slower than the C++ unordered map.


No, it's fairly clear that the update refers to the unordered maps case.


If you're going to compare performance of C++ maps, you may want to consider std::unordered_map, which is a hash map implementation vs std::map's red-black tree.


Not even considering that unordered_map is not the fastest hash map implementation for C++. There is plenty of the flat hash map implementation (swiss tables) around that beats unordered_map by an other factor....

https://abseil.io/blog/20180927-swisstables


Not only consider, but comparing a Go map to C++ absolutely has to do that since go is unordered. A c++ unordered can be significantly faster than ordered in many contexts.


I can't believe the author still published that article even after he realised he was comparing different map data structures.


> You get the productivity of something like Python with the performance of C++. Or at least very close to it in both cases.

s/very close/close enough/

C++ is still multiple times faster in many/most situations [1]. And while it's hard to measure productivity, it's nearly impossible to beat out-of-the-box productivity Python offers [2].

But do people need the performance of C++? Usually not. They just need something faster than Python/Ruby that uses less memory than Java.

And do people need the productivity/prototyping of Python? Often not. Most work is on established code bases where static typing (even weak static typing) is a huge benefit.

Go is full compromises, but in ways that turn out to be very pragmatic.

[1] https://benchmarksgame-team.pages.debian.net/benchmarksgame/...

[2] Though if you're making a TCP or HTTP server, Go is pretty great. For a CLI, Python stdlib argparse is unparalleled.


> And do people need the productivity/prototyping of Python? Often not. Most work is on established code bases where static typing (even weak static typing) is a huge benefit.

You can change "productivity/prototyping", remove productivity and just keep prototyping.

Dynamic typing like python has has nothing productive.

Dynamic typing means replacing few minutes to fight against the compilers errors by hours of debugging in runtime/production of problems that could be avoided easily with typing.

That a problem that appears very qickly as soon as you leave the world of "quick and dirty prototype".


> as soon as you leave the world of "quick and dirty prototype".

There's a lot of things though. Want to create a set of charts and graphs of data, especially in a repeatable way? Nothing better than matplotlib.


I have really mixed feelings about Go. 2-3 years ago we went on the train and (re)wrote a bunch of stuff in Go.

We absolutely 100% loved it for small contained pieces like monitoring checks.

When doing a basically boring API with user management some of us found it ok at best, others (like me) really hated it with a passion. Every task that should take 20 minutes and 20 lines of code in any web-first ecosystem (php+symfony, rails, flask) would take 3x as long with 2x as many lines of code, also because of "if err != nil". Sure, a lot of it might have been "Go beginners" but it just never felt smooth for controller-heavy web development.


I use go daily and have come to really like it, however I van completely relate to your pain points. I think there's a lot of confusion over the "go is simple" meme. Go is simple, but it's not easy.

The price one pays for simplicity is the need to discover and master various design patterns; it requires a bit of planning beyond syntax and module exports. It took me quite a bit of time before I felt downright fluent in Go.

I did get there, and I really enjoy the language, but I wasn't immediately productive.


I characterize it this way: Many people who have learned lots of languages are used to having a problem, and then poking through the new language for the feature that solves the problem. Languages designed by and used by these people tend to grow lots and lots of features.

In Go, you are expected to use the features that exist to accomplish the goal, so instead of knowing a ton of features at a somewhat surface level, you need to learn what is there fairly intimately, and use them to their full effect. For a simple example, there's no such thing as "class methods" in Go, so obviously anything best solved via class methods is impossible, right? Except of course it has "class methods". Class methods are just methods that don't reference the instance itself. Go doesn't have any syntactic label for them, but they exist just fine and I make fairly heavy use of them.

Personally, when I read someone complaining that they tried Go and it was "full of boilerplate, repetitive code everywhere and they kept having to copy&paste functions" that they were either A: in the completely wrong domain (see my other comment about how I don't think Go is good for math at all), or B: they didn't do this and were probably trying to jam a map/filter/reduce-heavy workflow, or an inheritance-heavy design, or something else not native to Go, into Go. My code isn't particularly repetitive. Even the error-handling in my mature code tends to be between 1/3-1/2 not "just return the error". (In prototyping it often starts out that way, but by the time I ship something, there's usually a lot more going on.)

As another for instance, you really need to learn to use interfaces to their fullest, as integrated into the type system. You can put methods on anything, even functions or other non-struct types, and anything with methods can conform to an interface. It's really helpful to be able to do that. If you reject all these options as "that's not how I want to design", you're in for a bad time.

Bizarrely, despite their incredible distance on my internal "map of all programming languages", this is one thing Go and Haskell kind of share in common, and something I took from my experiences with Haskell, where you are also handed a certain set of tools and expected to make them work. If you try to take Haskell and force it to be an object-oriented language, you're gonna have a bad time, and you'll complain about how stupid typeclasses are and how stupid immutable data is and how stupid it is that all your code has to be written in IO so why is it even separated out and all kinds of other things. But the solution is to learn how to actually do things the Haskell way, at which point you'll find that while it may not be the best choice for everything, it certainly has some interesting things to say. Go actually does too, around the virtues of structural typing and the privileging of composition over inheritance. It's much less profound than what Haskell has to say, but it's still something.

Neither Go nor Haskell are unique in this characteristic. You really ought to come in to every language you learn with the viewpoint to learning how it does things, rather than coming in and trying to make it be some other language. It's just the simple languages that tend to really put this in your face. The languages with bazillions of features usually have something on offer that looks close enough to what you want that you can force your way through into turning the language into a different language. (And when you ponder what it looks like when you've got a team of 50 people doing that, you start to see some of what Go is designed to fix.)


If I could upvote this twice, I would. You've managed to put words on something I've been struggling to articulate.

Your point about using Go's type system (esp. interfaces) rings particularly true. When I finally figured out how to effectively use the type system, I suddenly found myself writing extremely malleable code. You can move data types and functions around quite freely, and this makes it easy to design & refactor without losing your place.

The flip side, as you say, is that extreme flexibility requires you to build your own skeleton.

BTW, are you github.com/thejerf? If so, I've contributed to Suture, and use it quite often, so thanks for that.


Yes, that's me. I was slow on the draw and missed github.com/jerf .


> If you need to build performant APIs with a small team there is nothing that comes close.

The JVM and .NET think otherwise.


And yet Go pretty much ate the lunch of C# and Java when it comes to backends and devops, despite the fact that both had a decade of head start and generics and large dev teams behind them.

I like C# and Java, certainly much more than C++, but Go hit that sweet spot of little ceremony (try to write a single file Java or C# program), performance, deployability (again, try to deploy a Java or C# program as an Ubuntu service on a low-end 512 MB Digital Ocean host or provide a Java or C# cmd-line program to a Mac user) and programmer productivity.


>And yet Go pretty much ate the lunch of C# and Java when it comes to backends and devops, despite the fact that both had a decade of head start and generics and large dev teams behind them.

This is simply not true. Go is nowhere near that even when only new Greenfield projects are considered. I see many of the start-ups using it but most new projects are still in Java or c#. But go has captured the Dev ops tools area quite a bit.


>Go pretty much ate the lunch of C# and Java when it comes to backends and devops

Are you saying that Go is significantly more popular than Java for backend, server-side development? I'd like to see a citation on that, because pretty much any statistic I've read has said otherwise. If you are saying it's significantly more performant, then I'd like to see evidence for that too.


Benchmarking is error prone, and so are language statistics. Certainly Go has had a meteoric rise and much of the big innovations are happening in Go (Kubernetes, Docker, Prometheus, Terraform).

Go is simple, easy to install, and the benifits of a compiled language are huge. I can apply all of my systems knowledge to understand how my app is performing. The JVM is a career in of itself, and locks you into an ecosystem that is very proprietary and frankly full of lame enterprise salesmen and big business fogies. Go is fun. It’s branding is fun, it’s community is passionate, and a lot of big names are on the tin that speaks to a lot of us who love and grew up on Unix.

I mean, do you want to support Oracle and Larry’s bid to buy more islands or ken and rob?


Compared to passionate comunities, fun and evil CEO arguments, benchmarking and language statistics are not error prone.

You're not really arguing in good faith. This reads more like a parody.


> Benchmarking is error prone, and so are language statistics.

… and so is just saying stuff :-)


I'm running C# on hobby Heroku dynos right now, which are 512MB. I think dotnet core has changed the landscape a bit for C# web services. I made a library which allows me to start a new dotnet project with a vuejs frontend on Heroku within a couple of minutes. I'm really hoping dotnet core continues to see more adoption.


Yep .net core runs comfortably in a minimum 128mb aws lambda. It’s not ultra lean, but there aren’t many use cases I can think of where that matters.


> And yet Go pretty much ate the lunch of C# and Java when it comes to backends and devops, despite the fact that both had a decade of head start and generics and large dev teams behind them.

C# was a windows-only show until very recently with the introduction of.NET Core, and ASP.NET Core is gaining in popularity very fast.

IMHO Go's current adoption rate has more to do with politics and Google's aura than anything else, really.

And I really don't see your assertion on Go eating Java's lunch. Sometimes Java appears to be the only game in town wrt backend development, with Python appearing in second place.

> try to deploy a Java or C# program as an Ubuntu service on a low-end 512 MB Digital Ocean host

I have no idea how a generi C# or Java program fares with so little resources, but I've deployed ASP.NET Core and Spring apps on Hetzner's smallest VMs (2GB) without any problem. I had to google digital ocean's micro VMs to check if such a thing really existed, and I have to say that the price difference (Hetzner's 2GB VM for 3€/month Vs Digital Ocean's 512MB VM for $2.50/month) hardly justifies the trouble.

So unless you're compelled to save about $0.50/month on hosting, I hardly see the point of your example.


Go's big advantage over other memory managed languages is the very low overhead of its runtime - a hello world Go app is probably less than 5MB on disk with all dependencies, and takes up less than 5 MB of RAM. This is absolutely wonderful if you want an architecture of many independent containerized apps, especially with something like Kubernetes or Docker Swarm. Compared to that, the smallest Java container on the classic Docker repos is about 90MB in size and takes up a few tens of MB RAM.

Apparently there is a new Java project [1] floating around that is promising a way to use Java exactly in this use case, but I haven't played around with it.

[1]: https://quarkus.io/


http://august.nagro.us/small-java.html

You can run an empty Spring Boot service in 15MB of RAM or so.


How many mbs for a hello world in forth ?


Hey, I said 'over other memory managed languages'! I really wouldn't compare it to one of the tiniest languages this side of asm. I also think Forth would be a harder sell in a corporation than Go. Or C++. Or ASM, probably.


> try to deploy a Java or C# program as an Ubuntu service on a low-end 512 MB Digital Ocean host

Others have already pointed out doing this with .NET, and yes, it's also possible in Java land. I have done this, even running a Spring Boot service which is generally considered more heavy weight than other frameworks like Dropwizard. A plain Spring Boot service runs in about 15-20MB RAM if I recall. And now with the GC improvements in Java 11+, it's even better.

http://august.nagro.us/small-java.html


You can write csx files since 2015 probably, so it has been really easy for the last 4 years. In F# we are speaking of more than a decade, before go even existed. On the JVM you could have used groovy for even longer than that. For the rest of the points I guess that you never heard about .Net core. And I’m quite curious about the reason that you are unable to install the JRE on Linux, that is something that has been possible for at least two decades and with much, much less RAM than 512MB.


On the JVM, Beanshell has been around since the late 1990's so that's also over 2 decades. In fact, Apache Groovy was pretty much a clone of Beanshell, but with closures added, in its day.


Well yeah, Google uses Go, so clearly it must be the best technology out there! Why would a hip startup use crusty old enterprise technology like Java?


Ironically enough, the vast majority of Google's critical services are in Java and C++. I don't work there, but what's golang used for other than some few services here and there?


People have a lot of valid concerns about using a technology owned by Oracle, and the 2000s ended up leaving a bad taste in people’s mouths from the amount of enterprise shilling that went into Java. Now Java is showing it’s age and cross compiling is no longer the problem it once was.


People are not educated enough and should start living in 2019, not 2010. Java (trademark) is owned by Oracle, but implementation is open source, backed by big names.

> Now Java is showing it’s age and cross compiling is no longer the problem it once was.

I see numerous flaws in this sentence. How Java is showing it's age, language wise, comparing to eg. Go? It is fully OO, has generics and recent versions with advanced functional stuff are making Java pretty modern language on (almost) Scala level.

Regarding cross compiling, it is still the problem. If Go gives you 3-4 platforms out of the box, you can't say cross compiling is solved problem; try to work with MIPS or slightly less popular embedded platforms.


Some people would argue that being "fully OO" is indeed a sign of age at this point. :-)


Which would be ignorant, everything has its place. In any case, https://www.youtube.com/watch?v=HSk5fdKbd3o


None of your arguments were objective or technical, and you've missed the fact that the Java is by far the standard in backend development and more importantly is taught in universities.

Honestly, the only ads I see for Go developers are for blockchain projects, which suggest buzzword-driven development.


There are no objective arguments when it comes to programming languages. If people like Brendan Gregg say that benchmarking is difficult and error prone, we don’t stand a chance.

What we do know is that Kubernetes, Docker, Terrafofm, Prometheus, Caddy, et al. have taken off in part because Go is easily accesible and can scale. Go has risen metorically to the top just like Java did, and there are lots of really huge deployments of Go out there. The JVM will always have certain limitations and require it’s users to specialize in JVM tuning and architecture. Many users are also hesitant to support a company like Oracle, and the enterprise shilling that comes with the Java ecosystem.


Things that are difficult and error prone are often worthwhile. Probably why Brendan Gregg has built a career in performance engineering, instead of giving up and say, complaining on HN how hard life is.

And in fact performance itself is a very objective criteria. Either a piece of software is fast enough or it isn't. This doesn't refer only to percieved performance, but also hard performance indicators established in the requirements of a project. Sw dev 101 really.

Furthermore, languages and their implementations can be assigned to different performance personalities, from very fast (e.g C) to slow (e.g. Ruby). Go doesn't belong in the very fast bucket, it's one level below, hanging out with Java, C# and others.

The arguments regarding Oracle are frankly worthless, but if you want to go there, at least Oracle admit they're evil and restrict their evilness to price gouging on their products. They don't pretend to be some force of good while surveilling billions of people through devices, apps and web platforms like Google does.


Kubernetes was prototyped in Java, it only got rewritten in Go, because another team took over.


I think Java is taught in University and has become the Backend development standard because of historic reasons, not because of its technical qualities.


That's not true at all, and misses the whole collection of technical reasons Java became one of most successful anf popular platforms in history, and why it's still being adopted for other applications and platforms, including those controlled by Go's main proponent: Google.

It might be trendy or fashionable to poopoo on Java, but that criticism isn't driven by technical arguments.


Well, there are plenty of other languages that are just as capable as Java, they just came later.

I just feel like I've been lied to in University. Java was presented as the silver bullet in University. "It runs anywhere and models the real world". "In Java, you can't compare apples and oranges!". Turns out you can, if someone implements an AppleOrangeComparator. Great.

I'm just venting at this point, don't mind me.


Most of use learned C++, Java, or Python in school, that doesn't mean it's right or that it has to be a permanent fixture in your life.


Only in Silicon Valley and startups hoping to get acquired by Google.


I can’t really see how you can be as productive as python with go given the huge amount of boilerplate that you need to deal with and the lack of basic constructs that have been common in a lot of modern languages.


> Switched from Python to Go about 2 years ago.

What a strange comment. Python and Go are good at different things and you can use both.


They’re both general purpose languages that put a premium on usability. Except that Go is faster, runs better in multithreaded environments, and is actually simpler to grasp due to a liberal lack of features. I don’t think it’s crazy to conclude that you should replace all your Python with Go when you can have more efficient program and more compile time checks at no additional cost.

I’ve seen plenty of projects switching to mypy and asyncio kicking themselves that they can’t just use something like Go.


I find Python easy to use. I don't think Go is easy to use at all. Boilerplate, boilerplate, boilerplate.

I don't have to write for loops in Python. Go makes me.


Go is not especially friendly when dealing with lots of similar, but different, json though. Lots of boilerplate and overhead (more code) there.


A lot of that isn't so much "Go" as "statically typed language".

I say this not to defend Go, but to encourage anyone creating a JSON-based API to at least test with a statically-typed language. It's too easy in a dynamically-typed language to create a field that is either a number or a list of numbers, but you just boned all the static languages in the process. Unless you know you're only talking internally, and you're never going to change to a static language of any kind, JSON APIs should generally be written in a subset of JSON that is static-friendly.

It's not that crazy of a requirement anyhow; it's not really a great plan in dynamic languages either because it complexifies the code there, too, just not as much. It's a bit of discipline, yeah, but it's good for you even in a purely-dynamic environment.


Do note that Go also heavily uses reflection (from what I recall) for parsing json, so it can be a bit slower than one would expect.


In some ways Go is a litmus test. Do you have a simple, consistent schema? Are you trying to overload the types of fields and making parsing more difficult? I have run into this problem myself, but I realized that Go was pushing me to simplify and make things uniform which actually made my life way easier in the long run.


nice, I'm just going to send mails to the various creators of the JSONs I consume to ask them to change their protocols because it's not simple enough and solve world hunger while I'm at it


Most of my struggles here have been when consuming /existing/ json blobs, specified elsewhere: 3rd parties, other internal services, "versioned" json blobs, and the big one... consuming json structured logging.

So I think we could both be right.


The additional cost is in the inevitable boilerplate that you'll end up writing to handle real world problems. Python let's you extend the language to help with this. Go, by design, doesn't allow you to. There are pros and cons of each. Not once have I felt I could forget Python because Go exists.


> You get the productivity

How do you measure this? By feelz?

Because I've been a programmer and tech lead for 15+ years, and C++ projects are always done faster than PHP ones.

PHP may be "easy", but better programmers with better tools will always win on development speed.


It's just a shame that you could EASILY bolt on basic parametric polymorphism to Go with 0 cost and huge benefit. And it'll never happen.

The power of a universally quantified interface cannot be underestimated!


Never? This is one of the basic proposals in the Go 2 roadmap.


Rob Pike is against the current proposal and clearly states it comes from the community.

https://www.youtube.com/watch?v=RIvL2ONhFBI


the cost of power is chronically underestimated


Exactly - parametric polymorphism doesn't give you power. It in fact only ever makes your types more constrained!

In a theoretical Go:

    func id<A>(a A) A
This only has one terminating, non-panicking valid implementation.


This HN item links to the blog's front page. The actual article is at https://togototo.wordpress.com/2015/03/07/fulfilling-a-piked...


For anyone bothered by Go's simplistic error handling, a menu of Requirements to Consider for Go 2 Error Handling:

https://gist.github.com/networkimprov/961c9caa2631ad3b95413f...

See also the Go 2 Draft Design feedback wiki:

https://github.com/golang/go/wiki/Go2ErrorHandlingFeedback


After switching to ML languages for most development tasks, I don’t think I could face using a language with so few features.

Is most of the hype from a dev ops perspective? Go looks tiresome to program in.


Well, there's some blub paradox (PG) at play.

As Go team found out, it's not like people go from C++, OcamL or other full featured languages to Go.

For Python/Ruby etc programmings coming to Go, the lack of features feels exciting, because it makes them think they're programming closer to the metal (what with types, and static compilation, and contiguous arrays, and pointers).


Maybe you're taking the blub paradox backwards.

People think Go is worse than other languages because they look at it from a PL theory point of view, ignoring the practical aspects. "Compilation speed? Trivial cross-compilation and deployment? Consistent coding style accross projects? Ability to recruit junior developers/maintainers? Warnings-as-errors? Who needs that?"


>Maybe you're taking the blub paradox backwards. People think Go is worse than other languages because they look at it from a PL theory point of view

That's not the canonical version of the blub paradox though.

In the canonical description it's the increased expression (due to advanced PL features like macros and closures) of the language that can't be appreciated by the blub programer. So, Go is the one that lacks those things.

One could make a case that the other things you mention (compilation speed, cross compilation ease, etc) are also important and should be appreciated, but that wouldn't be the blub paradox.

Besides, other languages can have those as well while keeping their advanced features (compilation speed aside, the rest are more about tooling work, which is orthogonal to the language. And D, for one, can compile as fast as Go, while still having Generics).

https://digitalmars.com/d/archives/digitalmars/D/D_compilati...


> And D, for one, can compile as fast as Go, while still having Generics

And Delphi, Eiffel,...


Not the experience I had with Eiffel back then (but it was decades ago).


Were you generating native code all the time via C export instead of using MELT?


Go does have some benefits.

In my case, cross-compiling into one binary means it could be the easiest way to build and deploy on an embedded platform where I normally use a quite complex to set up C++ toolchain.

At the same time Go's a typical "sidekick" language, because it's really good only at a few things, unlike C++ or Java which are useable for almost everything.


I've written stuff in C++ as well as Python. The simplicity of Go appeals to me because it means the code is generally more readable (eg people aren't abusing advanced features quite so much).

Anecdotally I've found reading other peoples Go code far easy than I've found reading other peoples code in pretty much any other language. YMMV though


>Anecdotally I've found reading other peoples Go code far easy than I've found reading other peoples code in pretty much any other language

At the micro level yes. At the macro level though, it's easier to understand 10 lines of dense code than the 200 lines Go forces you to write for the same thing. At least you can see them in your screen all at once (and usually express the intent more directly).


For you, perhaps. However for me I completely disagree.

What you're failing to understand is that this is an entirely subjective rather than objective matter. What works for you isn't proof that it's the same for everyone else.

This is why I've said "The simplicity of Go appeals to me", "Anecdotally I've found" and "YMMV" to made it clear in my post that the subject you're discussing is actually a matter of personal taste.


>What you're failing to understand is that this is an entirely subjective rather than objective matter.

I don't think it is subjective.

It's just that as an industry we don't have large scale research into such things.

>What works for you isn't proof that it's the same for everyone else.

No, but our brains co-developed evolutionary for millions of years for some of us to be widely different than others in how they work.


> I don't think it is subjective.

The very fact that we disagree on what we find readable should be evidence enough that it's subjective.

> It's just that as an industry we don't have large scale research into such things.

I don't need large scale research to tell me what I, personally, find easier or harder to read.

Research would tell us what the general rule of thumb is but that's very different to saying there's a definitive rule that applies to everyone uniformly.

> No, but our brains co-developed evolutionary for millions of years for some of us to be widely different than others in how they work.

Forgive me if I'm missing your point here but some people like fast cars and some people like to cycle. Some people want kids while others do not. Some people enjoy working in IT while others want to be gardeners, carpenters, mechanics, doctors, lawyers, etc.

Even specifically to HN, we have enough arguments on here about emacs vs vi; CLI vs GUI; and tiled window managers vs dynamic / overlapping windows to demonstrate that personal preference is alive and well in the field of IT. Yet it's strange how you consider it not to be applicable when it comes to language preferences. I suggest that's failing of imagination on your part.


>I don't need large scale research to tell me what I, personally, find easier or harder to read.

I think we do.

Your phrasing implies that everybody (or at least just you) already and always knows and uses the optimal code styles that make it "easier" for them to read code.

I very much doubt that's the case.

People are deluded about what's "best for them" all the time.

Not to mention "what's best" might change with familiarity with other styles, training, etc, and people could be stuck in a shallow local optimum.

>Forgive me if I'm missing your point here but some people like fast cars and some people like to cycle. Some people want kids while others do not. Some people enjoy working in IT while others want to be gardeners, carpenters, mechanics, doctors, lawyers, etc.

Those are matters of taste. Not matters of cognitive skills and how people read best, learn best, etc.

In fact, the popular myth of personal "learning styles" (X training style being best for some people, Y style best for others, etc) has been demolished.

And yet tons of people confused their "preferences" with some objective reality of their preferred style being better for them.

https://blog.mindresearch.org/blog/learning-styles-myth

https://www.theatlantic.com/science/archive/2018/04/the-myth...


> Your phrasing implies that everybody (or at least just you) already and always knows and uses the optimal code styles that make it "easier" for them to read code.

No it doesn't. All I've been saying is I find Go - in general - more readable than other languages. Everything else thereafter is you badgering your language snobbishness onto others and me having to say, repeatedly, "I like what I like"

> Those are matters of taste. Not matters of cognitive skills and how people read best, learn best, etc.

"Read best" != "learn best". Anyone who has dyslexia will tell you that they struggle with layouts that others wouldn't take issue with. People who aren't native English speakers also often have different preferences for writing styles to native English speakers.


That can happen. However, when you do not have the proper language features (e.g. pipe operator) then code can become much less readable because it is more verbose and the control flow is unclear.

I think that simple languages only lead to more readable code when the problem domain is also simple.


Not really no. The relationship between verbosity and readability of control flow isn't as clear cut as you describe. For example Java is often praised in threads regarding Go yet that is far more verbose. C++ has all the features you described yet is still more verbose (overall) and less readable than Go.

Plus your post reads as if complex problems doesn't also decrease the readability in more descriptive languages but my experience is complex logic will be inherently harder to read regardless of the language.

I suspect what is actually happening here is your familiarity with language X means you find it easier to read code in language X - and that has nothing to do with verbosity, "proper" language features or whatever else. Personally, however, after 30 years of development on well over a dozen different languages (even some I'd be embarrassed to admit to), Go has consistently been up there for me as one of the easiest to read.


I don't see exactly how my comment connects with yours, so I will just try to clarify / expand on it.

* Java is often hard to read because it is verbose and lacks language features. For example, without operator overloading vector math does not read well.

* C++ (14 upwards) can be made very readable or it can be made very unreadable because it has so many features. I don't think it is obvious that C++ is less readable than Go. Are we talking about the worst case? the best case? the average case?

* The connection between control-flow clarity and verbosity is IMO due to features like the pipe operator and do-notation. These allow you to have code that reads linearly even when the execution is not. The alternative is something with many explicit function calls and nesting that jumps all over the place. Take a look at a Node.js program that uses async/await vs without and you will see what I mean.

* A complex problem domain will lead to harder to read code than a simple domain, but this is not relevant. My point is that an implementation for a complex problem in a language with features to manage complexity will be more readable than an implementation for a complex problem in a simpler language.

* It sounds like we have both extensively used multiple programming languages but reached different conclusions.


My point was for all the logical reasoning we equate to our choices it still ultimately boils down to a matter of personal taste.

For example it sounds like you favour OOP where as I tend to favour a more functional approach?

(disclaimer in case of knee jerk comments: I'm not calling Go a "functional language")


I don't think this is just a matter of taste. Advanced features allow the programmer to express more complex things more directly and less verbosely.

I agree that there are some things that are more taste driven (e.g. syntax).

BTW, I actually favour FP also.


> I don't think this is just a matter of taste. Advanced features allow the programmer to express more complex things more directly and less verbosely.

The fact that you and I disagree about how readable Go is should be evidence enough that it is a matter of taste. Otherwise we would be both agreeing that these facts lead the same outcome on readability rather than myself offering counterexamples to your points (and in fairness I don't disagree that you have well reasoned points even if I don't agree with your conclusion)

Here's another contradiction: Perl is often joked as being "executable line noise" because it allows you to express things in any which way you like and is antithetical to the very concept of verbosity. Yet these are properties you argue in favour of readability. So rather than verbosity+features having a boolean outcome on readability, I'd argue it's really more of a spectrum where (1) verbosity and (2) flexible grammar / descriptive features are axis on a graph. Somewhere in the middle will be a cluster of languages people consider readable - with some individuals preferring languages that scores higher or lower on one axis than another.

On the topic of verbosity, I've always thought the the language grouping (eg is it a higher or lower level language) gave a better approximation. For example Go does force some functions to be needlessly verbose however when taking your typical Go application and comparing it to the same thing writen in idiomatic Java or C# - languages I'd argue are in the same group - then Go doesn't actually come out all that badly at all. The problem Go has is the ugly areas of Go taints user perception for the other 90+% of the code (if it were as bad as some people express then I wouldn't waste my time with Go either).


Which ML do you mostly use for "general development"? Like simple tools, small web API's, etc? And what's the development/deployment story, compared to Go's (i.e. single .go file, easy (cross)compilation, single binary, pretty fast out of the box)?

I'm currently mainly using Go for these kinds of things, but I'm very interested in trying out an ML for this. I've tried Haskell but it seems very complex in a lot of cases.


F# might be a good fit: it is mature, has modern tooling and, in my experience, people tend to get productive quickly.


I'm a C++ programmer for over 10 years and looking forward to becoming a Go programmer, professionally.



I wonder how Dlang would have compared.


D is superior in every aspect except the BigCo support.


Why do you think that is (the lack of BigCo support)?


Because Walter Bright is/was not working at Google, Oracle or Microsoft? :)


> he expected the language to be adopted by C++ programmers, a prediction that hasn’t been realised

I respect go (and its runtime) a lot, but one little thing keeps me from using it: the reverse "argument type" ordering (backwards from C++) makes me avoid it. If this were the only language I used, it would be easy, but when switching back and forth, does anyone else find it hard to do?


Most newer languages are like this: Go, TypeScript, Rust, Swift, Kotlin, Scala, Nim, etc.

Admittedly most of these languages use a colon like `name: type` rather than plain `name type`, which helps readability a lot IMO. But you would unlock a lot of languages if you are able to get used to this style. Personally I find the `type name` style confusing. It's ok for variables, but it's super-confusing for function definitions where most language use a dedicated keyword (`function`, `fun`, `fn`, `def`, etc).


This is one of the easier things to get used to-- I don't find "code switching" between syntaxes to be difficult at all, especially if you are already looking at a codebase and can visually copy what's already there. It even makes some sense, if you read it as "variable foo is a bar".


My job requires switching between Go and Java occasionally. While I think Go is slightly more natural, it definitely adds cognitive overhead when going back to Java. But that additional overhead is nothing compared to the rampant and indecipherable stringing together of Guava futures in my company's Java codebases.


The newer jvm languages (particularly thinking of Kotlin and Scala) though, generally are type-second languages.



"one of my reasons for getting into programming was the opportunity to get paid to use Emacs".

Not a reason for getting into programming that many have, I expect!


Wow, I'm surprised to see the typical "Well, why didn't you just use Rust" comment hasn't been posted.

At the end of the day, it sounds like they just had some clunky C++ code that was due for a rewrite anyways (and the author admits as much). If you don't need it to be ultra low latency, some nice abstraction and modularity can already do wonders.

I've found Go to be an excellent Java replacement when all is said and done. Great for the vast majority of services you might write. But if you need to do anything lower level or that involves complex type logic you always end up with a clunky solution (and I'm not going to sing any praises for C++ having an elegant solution either).

That said, modern C++ (especially 17) can be made to look decently clean and without weird behavior in most cases. Yes, there's always the person that shows up and points out weird edge cases that you can run into but the situation is far, far better than it was years ago. With some good engineering practices in encapsulating low level optimized code you can largely limit the "danger surface" of your codebase.


If you haven't tried Kotlin, you really should. It's a highly expressive, null safe language with all the goodies you'd want without the bloat of Java. For web services, Kotlin + Vert.x + Postgres is a beautiful and highly performant thing.


When Go gets around supporting Java generics, dynamic loading, JDBC, PDF generation libraries, Office/LibreOffice manipulation libraries, Akka, CMS platforms, BluRay/SIM CPUs, a phone OS, a GUI library comparable to AWT (lowering the level as Swing/JavaFX/Android aren't even around), JMX instrumentation, hot code replacement.... then maybe just maybe, it is an excelent replacement for Java.


I can't tell if it's intentional but your post borders on fanboyism given about 80% of what you listed there is either already available for / possible in Go or is a Java specific term so naturally Go would have it's own mature libraries out there under a different name. For example

* JDBC, Akka, etc

There as all Java specific technologies but that doesn't mean that Go (nor any other language) doesn't have their own libraries to do the same. I mean you're not going to call Go's database libraries "Java Database Connectivity" are you

* PDF generation libraries, Office/LibreOffice manipulation libraries, CMS platforms,

Go already has all that

* a phone OS,

There isn't any phone OS's written in Java either. There are phone OS's that use Java as their preferred application runtime environment - such as Android - and many of them can and do support running Go binaries as well. There is in fact a lot of documentation online regarding running Go on Android.

---

I'll grant you that Go doesn't have hot code replacement nor generics but the former is a pretty niche case for Java development that wouldn't really improve Go's current workflow anyway - and the latter is such an tired old argument that you're not going to win any debates by pointing it out.

Disclaimer: I've programmed in both Java and Go (as well as more than a dozen other languages too). So I'm about as agnostic as they come when it comes to evaluating programming languages.


So where are the Go drivers offering 1:1 feature parity with JDBC for Oracle, DB2, SQL Server, Informix....?

An example for cluster based distributed framework similar to Akka in features?

What Go libraries provide feature parity to PDFJava, iText or POI?

Which Go CMS is comparable in features to Literay, Adobe Experience Manager?

Where is Go listed here?

https://developer.android.com/guide/platform


> So where are the Go drivers offering 1:1 feature parity with JDBC for Oracle, DB2, SQL Server, Informix....? > > An example for cluster based distributed framework similar to Akka in features?

> What Go libraries provide feature parity to PDFJava, iText or POI?

> Which Go CMS is comparable in features to Literay, Adobe Experience Manager?

Clearly I'm not going to waste my entire afternoon doing research for you just to win some internet points. Particularly when you're just going to find some other "irrefutable fact" to lord over Go thus making any sensible discussion pointless.

Actually I'm going to agree with you on one thing: while Go does support all of the above I'm sure there will be some specific edge cases that are available in a Java library - eg PDFJava - that isn't available in an equivalent Go library. However I'm also confident I could list of some edge cases that are covered by Go and not Java. Anyone with the slightest bit of level headedness would agree that you're not going to get 1:1 feature parity with every competing library in competing languages because different developers will prioritise different features. So you accomplish nothing by making such an argument in the first place.

> Where is Go listed here?

> https://developer.android.com/guide/platform

Why would it be listed there when Go doesn't target libc nor the JVM? That page also doesn't list about dozen other languages nor frameworks that also support Android.


The point was that Go is supposedly an excellent Java replacement, yet it's ecosystem is found lacking in comparable libraries.

As for Android, the whole userspace and a big part of Treble drivers are written in Java, which was exactly the point of being a phone OS written in Java.

So excellent replacement for Java? Not really.


A sane person would have read that as "excellent replacement for Java for many typical use cases" rather than "a drop in replacement for Java for any and all use cases"

Sure the GP didn't categorically set a context boundary but it's a pretty reasonable to assume he intended it to be read that way given we're all supposed to be intelligent people.

> As for Android, the whole userspace and a big part of Treble drivers are written in Java, which was exactly the point of being a phone OS written in Java.

The whole userspace isn't written in Java. Yes there is also an accompanying Java userspace but your exaggerated argument is ignoring the GNU components as well as native C++ parts too (which, as it happens, are also represented in the very link you posted).


> The entire feature-set of the Android OS is available to you through APIs written in the Java language.

Ah so Linux is not written in C, because there are some Assembly components there I guess.

What GNU components? The only GPL stuff surviving on Android is the Linux kernel.


> Ah so Linux is not written in C, because there are some Assembly components there I guess.

Oh do please stop exaggerating things. It's abundantly clear that I wasn't arguing the irrelevance of Java in Android. You simply said "phone OS written in Java" and I was making the point that Android wasn't exclusively written in Java, thus Go (amongst other languages) can and do still run on Android. Given the context was about whether x can run on y, it's very relevant if y was written exclusively in x, w or v.

Also I don't understand why you've taken such a personal offence to Go but this entire argument of yours has been really petty. You have your tools you like and the GP has theirs. Does the GPs tools really need "death by a thousand paper cuts" to make yours tools look more important? Clearly it doesn't because Java is proven technology. So why waste your time criticising everyone else's tools?

> What GNU components? The only GPL stuff surviving on Android is the Linux kernel.

Quick Google suggests you might be right there however that wasn't the case last time I did dev work on Android.

Even so, the NDK still exists.


> That said, modern C++ (especially 17) can be made to look decently clean and without weird behavior in most cases.

The issue is when you need a library, it becomes much harder than in pretty much any other language. And your library probably isn't written in your nice clean subset.

> I'm surprised to see the typical "Well, why didn't you just use Rust" comment hasn't been posted.

Rust only hit 1.0 midway through 2015, so it was a bit early for that.


> Wow, I'm surprised to see the typical "Well, why didn't you just use Rust" comment hasn't been posted.

https://news.ycombinator.com/item?id=19387279

Also, yours seems to count as well.

I think it's fine to compare languages - some features can be best described by contrasting them with others.


Article from 2015.


Ups and downs?


Occasional pauses of 400-500 milliseconds? How would one mitigate that when coding a restful API server?


This article is pretty old. If this is from garbage collection, Go has more than addressed this with <100 microsecond stop the world in "typical worse case" scenarios. I believe it also tries to spread out the work to provide consistent latency.


My, possibly incorrect understanding, is that it had implemented hard time max pause durations, which will only lead to an infinite amount of garbage under some use patterns. Given the point if gc is you doubt think about it that's bad.

Giving go a gc was a total design failure


Go's GC is the best GC I've seen in practice. It's just silently working in the background without causing visible interruptions. And contrary to what you imply, I've never seen the GC cause memory leaks.


> "visible interruptions"

Humans can see things only longer than 200milliseconds. A small eternity.

The point is there are entire classes of application which have latency targets in the handful of microseconds. A 1ms STW isn't acceptable in this scenario.


The erlang gc is far better than the go gc, and it's deployed on every Cisco and juniper switch.


You could wait until $current_year, in which GC pauses are about 50 microseconds.



> Posted on March 7, 2015


Added. Thanks!


No worries.


Four years old and the typo in the title still hasn't been fixed?


You mean "Pikedream"? Its a play on words. One of Go's core developers is Rob Pike.


I think they meant the ups "of" downs.


Wow, I looked at that repeatedly and closely, yet didn't see it until I read your comment. Paris in the the spring...

Fixed now. Thanks!


>> The worse the abstraction that you and your colleagues are likely to use, the better a language is Go

I feel the same way about most statically typed languages. They're designed for developers who aren't good at coming up with abstractions. Basically, they're great for junior developers. For senior developers, they just give you more work and slow down progress.

On my last project (statically typed language), we had to do a refactoring of a decent portion of the code just because it allowed us to use one interface/type instead of two related interfaces/types (one inheriting from the other). It didn't fix any bugs, make it more maintainable or even change the external behaviour at all.


Another gent who asserts that languages he likes to use are better for "senior developers" while those he doesn't are better for "junior people".

Don't hire anyone who makes such insulting remarks.


I have to side with jondubois on this one, and I don't think it's an "insulting remark" to make.

According to my experience, there is clearly a spectrum of language difficulty. Go is on the one end, being easy to pick up and use productively by design, and e.g. Haskell is more on the other end, where you have to take a lot of time to learn the underlying concepts before you can use them really effectively.

Languages like Haskell are clearly aimed at "senior developers" in a way that Go is not, where "senior" doesn't necessarily mean "min. X years of industry experience", but "min. X years of category theory experience".

And this difference is important to keep in mind. When I work on a really difficult problem, it's probably a good idea to choose a powerful language to help me solve the problem, but it means that only sufficiently senior developers can work on the program. On the other hand, a run-of-the-mill CRUD application should probably be written in something like Go or JavaScript because it's way easier to hire developers for these languages. If I write a dumb CRUD application in Haskell, it means that my experts can probably never hand this over to junior developers. That's not a productive use of their time.


Why is it insulting? I didn't say that using the language means that you're a junior. I just said that it was designed for juniors.

Just like there are adults who wear diapers, it doesn't mean that they're juniors. In fact, most of them are very senior. They just like to be on the safe side.


Gents, this ^ is what "full retard" looks like.


Sorry for the confusion, next time I will try to dumb it down further... Maybe I need to start using static type annotations to indicate sarcasm.


I'm curious in what way static languages are "designed for developers who aren't good at coming up with abstractions", are "more work" and "slow down progress".

Those are quite strong statements to not be backed up by strong examples, and I personally find that your example does not clearly illustrate how that would make statically typed languages have the attributes you ascribe them.


> For senior developers, they just give you more work and slow down progress.

From my point of view, the static type system does work for me. Having to write tests for what a static type system can prove is tedious, and it only provides run time instead of compile time errors.


I don't think he's bothering with tests ;-)


I get that you're frustrated, but... it's unhelpful to dream up reasons for something decades after the fact happened. The more reasonable explanation is that they're designed that way, so that the compiler can verify particular properties of the code and because back then the performance difference really mattered. That in turn has some costs on development speed in the prototyping phase.

Can anyone seriously argue that our industry has a problem with development speed? And that extra speed will evaporate once the code base becomes big enough and one needs 1:1 test-to-code lines to keep the thing under control.


So basically you are saying that using a language with static typing you are guaranteed by the compiler that your refactoring is safe, while in a dynamic typed language you completely lose this guarantee and you will introduce bug, and you choose happily the second option? And you are also convinced that people that want some correctness guarantee are juniors while who doesn’t care of introducing bugs is a senior? Since when did the world start going upside down?


Dynamic languages are ok for toy projects/websites.

If you have to build something large/complex/that has to work within constraints, statically typed languages are the way to go.

If refactoring "a decent portion of the code" of a project involves only a couple of interfaces/types, it is a tiny project and yeah, using a toy language would probably have been ok for that.


Agreed, that's the same thing I feel now that I am experienced in both types of languages (statically and dynamic typed). I am way faster when using dynamic typed languages.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: