Hugo Hacker News

Go 1.17 Release Notes

benhoyt 2021-08-16 22:14:05 +0000 UTC [ - ]

To me, the most interesting change is the performance improvement due to the new register-based calling convention. Your CPU-bound programs will magically get 5% faster when compiled with 1.17:

> Go 1.17 implements a new way of passing function arguments and results using registers instead of the stack. Benchmarks for a representative set of Go packages and programs show performance improvements of about 5%, and a typical reduction in binary size of about 2%. This is currently enabled for Linux, macOS, and Windows on the 64-bit x86 architecture (the linux/amd64, darwin/amd64, and windows/amd64 ports).

I love how they're doing it in such an iterative fashion: even assembly functions don't have to be rewritten. Then again, I guess doing it progressively like this is the only feasible way to avoid reworking all the low-level assembly routines in one fell swoop.

benhoyt 2021-08-16 23:25:35 +0000 UTC [ - ]

Wow, this update is awesome: my GoAWK interpreter (https://github.com/benhoyt/goawk) runs a simple CPU-bound AWK program 38% faster when compiled with Go 1.17 (compared to 1.16).

  $ time goawk_go1.16 'BEGIN { for (i=0; i<100000000; i++) s += i; print(s) }'
  4999999950000000
  real    0m10.158s ...
  $ time goawk_go1.17 'BEGIN { for (i=0; i<100000000; i++) s += i; print(s) }'
  4999999950000000
  real    0m6.268s ...
I wonder why it's so much better than their advertised 5% perf improvement? Here's a quick CPU profile: https://i.imgur.com/csJyOYq.png ... I don't get too much out of it at a glance, just seems like everything's a bunch faster.

drchase 2021-08-17 02:07:42 +0000 UTC [ - ]

Hi, I'm one of the people who worked on it, and the guy who did the initial estimate back in early 2017. 5% is the geomean of a lot of benchmarks; a whole lot fall in the the 4-8% range, a few do worse because the new ABI creates new patterns of register use that don't fit well with the current register allocator, and the fix was larger than we wanted to risk. (See https://github.com/golang/go/issues/46216 )

prattmic 2021-08-16 23:56:37 +0000 UTC [ - ]

The benefits come primarily from avoiding extra work spilling arguments to/from the stack on function calls. If you are making lots and lots of function calls, particularly to small functions that can't be inlined, there could certainly be much bigger improvements.

benhoyt 2021-08-17 02:55:07 +0000 UTC [ - ]

Overall for GoAWK I get an 18% speed increase on my micro-benchmarks between Go 1.16 and 1.17 (see https://github.com/benhoyt/goawk/commit/1f314f421273b3dc164f...) and I measured an 8% speed increase on my "slightly more real-world" benchmarks (these ones: https://github.com/benhoyt/goawk/blob/master/benchmark_awks....).

barsonme 2021-08-16 23:45:54 +0000 UTC [ - ]

just an fyi: you can use the -diff_base flag to diff the profiles without opening both profiles side-by-side.

benhoyt 2021-08-16 23:54:10 +0000 UTC [ - ]

Oh, good to know, thanks!

_ph_ 2021-08-17 08:10:12 +0000 UTC [ - ]

The speed gained depends a lot on the structure of the code benchmarked. Natively written Go code has more computation happening in local loops without many function calls, the optimization brings less effect. An interpreter often calls a function for every single directive executed. This means, you have a lot of function calls inside loops, sometimes for every single operation executed. This of course profits massively from this optimization.

Scaevolus 2021-08-17 04:10:56 +0000 UTC [ - ]

Look at the disassembly and observe how your function calls have far fewer push/pop operations going on, and how the function prologues/epilogues are smaller.

jen20 2021-08-17 04:22:27 +0000 UTC [ - ]

This also likely improves compatibility with observability tooling which assumes the standard calling convention, which is welcome.

benhoyt 2021-08-17 04:55:44 +0000 UTC [ - ]

Note that this switches to a register-based calling convention, but it's not the platform "standard" one. The Go authors describe the rationale for this here: https://go.googlesource.com/proposal/+/master/design/40724-r...

umvi 2021-08-16 21:53:24 +0000 UTC [ - ]

At first I didn't like Go because I thought it was too opinionated. But the more I used it, the more I found the tooling to be just heads and shoulders above similar languages like C/C++.

I use VSCode a lot, and in VSCode you can press "F12" to jump to the location of a function definition. In Go I find myself deep diving into github libraries all the time just because F12 takes me there. That never happened with C/C++ - the tooling was just too disparate and fragile.

My biggest complaint with Go is lack of generics. I mean technically you can achieve generics with an unholy empty interface/reflection incantation but it's a huge pain compared to C++.

jchw 2021-08-17 04:24:13 +0000 UTC [ - ]

I think people claiming modern C/C++ tools are close to as good as Go’s are wrong, in my opinion. With that having been said, IMO if you want the C++ experience to be better, use Clangd and generate a compile_commands.json file. For many projects you can use wrappers like compiledb, or Bear, and some build systems like CMake can output it natively. Either way, once you have found a way to do this, and get it outputting into the project root, add a local ignore for it (in .git/info/exclude) and install the VSCode clangd extension.

Truly, it is night and day. I can get Real code intelligence on almost anything, including large C projects with custom Makefiles that compile cross-arch like Wine. It only ever has trouble with unusual translation unit patterns. (You can see this in Higan for example, which has .cpp files including other .cpp files; clangd seemingly fails to put them in the context of the intended translation unit.)

It’s not as good as Go, but it is life-changing for me.

initplus 2021-08-16 23:26:11 +0000 UTC [ - ]

If you use C++ tools where your dependencies get pulled in and built from source like Go does, you get the same experience. If you only have prebuilt system libraries obviously this doesn't work.

It's not like Go's tooling is that much better than the best modern C++ tools, but the ecosystem is so much more unified by having 1 blessed set of tooling. Rather than 20 different package managers/(meta)build systems. No need to worry that I am using build system X, but I want to use a library using build system Y.

yakubin 2021-08-17 01:40:16 +0000 UTC [ - ]

In C++, preprocessor and template magic in dependencies tends to break such IDE convenience features as "go to definition" or "find all references".

kccqzy 2021-08-17 03:55:22 +0000 UTC [ - ]

This almost doesn't happen when your tools are good. At Google the Kythe tool (https://kythe.io) gracefully deals with templates, macros, and much more. Both "go to definition" and "find references" just work with 99.9% of C++ code, including within complicated macros and templates.

(The one and only case I've ever found it doesn't work is to find references to a function that's only called via ADL in a widely used template that's instantiated with thousands of different types.)

yakubin 2021-08-17 09:57:05 +0000 UTC [ - ]

What about SFINAE?

kccqzy 2021-08-18 19:28:38 +0000 UTC [ - ]

Works. Because it uses the compiler to figure out what calls what, and so the compiler has already selected the right overload.

Groxx 2021-08-17 06:19:42 +0000 UTC [ - ]

tbh I've had dramatically worse type-aware IDE features with Go than almost any other mainstream language. gopls routinely misses trivial things in the same file. Goland does substantially better, but it still has major problems gathering a complete call hierarchy, and basic things like moving a file to a different package (with no compilation issues, just import rewrites) still don't work reliably.

throw149102 2021-08-16 22:19:27 +0000 UTC [ - ]

I hate to be "that guy", but you should try Rust. It also has amazing tooling, and is probably more comparable to C/C++, considering you don't have to use a garbage collector in Rust.

throwaway894345 2021-08-16 23:29:28 +0000 UTC [ - ]

I really like Rust, but in my mind it's still relegated to the periphery of tasks that must be super fast (like C/C++, to your point) and/or low-level. Rust has made impressive strides at reducing the toil involved in compile-time memory management, but there is still a big productivity gap between the borrow checker and GC.

Frankly, people are making boatloads of money off of software written in Python and JS--languages which are both far less safe than Go and far slower and yet Go is on par with those languages with respect to productivity (I argue it's more productive due to its static typing features, but others argue it's less productive presumably because of the learning curve). Most software doesn't need to be so fast or correct.

Also, I work in distributed systems and SaaS (software as a service). In this world, most errors are silly type errors ("undefined is not a function", forgetting to `await` some async function, etc) that would be caught with a flat-footed type system like Go's. Beyond that, the next largest bucket are issues that even a sophisticated type system like Rust's wouldn't help with, such as infrastructure issues (missing/misconfiguration of some cloud permission, networking rule, etc), misconfiguration of the service, race condition with many processes accessing the same networked resource, deployment error, etc. This means that Rust's stated advantages aren't really as nice as they initially sound.

As a programmer, I can appreciate a super fast language with a strict type system; however, as an engineer or a technologist, Rust is rarely a good fit for me. Go seems to be a lot closer to the sweet spot, which makes sense because it was developed expressly with distributed systems in mind.

josephg 2021-08-17 00:05:31 +0000 UTC [ - ]

> In this world, most errors are silly type errors

I can’t speak to python, but typescript has basically fixed this problem overnight in the javascript ecosystem. The typescript compiler finds almost all small bugs like this while I’m coding. And as an added bonus, type hints allow the IDE to be much more helpful - adding to jump to function support, autocomplete, method parameter suggestions (or documentation on hover). And typescript is easier to read than javascript because you don’t have to guess what data type some variable is.

I love javascript’s quick and dirty nature, but I still use typescript instead of javascript now for any code I write that I expect to survive the week. (And as a bonus: you still get IDE type hints when calling typescript functions from raw javascript!)

Typescript’s type system is also more powerful than Go’s. It supports enums, genetics and type unions (eg x: string | number).

Go sits in the awkward place of having a worse type system and no significant advantages over typescript for me. It’s awkward to use go on the web. Go is faster than javascript on the server but that usually doesn’t matter. And when it does I can reach for C or Rust. Both of which work really well with native code, or with JS through wasm.

Rust is much harder to learn than Go - but personally I’ve climbed that hill already. Once you’re over that hill, Rust is much more expressive. On purpose both ways - go isn’t trying to be expressive, and rust is. I love rust’s parametric enums and output types in traits. Which I now sorely miss in other languages.

I can imagine Go shining a lot more brightly when working with a team which has mixed skill levels. Most of my work lately has been solo, so I don’t need gofmt to enforce a consistent code style, or anything like that. I do miss Go’s green threads. Rust’s afterthought scattergun approach with threads and futures feels like a mess. But I can’t see myself ever really using Go. It’s weak in areas I want it to be strong (eg the type system). And strong in areas I just don’t care much about. (Eg consistency).

tptacek 2021-08-17 03:01:33 +0000 UTC [ - ]

Rust is very expressive, but it also makes some tasks that are relatively simple in other languages much harder (anything involving tree structures, for instance), and some of what it gives you expressive control over is stuff you usually don't need to think about at all in other languages.

Rust can do some things Go simply isn't suited for right now; for instance, there is no way we're getting kernel Go in the foreseeable future. You couldn't reasonably build a browser, or browser components, in Go.

Both languages have their place. There is a lot of overlap, and the original comment about how one might prefer Rust if what they miss most in Go is generics makes sense. But the idea that the only thing you'd gain by choosing Go over Rust is easier development on big teams is just false. One language is GC'd, the other is borrow-checked. GC makes a lot of things faster to build. This debate ended in the mid-1990s.

zozbot234 2021-08-17 05:37:26 +0000 UTC [ - ]

> Rust is very expressive, but it also makes some tasks that are relatively simple in other languages much harder (anything involving tree structures, for instance)

Much harder, not really. Rust lets you add mutability, reference counting and thread safety to your data structures, you just need to know when to use these features. Yes, Go has fully general GC but very few problem domains have a real need for general GC.

throwaway894345 2021-08-17 12:34:57 +0000 UTC [ - ]

> Yes, Go has fully general GC but very few problem domains have a real need for general GC.

No, no one needs a GC, but many problem domains need productivity, and GC is the most productive way to manage memory to date. As previously mentioned, Rust has made truly impressive strides in improving productivity of borrow-checking, but it still remains quite far behind GC.

josephg 2021-08-17 13:13:26 +0000 UTC [ - ]

Yes, having written a b-tree in rust recently I can confirm this. My child node pointers look like this:

    enum Node<E: EntryTraits, I: TreeIndex<E>> {
        Internal(Pin<Box<NodeInternal<E, I>>>),
        Leaf(Pin<Box<NodeLeaf<E, I>>>),
    }
Yikes. I could probably clean this up a little, but not a lot.

I've been working on this code for months and I still have no idea if my internal b-tree functions should be taking a &mut self, self: Pin<&mut Self> or a NonNull<...> or something else. The compiled code is blazing fast, and being able to control behaviour so clearly with a few parametric type parameters is amazing.

But the process of figuring out the best way to code it up is awful, and it requires all of my attention and capacity. I doubt Rust will ever gain the sort of mainstream usage that javascript & Go have because of how complicated otherwise simple problems can become. Its a great language for the linux kernel and web browsers. But I can't imagine many normal programmers will want to build regular websites and apps in rust.

GC languages are slower, but credit where its due - they make it so much easier to just dive in and spend all your braincells thinking about your problem domain.

pjmlp 2021-08-17 04:32:10 +0000 UTC [ - ]

I have been in plenty of hills since I wrote those first BASIC lines on a Timex 2068.

Just like consumers don't care if their favourite music application is written in Electron, as long it plays their favourite album on their newly acquired speakers, they also don't care what type system was used to deliver such experience.

With Go finally getting generics, it will be pretty alright, 99% of distributed computing applications need not care about zero GC code, only proper use of value types.

zzt123 2021-08-17 01:43:21 +0000 UTC [ - ]

Your point about Go shining a lot more brightly when working with a team which has mixed skill levels was one of the design goals of Go, so it seems they’ve succeeded on that front if you’ve come to that conclusion independently!

silisili 2021-08-17 02:39:33 +0000 UTC [ - ]

IMO, Go is the easiest language to read by far. Python can be, but lets one use a lot of hard to figure out magic. Go is very much WYSIWYG, and to me that's its greatest strength. It's easy for anyone to jump in about anywhere. And that lends itself well to teamwork.

josephg 2021-08-17 07:01:27 +0000 UTC [ - ]

I find that's only true so long as the problem you're solving fits well into Go's view of the world.

If you want to make a data structure holding the equivalent of a parametric enum (or tagged union from C), go is very awkward to use compared with richer languages like Swift or Rust. Go is also awkward if you want to implement custom generic data structures.

Eg, this[1] code I wrote a couple years ago for doing text based operational transform became about 1.5x longer in Go compared to rust or typescript because its so awkward to express a parametric enum in go. And it was much harder to read & more buggy as a result. Sadly I lost the go version of the code. I'd be curious if someone with more go experience could do a better job, but I'm skeptical.

Hopefully the situation improves somewhat when generics land.

[1] https://github.com/josephg/textot.rs/blob/03c84b7c35a375ba7d...

throwaway894345 2021-08-17 12:37:30 +0000 UTC [ - ]

I agree that enums are the thing that Go is lacking. I would really like first-class enums perhaps even more than generics. Hopefully we get them.

readlikeasloth 2021-08-17 07:22:30 +0000 UTC [ - ]

> I can imagine Go shining a lot more brightly when working with a team which has mixed skill levels.

Thank you for expressing that thought. However: I came quite to the opposite conclusion. I´m currently working in a small team with members that are not so versatile in coding, coming from a JavaScript background. It has been surprisingly easy to teach them Spring Boot. You follow a typical layout and everything falls into its place. I have my reservation that the same holds true for Go. It feels like there is much more room for confusion. Do I use global functions or receivers? How do I inject my dependencies and where do manage them? How do I build constructors and do I really need them? I found surprisingly little advice on those topics.

tialaramex 2021-08-17 01:47:17 +0000 UTC [ - ]

> but there is still a big productivity gap between the borrow checker and GC.

The borrow checker and GC aren't really doing the same thing though. One very important distinction in some fields (but unimportant in others) is that GC only really cares about memory resources. So you are (or should be) doing explicit manual cleanup for all non-memory resources in a GC language. Rust isn't, in Rust we don't write explicit resource cleanup for a programmable interrupt controller, or a file, or a database connection -- the resource knows how to manage itself, and Rust promises to tell it immediately when it falls out of use, whereas a GC can't promise to ever clean up which is why Go doesn't even bother providing a means to do this automatically for your non-memory resources when they're garbage collected (Java does, but again, no promises it ever fires, so, don't rely on this).

throwaway34241 2021-08-17 02:27:55 +0000 UTC [ - ]

> Go doesn't even bother providing a means to do this automatically for your non-memory resources when they're garbage collected (Java does, but again, no promises it ever fires, so, don't rely on this).

It sounds like you are talking about finalizers? If so, they exist in Go in a similar way to the Java feature [1].

[1] https://pkg.go.dev/runtime#SetFinalizer

throwaway894345 2021-08-17 12:46:40 +0000 UTC [ - ]

Finalizers aren’t quite the same. They run when memory pressure exceeds some threshold, but the GC analog for borrow-checking is something that will clean up a resource when the pressure on the resource in question exceeds some threshold (e.g., file handles). And even then I’m not sure if that ticks all requirements for resource management.

throwaway34241 2021-08-17 20:24:58 +0000 UTC [ - ]

I meant to respond only to the quoted section comparing Java and Go, which states that Go doesn't have a way to do cleanup on GC (finalizers). I agree with you (and the two other replies and the quoted section itself) that finalizers are not a replacement for RAII etc.

I don't use them for resource cleanup, but I think they probably make sense for library authors as a back-up mechanism (for example, the standard library has a finalizer to close file handles, and the current runtime does GC every two minutes in the absence of memory pressure). You can also explicitly run the GC in Go, and while generally there's a lot of problems with that idea I could maybe imagine it being viable for certain unusual workloads.

beltsazar 2021-08-17 03:54:06 +0000 UTC [ - ]

You usually want to release non-memory resources deterministically, but finalizers in C#/Java/Go are not deterministic. That's why finalizers usually are not used for resource management. Instead, C# has using statements, Java try-with-resources, Go defer statements.

sagichmal 2021-08-17 03:24:32 +0000 UTC [ - ]

Finalizers are not guaranteed to run. They're not ersatz destructors.

throwaway34241 2021-08-17 03:45:02 +0000 UTC [ - ]

Yes, if you look at the quote I was responding to, it appears they are saying Go doesn't have finalizers, which is incorrect.

xpressvideoz 2021-08-17 06:52:46 +0000 UTC [ - ]

Huh, I also thought Go had no concept of finalizers. That is a bit... awkward way to set a finalizer. But thinking it again, it may just be due to my unfamiliarity.

pjmlp 2021-08-17 04:36:17 +0000 UTC [ - ]

That is a misconception, RAII like code can be achieved in two ways:

- defer (possibly add a go vet check for when missing it)

- when Go 1.18 brings generics, the withFunc pattern from FP where lambdas get given a resource released on function exist, thus having regions/arena like resource management

tialaramex 2021-08-17 11:36:29 +0000 UTC [ - ]

The description for go vet says, "it should be used as guidance only". Go vet is in fact a linter (go lint is also a linter, but one focused on style).

So what you're doing there is adding a control to try to mitigate the consequences of a language failing. Go doesn't actually prevent you from getting this wrong, but a linter such as go vet can flag cases where it suspects you screwed up, and maybe you'll catch the worst mistakes most of the time this way and by having usage conventions.

We shouldn't mistake this for equivalent capability. The complicated cases that tempt people to ignore or switch off such linting are exactly the cases most likely to have an undetected problem.

So this is "RAII like" only in the sense that "Just remember to do it properly" is RAII like, and we could make exactly the same claim for C.

pjmlp 2021-08-17 13:12:10 +0000 UTC [ - ]

Just like you need to use clang tidy to fix all the issues with writing proper C++ code.

tialaramex 2021-08-17 16:54:36 +0000 UTC [ - ]

Fair. However, there continues to be an unresolved tension in the C++ community about whether they want C++ to focus on its considerable legacy by ensuring enduring compatibility ("No ABI breaks, ever!") or to continue growing and changing even if that means not everybody can follow ("Performance trumps compatibility, ship it!"). Although we can anticipate some sort of compromise, it just isn't possible to have a C++ 2x that delivers everything the modernizers want yet still runs people's technically conforming C++ 11 code unchanged with that binary DLL they've got no source code for. Some people will be unhappy, maybe everybody in the C++ community will end up unhappy.

In the "no breaking changes" case I agree that C++ objects that live in the free store are in the same place as Go, the programmer has a responsible which they may not fulfil, to manage this resource and a linter can only help mitigate this problem.

But plenty of people including Stroustrup want to do lifetime management, despite potential breaking changes from that, and under lifetime management the compiler has visibility into your object lifetimes and can reject programs which inadvertently leak.

Now, that doesn't (can't) make leaks impossible, but it means any leak is now in some sense "on purpose" and would happen for GC'd resources too, it isn't just an accident. For example Rust's mem::forget will prevent the drop happening for the object you're forgetting, but it's not as though you type mem::forget() by mistake. You clearly wanted to achieve that (e.g. you stole the underlying Unix file descriptor from a File and sent it over a socket to a separate process, so now cleaning up that descriptor is the Wrong Thing™) and incorrect usage is not the same category of error as forgetting a with clause in Python.

deathanatos 2021-08-17 07:00:52 +0000 UTC [ - ]

defer (/using/with) all require the programmer to remember, and write, O(use site) times, the code to release the resource.

RAII destructors, on the other hand, do not permit the coder (at the use site of the type) to forget, as the destructor is invoked automatically when the variable goes out of scope.

In my time reviewing Python (also GC, "with" provides a similar functionality), this is a very common error.

pjmlp 2021-08-17 08:44:06 +0000 UTC [ - ]

Apparently you missed my go vet reference on purpose, just make all of us aware of it not being the same.

Same applies to using on .NET, where forgetting to call using on an IDisposable type can trigger a compiler error, via Roslyn plugins.

Also seem to be unaware how closure based RAII works on FP languages.

RAII isn't triggered on heap allocated objects unless smart pointers are used everywhere.

Python doesn't have a static analysis tool for with, nor does it support value types with stack allocation. Not all GC languages were born equal.

nemetroid 2021-08-17 09:00:51 +0000 UTC [ - ]

> RAII isn't triggered on heap allocated objects unless smart pointers are used everywhere.

It's triggered unless you have memory leaks.

pjmlp 2021-08-17 13:12:43 +0000 UTC [ - ]

So it isn't.

nemetroid 2021-08-17 16:36:25 +0000 UTC [ - ]

Correct, but except in very special cases, memory leaks are themselves considered bugs. Using memory leaks as an example of RAII not always working is, in my mind, similar to saying Rust doesn't prevent memory errors since it has the "unsafe" keyword.

pjmlp 2021-08-17 18:11:54 +0000 UTC [ - ]

Except when using system code you might have to pack pointers in OS IPC structures which don't map into smart pointers.

Similar to forgetting memory in Rust, and then there is no miracle RAII.

nemetroid 2021-08-17 18:46:45 +0000 UTC [ - ]

Yes, if you're not using RAII, RAII doesn't work.

throwaway894345 2021-08-17 12:44:17 +0000 UTC [ - ]

Agreed, but per my original post, the additional safety that Rust affords protects against a very small share of bugs in SaaS applications. The value proposition isn’t good because you trade so much productivity for a small improvement in quality, and productivity is at a premium and quality (sadly) can be mitigated in other, less costly ways (e.g., progressively rolling out new changes, continuous deployment i.e., bugs can be patched in hours rather than months, etc).

Zababa 2021-08-17 07:00:50 +0000 UTC [ - ]

I have the opposite view on the type systems: Rust's type system is really great for business logic because it's really expressive (based on algebraic data types), but the language being made for low level stuff makes some things a pain. If you do something like DDD, ADTs really shine, especially when combined with pattern matching. I do agree that when you can afford a GC, Rust can be annoying.

Blikkentrekker 2021-08-17 01:57:27 +0000 UTC [ - ]

> I really like Rust, but in my mind it's still relegated to the periphery of tasks that must be super fast (like C/C++, to your point) and/or low-level. Rust has made impressive strides at reducing the toil involved in compile-time memory management, but there is still a big productivity gap between the borrow checker and GC.

Even if it weren't for the performance I would use Rust over Go due to the lack of generics alone, as well as the associated general philosophy of Go that boilerplate is good and one should repeat oneself as much as possible.

throwaway894345 2021-08-17 12:59:30 +0000 UTC [ - ]

I really, really like type systems, but I’ve built enough software to know that the returns for most applications diminish after a Go-like type system is in place, and there are many other factors that are more important than type systems (and Rust gets a lot of these right too!) such as performance, productivity, tooling breadth and quality (especially build tooling and package management), ecosystem, deployment/distribution story, learning curve, readability, etc. Ultimately I’m of the opinion that as cool as type systems are, for most software they need to exist in service of productivity which means you need a basic type system that helps you but gets out of your way. Type systems can’t catch many classes of bugs that are increasingly prevalent in a world where our services are getting increasingly smaller (or rather, where more infrastructure and networking are being put in between the logical components of our application) and we’re often able to detect bug fixes before they impact a significant number of users (progressive deployments, automated rollbacks, continuous deployment, etc) but the discourse around type systems hasn’t caught up yet.

Blikkentrekker 2021-08-17 13:28:46 +0000 UTC [ - ]

It has nothing to do with catching bugs.

It has to do with that one sometimes has to write 80 lines of code in Go to to the same as 3 lines of Rust code due to having to repeat oneself all the time.

throwaway894345 2021-08-17 14:07:20 +0000 UTC [ - ]

That’s an even more superficial concern. No one is bottlenecked on the rate at which they can type.

erik_seaberg 2021-08-17 18:18:48 +0000 UTC [ - ]

Maintainers are bottlenecked by the rate we can (re)read. Boilerplate is the problem that makes us want powerful languages; if it didn’t matter we could have stayed with assembly.

Blikkentrekker 2021-08-18 02:59:28 +0000 UTC [ - ]

One especially is in the case where it's boilerplate code that does not rely on any real deep thought but writes itself, and with the possibility of bugs in it, most of all. Anyone can make a simple typo or forget something in the endless boilerplate.

It also gets in the way of reading code.

ithkuil 2021-08-16 23:17:13 +0000 UTC [ - ]

I hate to be "that guy" too, but coming from somebody who really likes Rust and is using it more and more (also at $dayjob now) we must admit that Go tooling is one step ahead. CPU profiler, allocation and heap profiler, lock contention profiler. It all comes out of the box.

Yes you have cargo flamegraph for profiling locally and you now have pprof-rs to mimick Go's embedded pprof support. But allocation heap profiling is still something I struggle with.

I saw there was a pprof-rs PR with a heap profiler but there was some doubt as to whether it worked correctly; to get a feeling of how that approach would work but without having to fork pprof-rs I implemented the https://github.com/mkmik/heappy crate which I can use to produce memory allocation flamegraphs (using the same "go tool pprof" tooling!) in real code I run and figure out if it works in practice before pushing it upstream.

But stuff you give for granted like figuring out which structure accounts for most used memory, is very hard to achieve. The servo project uses an internal macro that help you trace the object sizes but it's hard to use outside the servo project.

The GC makes some things very easy, and it's not just about programmers not having to care about memory; it's also that the same reference tracing mechanism used to implement GC can be used to cheaply get profiling information.

kouteiheika 2021-08-17 03:23:24 +0000 UTC [ - ]

> But allocation heap profiling is still something I struggle with.

Have you tried this one? https://github.com/koute/memory-profiler

nyanpasu64 2021-08-17 04:59:52 +0000 UTC [ - ]

How does this one compare to Heaptrack (which is a CLI/GUI memory profiler that supports C++ and probably Rust as well)?

kouteiheika 2021-08-17 07:11:12 +0000 UTC [ - ]

There are many differences, but the main ones are that it has less overhead when profiling, more thorough analysis features (Heaptrack's GUI is relatively simple compared to it), and the next version will have scripting capabilities for analysis.

the8472 2021-08-16 23:25:20 +0000 UTC [ - ]

> But allocation heap profiling is still something I struggle with.

Switch to a dumb allocator and then profile mmap calls or page faults? That should get you large allocations at least. It's a pretty crude proxy. The other allocation profilers I'm aware of cause significant slowdowns.

ithkuil 2021-08-17 07:49:33 +0000 UTC [ - ]

Sorry I don't understand what you're suggesting.

I currently intercept calls to malloc/calloc/realloc/... and capture stack traces. This way I know how much memory gets allocated for each allocation site. Since allocations usually go through constructor calls, the presence of a constructor in the stack trace can let you infer how many structures of a given type are been allocated. Knowing how big they are is more tricky since allocation for the whole struct and its parts doesn't have to happen entirely in the constructor (some structures like vectors and hashmaps can grow, some structures can collect data from other sources and then hold onto them, etc)

Furthermore to know how the live memory is broken down between object type and allocation sites, you also need to track freed memory. This is significantly more tricky to do efficiently. I currently take an allocation sample every N bytes being allocated and use a poisson process estimator to scale the total allocated bytes.

The only ways I know to account for in use memory is to track every single allocation or to add some extra space for every allocation where we record whether a block had been sampled and of yes, what was its corresponding allocation event.

Can you please elaborate more on your suggestion?

the8472 2021-08-17 15:17:37 +0000 UTC [ - ]

Looks like I misunderstood, I was suggesting something more indirect than tracing calls to malloc. If you're already doing that then I'm not aware of any better low-overhead solutions. There are high-overhead options such as valgrind's dhat.

bombela 2021-08-17 01:55:00 +0000 UTC [ - ]

What about valgrind? https://valgrind.org/

ithkuil 2021-08-17 06:48:55 +0000 UTC [ - ]

Valgrind is great, precise but slow.

The missing feature I was comparing was the ability of Go to provide good estimated heap and allocation profiles using minimal overhead on a production workload.

_008_jb_ 2021-08-17 03:18:21 +0000 UTC [ - ]

Ah, the desperate Rust promoter crew enters the Go thread ...

Rust is great for language enthusiats doing hobby projects or for learning. The Rust book is great. Rust has great language features.

Having that said, Rust is the right tool for the job for a very small niche. Basically, if you would have used C++ before and don't need much developer reach or mature libraries. And only for the rare cases you really cannot affort a GC (even though Go's GC is highly optimized).

Go on the other hand, is an industrial strength proven and mature general purpose language. It is the best fit for various kind of networking application. Especially APIs, but also infrastructure where you can live with an GC. CLIs are great with Go as well.

If you're working on a professional grade project (where the GC is acceptable), Go is much superior than Rust in all regards.

Rust is advertised for years and years and didn't have it's breakthrough yet. This empirical fact cannot be ignored. There're reasons for this, of course. Some are:

- Writing Rust consumes so much more mental power with so little gain. That mental energy should be directed to solving the problem.

- Go makes everything besides your problem at hand easy. You can focus on solving your problem, not fighting the Borrow Checker

- Rust's ecosystem is not reliable. Many essential libs are one-man-shows. Version 0.1 everywhere.

- Rust is for and by language enthusiats. If you need to rely on libs for longer than a couple of years it is a hight risk for your project

- In terms of real world performance: Go is so close to Rust that there're very very very few use cases that really need that marginal gain

asadawadia 2021-08-16 22:29:34 +0000 UTC [ - ]

why though? some perf gains?

DenseComet 2021-08-16 22:49:15 +0000 UTC [ - ]

Go isn't low level in the same way that Rust is. Both languages have their places, but Go seems to be more of a better Python/JS, whereas Rust is a better C++.

37ef_ced3 2021-08-16 23:38:16 +0000 UTC [ - ]

Go should be understood as a replacement for C and C++.

If you're writing a compiler, or some other project where extreme high performance is not necessary (e.g., if you're willing to be, say, a factor of 2 slower than C) then Go is a good choice.

Go's performance is excellent, and should be sufficient for all but the most demanding applications. It's not a crummy scripting language like Python/JS.

munificent 2021-08-16 23:58:04 +0000 UTC [ - ]

> Go should be understood as a replacement for C and C++.

I think Go is a good replacement for the things people used C and C++ for twenty years ago, but less of a replacement for the things people use C and C++ for today.

Back then, C/C++ was your default "write big server program that needs to go relatively fast" language, and Go is targeting that. But in the meantime, Java got fast enough and hardware got cheap enough that Python, Ruby, and JavaScript have also eaten into that domain.

Today, I see C and C++ used primarily for embedded work and games. I don't see Go as being a great fit for either of those.

zarzavat 2021-08-17 00:02:48 +0000 UTC [ - ]

Go is a replacement for a certain subset of C/C++ projects that are I/O heavy, e.g. web servers. However the GC precludes it from many uses of C/C++, e.g. you couldn't write an AAA game in it, or a kernel, or a toaster.

asadawadia 2021-08-18 14:13:39 +0000 UTC [ - ]

Yeah but how often do we meet toaster devs?

vp8989 2021-08-17 01:11:53 +0000 UTC [ - ]

GC is quite problematic for web servers too. Lots of people "solve" that by giving AWS more money.

theshrike79 2021-08-17 05:22:14 +0000 UTC [ - ]

Web server workers, by definition, don't run for extended periods. Making them the perfect candidate for GC languages.

  1) Get request
  2) Serve request
  3) Get GC'd
No memory issues.

vp8989 2021-08-17 11:39:38 +0000 UTC [ - ]

Serving web requests with ephemeral workers introduces some new problems though. Generally efficiency in this space comes from pooling, reusing connections, multiplexing etc ...

Ie. amortize expensive work over many requests.

Xenograph 2021-08-17 03:53:14 +0000 UTC [ - ]

Why is GC problematic for web servers?

vp8989 2021-08-17 11:33:25 +0000 UTC [ - ]

Because GC will pause threads while requests are being served, this leads to latency spikes at the higher percentiles.

It introduces a whole distinct type of complexity into operating the server.

The latency of a request to your server is not just caused by the code that is ran to serve that request (which is how people intuitively would think about it), it can be caused by GC pauses that are happening because of the memory pressure caused by previous requests.

dilyevsky 2021-08-16 23:07:52 +0000 UTC [ - ]

Python/Js are interpreted by default and not good system programming choices. Go is compiled. This comparison totally misses the mark. In fact Go was developed mostly as C++ but sometimes Python replacement at Google.

atombender 2021-08-16 23:39:53 +0000 UTC [ - ]

And yet Go seems to have not captured many C++ developers, but rather developers from languages like Python, Ruby, and JavaScript.

The Go team originally wanted to replace C++, especially inside Google, but they didn't succeed. According to googlers on Hacker News, very few projects inside Google actually use Go.

The reason Go attracted these kinds of developers is precisely that it isn't a systems programming language. Systems people want something like Rust instead.

pjmlp 2021-08-17 04:39:01 +0000 UTC [ - ]

Lack of generics kept most of us away.

You don't return to 1992 C++, thinking nah this is fine.

Companies like F-Secure are using Go for real system programming just fine.

https://www.f-secure.com/en/consulting/foundry/usb-armory

dilyevsky 2021-08-17 16:39:35 +0000 UTC [ - ]

Under wider definition of “system software” you can consider db tech (etcd, cockroachdb, digraph) and compute management software (kubernetes, nomad, docker) to be included into that. Which is where Go is doing just fine. Lower level stuff is harder but because of gc not lack of generics

pjmlp 2021-08-17 18:06:45 +0000 UTC [ - ]

GC is just fine for OS development, as proven by Xerox PARC, ETHZ systems used in production.

The problem is more of mentality and not having management willing to push it down unbelievers no matter what, that prefers to recycle UNIX clones instead to save money.

Midori powered Asian Bing for a while and even then the Windows team did not believe it was possible.

dilyevsky 2021-08-16 23:57:41 +0000 UTC [ - ]

It def convinced me. I wrote c++ for almost 10 year before switching to Go. Even after c++11 have come out it was still night and day. Anecdotally the team i left at google rewrote some of my stuff from c++ in Go that I didn’t get to at the time

_wldu 2021-08-17 00:27:26 +0000 UTC [ - ]

Same experience here. 10 years ago, I wrote a lot of C++, today I write almost exclusively Go.

vp8989 2021-08-17 01:20:28 +0000 UTC [ - ]

"The Go team originally wanted to replace C++, especially inside Google"

Im glad you said that and I was not just imagining that myself. I remember following the Go language closely in its earlier days and it was often spoken about as a "systems language" but it actually seems to have ended up settling as a language to write servers for people who are sick of OOP but still like their imperative C style code.

vips7L 2021-08-17 12:46:01 +0000 UTC [ - ]

In my opinion they should have pushed for D instead of Go. It’s leagues better as a systems language if you can tolerate GC or if you can’t you just don’t use the GC.

KronisLV 2021-08-16 23:30:21 +0000 UTC [ - ]

That's just one of the ways to think about it. Java, .NET (I felt like adding these two to expand on the comparison), Python and JS are all languages with a relatively high level of abstraction and large and useful ecosystems surrounding them. They're pretty popular for all sorts of application development, but all suffer from certain problems:

  - Java has lots of brittle reflection in some libraries and JDK can be finicky, especially with GC tuning
  - .NET needs a runtime, historically there's Mono, now there was .NET Core and now there will be just .NET, though in some cases there's also IL2CPP and so on
  - Python not only generally runs slow, but also has problematic package management, especially with vent
  - JS (Node in particular) has similar package management woes as well as really fast package deprecation
Go at least partially solves some of those problems, by being compiled, having decent performance, somewhat rich ecosystem and passable package management, all while the language remains usable.

Lots of applications and some tools will get written in Go because of this, because it's pretty reasonable to use in most cases.

In comparison, C, C++, Zig and Rust would all be better suited for systems level programming or embedded development - they typically let you write more performant and less memory hungry code, at the expense of foot guns and slower development.

kcartlidge 2021-08-17 20:29:36 +0000 UTC [ - ]

I do both C# and Go so have no axe to grind here. Just two small points though.

> .NET needs a runtime

It doesn't. With a single command .NET Core can produce stand-alone single-file cross-platform deployables needing no SDK, Framework, runtime, or other dependency on the server.

> Go at least partially solves some of those problems, by being compiled, having decent performance, somewhat rich ecosystem and passable package management, all while the language remains usable.

It does, and I've been a big fan of Go for a fair few years now. However every point made in that sentence applies equally to .NET too.

The one area where Go beats C# (and most others) hands-down for me is the build time. It's a whole order of magnitude (possibly several) faster than most alternatives.

asadawadia 2021-08-18 14:15:26 +0000 UTC [ - ]

>systems level programming or embedded development

Like building what?

erik_seaberg 2021-08-17 00:13:29 +0000 UTC [ - ]

GC tuning is painful, but it’s quicker and more reliable than rewriting code hoping for same result.

KronisLV 2021-08-17 06:51:19 +0000 UTC [ - ]

Partially agreed, many times it's just a patch over a codebase that's stuck only running on JDK 8 - people expect tuning to be sufficient when the code is slowly rotting.

I'd argue that most systems that won't die out eventually will need a rewrite, or alternatively, in the modern day we'll see more and more polyglotic systems popping up.

Therefore it always makes sense to explore the best options for a particular bit of development, regardless of whether it's Node, Python, Java, Go or something else.

omginternets 2021-08-16 23:23:52 +0000 UTC [ - ]

It hits the mark because it targets the niche that was once occupied by interpreted languages.

throwaway894345 2021-08-16 23:32:58 +0000 UTC [ - ]

But it wasn't just "interpreted languages". Really the only interpreted languages in "the niche" were Python and a little bit of Ruby, but there was also a whole bunch of Java, C#, and C++.

asadawadia 2021-08-18 14:12:39 +0000 UTC [ - ]

That is not really a reason. What can I not build in Go that is easy to do in rust?

rowanG077 2021-08-16 23:15:18 +0000 UTC [ - ]

No. More because Rust offers modern language features and it's much easier to get code working then in Go.

candiddevmike 2021-08-16 21:55:21 +0000 UTC [ - ]

Generics are hopefully coming February 2022 with 1.18.

nappy-doo 2021-08-16 22:07:52 +0000 UTC [ - ]

Important note: it's the preview release. Not everything will be done, there will be gaps. But the first parts of the work have already merged to master, and it's coming.

xwdv 2021-08-16 22:06:06 +0000 UTC [ - ]

This will be when I finally use Go for something then.

azth 2021-08-17 12:52:04 +0000 UTC [ - ]

Even with generics, it has so many issues:

* Error handling is error prone and awkward.

* No proper enums

* No sum types/pattern matching

* Null pointers exist

* Its interfaces are very awkward. There are superior implementations of what they tried to do in other languages.

* Subpar IDE experience, made worse by how interfaces are implemented. You need an IDE for any non-trivial project.

* Very awkward choice to have visibility be determined by the case of the first letter in the function or variable. Simple changing of visibility to public will require larger diffs if it's used a lot.

* No private visibility, only package private.

* Very crude import mechanism. Try to rename a package and see how the IDE fails to figure out what to change.

saturn_vk 2021-08-18 06:44:04 +0000 UTC [ - ]

Funny, I've only ever used vim for go projects big and small, and the experience was excellent

Wouldn't any visibility change require a large diff in all languages?

ggregoire 2021-08-16 22:49:57 +0000 UTC [ - ]

Have you tried "F2" (rename symbol)? It's amazing, it's like a "safe" find & replace all.

klodolph 2021-08-16 22:09:41 +0000 UTC [ - ]

Yes, I've been using F12 a lot too, just to dive into random projects. It's amazing how much this little feature means.

flohofwoe 2021-08-17 08:40:54 +0000 UTC [ - ]

Cross-platform Intellisense support in VSCode with the Microsoft C/C++ extension has improved a lot lately, especially in combination with the CMake Tool extension to automatically discover header search paths and other compilation settings (it used to be very brittle in the beginning on Linux and Mac).

But if you're coding C/C++ in a "proper" IDE like Visual Studio, CLion or Xcode, such features are expected to work out of the box. IME, Intellisense-like features are usually only a pain to setup in "non-integated" development environments like Vim or other text editors.

The_rationalist 2021-08-16 21:59:03 +0000 UTC [ - ]

If your reference point is C/C++ then sure.. Go is a progress, but give more modern languages a try, like Kotlin, they sure are a major ergonomic and featureful evolution over go

nestorD 2021-08-16 22:49:59 +0000 UTC [ - ]

That's the sad truth. Go is not leap and bound better on that front, it is C/C++ that has not catched up.

int_19h 2021-08-17 17:56:35 +0000 UTC [ - ]

This is more a limitation of VSCode than C++. Go to Definition for C++ works just fine in Visual Studio proper for many years now.

eweise 2021-08-17 05:37:03 +0000 UTC [ - ]

"you can press "F12" to jump to the location of a function definition" Seems like standard IDE functionality for at least 20 years.

_008_jb_ 2021-08-17 03:31:47 +0000 UTC [ - ]

When I started using Go I was more than sceptical. I had to mirgrate a CLI tool written in Kotlin (still my favorite language) because the memory consumption was to high.

After a while I actually understood why Go is such a successful language / ecosystem:

Go's priority is to make projects easier. It is doing so by all it's smaller and larger features respectivly skipped features. But one soon understands the big picture of the Go team. Go is designed by very experienced devs who knew what is important and what not.

In my Go projects, I don't have to worry about:

- Memory safety

- Tooling

- Performance

- Structure (once I understood the package desgin philosophie)

- Difficult syntax

- Concurrency

- Libraries, as we can do most with the standard lib

- Maturenes and stability

Instead I focus on the things that count:

- Solve the problem at hand

- Create correct, stable and maintable software

And as this was not enough, the Go team comes around the corner with an 5 % average performace gift.

Awesome.

systemvoltage 2021-08-17 05:06:03 +0000 UTC [ - ]

Not a GO dev, has the debugging experience improved?

theshrike79 2021-08-17 05:28:41 +0000 UTC [ - ]

Dunno, 20 years in the business and I still debug with print statements.

It's enough in 95% of cases and it works every time in every environment. In the time it takes me to get a debugger running and attached to a process, I've print-debugged it and fixed it already =)

maccard 2021-08-17 10:16:03 +0000 UTC [ - ]

> In the time it takes me to get a debugger running and attached to a process,

In any modern editor or IDE, getting a debugger running and attached to a process usually amounts to opening a folder, adding a breakpoint and pressing F5

ithkuil 2021-08-17 08:30:44 +0000 UTC [ - ]

Same here. I very rarely reach for a debugger. Especially with Go where you can recompile the binary very quickly.

I tend to reach for a debugger when doing post mortem debugging or when I work with C where that's usually the easiest way to get a stack trace when a program crashes.

That said, time travel / reverse debuggers can be quite useful indeed: see http://choly.ca/post/debugging-go-with-rr/

harikb 2021-08-17 06:55:41 +0000 UTC [ - ]

VsCode or IntelliJ with delv works beautifully for most common tasks. But don’t compare to JVM/Java. It does get trickier if you have many concurrent/goroutines but that is not unique to Go.

pxue 2021-08-16 22:47:41 +0000 UTC [ - ]

The go code I wrote in 2015 looks and works exactly the same way new Go code I'm writing today. Even with all the upgrades.

You have no idea how amazing that feels.

pjmlp 2021-08-17 04:42:21 +0000 UTC [ - ]

My C and C++ code for Windows 3.1 written in 1995 would compile just fine today on Windows 10.

ithkuil 2021-08-17 08:32:26 +0000 UTC [ - ]

I heard this a lot but I wonder if it's still really true. I mean, can I literally open the latest visual studio and open an old project, hit build and it will just work?

pjmlp 2021-08-17 08:40:56 +0000 UTC [ - ]

Naturally it depends on what you are doing, but for regular Win16/Win32 code that uses Windows type macros and no low level tricks pretty much so.

And even if you take Win16 out of the picture, Win32 exists since 1995.

ithkuil 2021-08-17 08:54:42 +0000 UTC [ - ]

I'm not doubting that the windows API has remained stable. I'd like to try if the tooling (e.g. IDE config format or makefile equivalent) has also received the same amount of attention to backward compat

pjmlp 2021-08-17 13:10:52 +0000 UTC [ - ]

Again, depends on what you were using.

Borland C++ or Watcom, probably not much help.

Delphi, C++ Builder, Visual C++ for Windows 95/2000 mostly work, naturally you might need to use the IDE wizards to upgrade project files, or fix tooling paths on the makefiles.

dbremner 2021-08-17 20:49:07 +0000 UTC [ - ]

https://github.com/dbremner/cake is a 16-bit Windows 1.0 application that I ported to Win64; most of the changes weren't required.

systemvoltage 2021-08-17 05:00:07 +0000 UTC [ - ]

As a person that gets anxiety from JS projects and how fast things move, I love this aspect of C. It puts the soul to ease.

steren 2021-08-17 05:13:04 +0000 UTC [ - ]

The JS code you wrote 20 years ago still runs today (and much much faster)

The problem is not JS, more the dependencies you decided to take for your project.

pjmlp 2021-08-17 06:40:06 +0000 UTC [ - ]

Indeed. Going back to my example, stuff written with OWL won't compile today.

maccard 2021-08-17 09:57:59 +0000 UTC [ - ]

The C++ I wrote in 2013 still looks and works the same as it did 8 years ago, and I can write code in the same way, or opt in to newer features in C++14/17 too. The C++ code that I wrote in 2007 (when I started writing C++) also still works.

2021-08-16 23:13:39 +0000 UTC [ - ]

rowanG077 2021-08-16 23:18:54 +0000 UTC [ - ]

Isn't that the case for the vast majority of languages though? The python,c,Haskell,js,language x code from 2015 works the same now as it did 5 years ago.

slownews45 2021-08-16 23:29:52 +0000 UTC [ - ]

python 2 -> 3 was not easy (aka a total nightmare).

By 3.6 it was pretty good though and even migrations and dual code bases were easier because they went back and allowed things like u"" for unicode strings to continue to mean unicode strings in python 3 - before that they'd actually blocked unicode in 3 that was working well in 2.x code bases! It was a total in your face move to break the ecosystem even though supporting u"" would have been almost no cost in terms of improving compatibility with existing code. ASCII handling (important for things like web tech stacks header handling etc) also improved thankfully. 3.x made things a lot harder initially (and slower too).

Also a lot of code doesn't look the same over time - the conventions (formatting) are either very poorly defined or change. Go seems to have pretty much one format from fmt

etc

fny 2021-08-17 04:28:10 +0000 UTC [ - ]

Python was a unique shit show.

int_19h 2021-08-17 17:59:02 +0000 UTC [ - ]

Python 3 should be considered a different language from Python 2, pretty much.

slownews45 2021-08-17 20:07:53 +0000 UTC [ - ]

The level on intentional compatibility breaking was crazy.

The fact that they went out of their way to break python 2 unicode when running on python 3 was just totally nuts. Especially after making such a big deal about unicode!

I've never seen anything like it I don't think? Maybe the new Perl that never really landed?

erik_seaberg 2021-08-17 22:12:38 +0000 UTC [ - ]

They’re still working on Raku, it just isn’t branded “Perl 6” anymore.

int_19h 2021-08-17 20:33:11 +0000 UTC [ - ]

Well, there were good reasons to change the way py2 worked with Unicode, and I don't think this could have been made right without severe breakage either way. The only thing that should have happened sooner is support for u"" literals.

slownews45 2021-08-17 22:00:00 +0000 UTC [ - ]

I had developed with unicode in mind using u"".

For some reason this was not good enough for the python 3 folks - they actively broke this code which was from folks who had SPECIFICALLy addressed unicode in their apps.

And yes, they could have supported u"" (and a number of other things).

They went - unicode is so critical we will break the world, and then for folks who had already supported unicode well or wanted to dual target a library they said your u"" approach to unicode is so bad we will break it.

Total BS in my book.

Strum355 2021-08-17 00:47:55 +0000 UTC [ - ]

Ive had a number of python libraries (and Im not talking small, lesser known ones) that would depend on (and break in the absence of) specific quirks in Python versions that made upgrading python an unnecessary hassle.

pxue 2021-08-16 23:33:11 +0000 UTC [ - ]

For JS, es6/ecma2015 -> ecma2021, lots of language changes.

rowanG077 2021-08-16 23:43:16 +0000 UTC [ - ]

Of course changes but afaik it's backwards compatible.

ricardobeat 2021-08-17 01:41:46 +0000 UTC [ - ]

I don't think the OP was making a point about backwards compatibility, but how the language itself has remained stable (sign of good design?). That is not the case for the majority of languages, except for maybe Ruby. Javascript in particular looks nothing like code from 6 years ago.

systemvoltage 2021-08-17 05:04:25 +0000 UTC [ - ]

Furthermore, you rarely just plain JS. It's always another behemoth framework such as React or Angular.

I challenge anyone to upgrade a project from Angular ver 5 to the latest. It's possible but you'll also question whether its possible for you to quit software engineering and learn woodworking.

37ef_ced3 2021-08-16 21:56:45 +0000 UTC [ - ]

My appreciation for Go has been monotonically increasing for years.

The more Go code I write, the more I like the language, the tools, and the standard library.

Low-friction, high-quality software development.

If only I could use it professionally, instead of C++.

theshrike79 2021-08-17 05:26:24 +0000 UTC [ - ]

Go is the choice of professionals IMO.

When you have a team of varying skill levels who need to deliver shit that works on a schedule and still have some kind of performance, you use Go.

Even more so if you need to hand off the maintenance to a team of randoms from god knows where and THEY need to be able to keep it running and add new features.

It doesn't have the fanciest new EXPRESSIVE operators or anything exciting really. It just does the job, albeit verbosely.

You can do fancy shit on your own time.

int_19h 2021-08-17 17:59:43 +0000 UTC [ - ]

I would argue that e.g. decent error handling is not "fancy shit".

theshrike79 2021-08-17 18:38:54 +0000 UTC [ - ]

It's perfectly decent, it forces you to explicitly handle or ignore every error.

It's verbose as all hell, but on the other hand you'll notice immediately if it's missing.

Go is not a "fun" language by any means, but that doesn't mean it's not a productive one.

int_19h 2021-08-17 20:32:15 +0000 UTC [ - ]

I don't see how it forces you to ignore? You don't have to be explicit to ignore the returned error code.

theshrike79 2021-08-18 08:29:43 +0000 UTC [ - ]

That's what the underscore is for, explicit ignore. And it's a huge code smell.

You instantly notice it because the if err != nil -template is missing after it =)

int_19h 2021-08-18 09:08:14 +0000 UTC [ - ]

Sorry, I didn't word that well. What I meant is that you can ignore the returned value altogether, including the error.

https://github.com/golang/go/issues/20803

In contrast, e.g. Zig requires you to use _ if you want to ignore.

kubb 2021-08-16 23:43:31 +0000 UTC [ - ]

I’ve been using Go at a large software company for 4 years. It does the job, my biggest complaints are the low capacity for abstraction, verbosity and ergonomic pain points. It can do certain things well, but in a large enough project you’ll inevitably find that it doesn’t give you the best tools for certain types of problems.

I don’t know what to think about the upcoming generics. It feels late to make such a big change so long after the language has been established. At least a plan for it should have been integrated into the language from the start.

It feels like a missed opportunity - an effort with similar funding but a more sound theoretical foundation than being Newsqueak 3.0 could have become an industry game changer. Instead it fills a niche, which is a success, albeit smaller.

vp8989 2021-08-17 01:27:19 +0000 UTC [ - ]

"It feels like a missed opportunity"

I agree with that. I think looking back on it, Go arrived on to the scene at a perfect time where lots of people were rearchitecting so there was lots of natural appetite in the industry for a new language. Go vacuumed up a lot of this opportunity but ultimately hasn't really evolved the practice of software engineering all that much.

erik_seaberg 2021-08-17 05:39:23 +0000 UTC [ - ]

> feels late to make such a big change

This is recoverable. The Java world was also saddled with a decade of awful pre-generic code, but we burned almost all of it to the ground and started over.

int_19h 2021-08-17 18:01:01 +0000 UTC [ - ]

The need to tackle generics later resulted in design decisions such as type erasure, which even today are still limiting the evolution of Java.

erik_seaberg 2021-08-17 19:59:11 +0000 UTC [ - ]

Yeah, that was an unforced error. Fundamental changes to a language can and should rely on upgrading deployed runtime libraries.

zozbot234 2021-08-17 14:48:27 +0000 UTC [ - ]

> an effort with similar funding but a more sound theoretical foundation than being Newsqueak 3.0 could have become an industry game changer.

That's a pretty good description of Rust 0.x. Might ring a bell.

avl999 2021-08-16 22:09:30 +0000 UTC [ - ]

Go is my favorite language to write code in. I was very skeptical when I first started learning it for a new job with error handling that looked archaic in comparison to exceptions but once I got the hang of it it has quickly become my preferred language of choice for any project.

Once generics are finally added the language should basically be a no-brainer for any serious production code.

ridiculous_fish 2021-08-16 23:44:24 +0000 UTC [ - ]

From a distance it looks difficult to write in. For example, two recommended ways to delete from a slice:

    a = append(a[:i], a[i+1:]...)
    // or
    a = a[:i+copy(a[i:], a[i+1:])]
Both seem harder than necessary.

Is that code just idiomatic, and Go programmers recognize it instantly? Or maybe they don't deal with slices that often?

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

benhoyt 2021-08-17 00:06:17 +0000 UTC [ - ]

With the introduction of generics in Go 1.18 and the accompanying "slices" package (https://github.com/golang/go/issues/45955), we might soon write that as:

  a = slices.Delete(a, i, i+1)
That said, in code I write, I rarely need to do an in-place delete from a slice. I think it's rare enough that recognizing the idiom is okay.

lanstin 2021-08-18 17:54:09 +0000 UTC [ - ]

Because in-place delete is slow and you want to avoid algorithms with a lot of in-place deletes. So it's ok to have it be awkard, it's like the names of rare elements being longer than the names of common elements, not an issue.

azth 2021-08-17 13:05:08 +0000 UTC [ - ]

It's so weird why they're adding a slices package instead of making them functions on the slice itself. They did the same with strings.

Compare

    strings.ToUpper(strings.Replace(strings.Trim(s), "a", "b")))
Instead of

    s.Trim().Replace("a, "b").ToUpper()

benhoyt 2021-08-17 21:16:28 +0000 UTC [ - ]

For better or worse, this is by design. See: https://golang.org/doc/faq#methods_on_basics

It's "to avoid complicating questions about the interface (in the Go type sense) of basic types". It also allows separating the builtin functions, of which there are very few (they have to be in the core language spec), from the stdlib functions like strings.ToUpper, which there are many many more of and are added to more quickly than the builtins.

azth 2021-08-17 22:41:21 +0000 UTC [ - ]

I'm not sure what their answer means. They used a similar hand wavy answer about why the language has null pointers. "Complication" here refers to how complicated their implementation of the compiler is, not the end user complication, which is what they traded off (simpler compiler for more complex user code).

I didn't get the last point. builtin functions are there (for a big part) because the language doesn't have generics. Functions on types can be added without affecting the interface, since strings don't implement any interface (at least not on purpose, another problem with golang). Java constantly enriches standard library types with more useful methods.

barsonme 2021-08-16 23:53:20 +0000 UTC [ - ]

No, you deal with slices all the time. You just recognize the idiom.

You can restructure it if you don't need to preserve order:

    a[i] = a[len(a)-1] 
    a = a[:len(a)-1]
In general* Go does not hide complexity from you. It's a blessing and a curse.

*: because the Internet is a nitpicky place.

woodruffw 2021-08-17 00:31:57 +0000 UTC [ - ]

This has been my experience as well: I had to fix a bug in a Go codebase recently, and the fix required me to de-duplicate an unordered collection of elements. It turns out that there's (1) no built-in way to do this, and (2) there isn't even a built-in set structure in Go, so you can't do the obvious solution without explicitly using a map with a chaff value. Stack Overflow has dozens of duplicate questions for this, all with ridiculous O(n^2) or buggy (or both) answers.

jrockway 2021-08-17 00:57:51 +0000 UTC [ - ]

map[T]struct{} is the set type in Go. (Note that struct{} is represented with 0 bytes of memory, so it's not chaff.)

You might like the slices proposal for utility functions like these: https://github.com/golang/go/issues/45955

woodruffw 2021-08-17 01:16:59 +0000 UTC [ - ]

Thanks. I wasn't aware that struct{} is zero-sized, so that plugs that hole.

I'll admit freely that I'm not a proficient Go programmer, so it's easier for me to see the things I don't like than the things that Gophers praise. But even this solution leaves me unsatisfied, in contrast to what I'd reach for in Rust or even C++: it requires that I know that struct{} is zero-sized, and I still have to do the manual legwork of writing a CS101 dedupe function.

tialaramex 2021-08-17 02:00:09 +0000 UTC [ - ]

Zero Size optimisations are a thing in Rust too though, and I don't think an empty struct being zero size is any more surprising than Rust's empty tuple being zero size.

Like, if you tell Rust you want 4 million empty tuples in a vector, it will give you a vector with exactly 4 million empty tuples in it and no heap allocation because those empty tuples don't take space, so, 4 million of them also doesn't take up any space.

woodruffw 2021-08-17 02:07:35 +0000 UTC [ - ]

The key difference being knowledge: I don't have to know `HashSet` is really just a map with a ZST to use it efficiently. All I need to know is that I don't need a value, and the ecosystem obliges me. This in contrast to Go, where I need to know that `struct{}` is a ZST, that the "right" way to do a set is to use it as the map value, &c.

typical182 2021-08-17 02:40:13 +0000 UTC [ - ]

There is also a WIP Go proposal for adding a set to the standard library using the upcoming generics:

https://github.com/golang/go/discussions/47331

bombela 2021-08-17 02:01:02 +0000 UTC [ - ]

Fun thing, is Rust employs the same trick. The HashSet<T> is an alias for HashMap<T, ()>. "()" is the unit type. Which is zero-sized.

Because Rust has generics, it is more ergonomic though.

https://doc.rust-lang.org/std/collections/struct.HashSet.htm...

kjksf 2021-08-17 00:10:21 +0000 UTC [ - ]

You can find things to complain in any language, including the languages that YOU use.

For every complain like that in Go I'll find a hundred in C++, ten in Java / JavaScript / Python.

As to your question, as a full-time Go programmer: this doesn't come up often.

When it does, I google "slice tricks" and copy & paste the formula.

It'll be fixed by next release because generics will enable writing a function that does that and that function will be in standard library.

Why wasn't this fixed before generics? Because the only way to do it would be via a compiler built-in function and Go team is rightfully reluctant to bloat the language / compiler for minor functionality like that.

ridiculous_fish 2021-08-17 02:51:48 +0000 UTC [ - ]

Oof, I have no desire to spark a language war. Every language has its warts for sure.

It seems strange to not provide slice.erase as a primitive operation, and instead to recommend implementing it in terms of append. Usually removing from a vector does not require allocation. Still it may be for the best with upcoming generics, fewer builtins is good.

_ph_ 2021-08-17 08:15:00 +0000 UTC [ - ]

This is an interesting point. On the first glance, it looks like a very obvious missing piece and having to hand-write such expressions feels like unnecessary painful.

On the second glance, one can see why probably it was left out: there are two fundamental different ways to implement the deletion. You can copy the last element into the slot of the deleted element, or you can move all elements after the deleted one place to the left. The latter is what you implemented and preserves the ordering, which often is desirable. The former is way more efficient, if you don't need to preserve the order. A default implementation would probably choose to preserve the order and thus introduce a systematic inefficiency which is easy to be avoided.

Fortunately, with the introduction of generics and especially the new slice package, this discussion becomes moot.

bilinguliar 2021-08-17 06:51:01 +0000 UTC [ - ]

It is not only hard for you, but it is also hard for a computer. And Go expresses this perfectly. Once you hide this behind a convenient `slice.Delete(x)`, you may end up with a codebase that bleeds performance.

avl999 2021-08-17 02:39:01 +0000 UTC [ - ]

Dealing with any kind of collections will often require you to write more code than you are used to, see the other post in this thread about someone complaining about a lack of Set type. Most of these problems are due to the fact that the language doesn't have generics yet, however generics are coming soon and once they do arrive we will have a robust collection and concurrent collection library to alleviate for most of these problems.

For now you just have to accept these quirks as part of the tradeoff of working with the language. I don't find it that big of a deal esp with the solution on the horizon.

jdsleppy 2021-08-17 00:10:06 +0000 UTC [ - ]

With two years of working in Go at two companies, I have never had to write that. I can't remember writing the equivalent in Python over even more years, either. The 99% case is building a list/slice, not removing from one.

Have you ever had to remove elements from a list/slice?

ridiculous_fish 2021-08-17 01:30:01 +0000 UTC [ - ]

Yes, a sorted vector is a common data structure for frequent queries and infrequent updates.

jrockway 2021-08-17 01:00:24 +0000 UTC [ - ]

gopls seems to be integrating some of the "tricks" into editor completion. This one isn't there, but other idioms are. (With gopls > 0.7.0, it's enabled by default. Details: https://github.com/golang/tools/releases/tag/gopls%2Fv0.7.0)

yobert 2021-08-16 23:51:00 +0000 UTC [ - ]

I use slices all the time, but very rarely need to delete out of the middle of them. So it seems like a bit of an edge case to me.

matthewmacleod 2021-08-16 22:31:51 +0000 UTC [ - ]

A couple of nice tweaks that I'm happy to see.

I didn't completely expect it, but I now find myself reaching for Go first when writing something like a small script or utility that I'd previously have written in something like Ruby or Python. It all feels much more solid.

That said… I really chafe against the anaemic type system when writing anything a larger than that. I hate not being able to communicate things like "this can never be nil and if it's is then it's an error" to the compiler. I resent having to write three lines of boilerplate every time I want to reverse a flipping 10-item slice, and the design decisions leading to the existence of `time.IsZero()` are bordering on criminal.

I don't know if I'm the odd one out for feeling like that – parts of the language and ecosystem are just absolutely wonderful to work with, but the unergonomic sharp edges bother me so much that I end up finding it really _annoying_ to write a lot of it.

jrockway 2021-08-17 01:05:51 +0000 UTC [ - ]

> I resent having to write three lines of boilerplate every time I want to reverse a slice.

Up above I talk about gopls's postfix completions, and reversing a slice is one: https://github.com/golang/tools/releases/tag/gopls%2Fv0.7.0

cpeterso 2021-08-16 23:39:33 +0000 UTC [ - ]

> the design decisions leading to the existence of `time.IsZero()` are bordering on criminal.

What's the rationale for Time.IsZero()? Seems like every use case for Time.IsZero() I think of is a code smell. The 1970 epoch is an implementation detail that should be hidden or configurable. People probably use IsZero() as a sentinel value to indicate "undefined time", even though the 1970 epoch is a valid point in time.

kjksf 2021-08-17 00:24:12 +0000 UTC [ - ]

.IsZero() is a pragmatic solution given the constraints of the system (i.e. Go language).

Go has a notion of "zero value". When you don't assign a value explicitly it'll be set by the compiler to "zero value" of that type.

This is much better than C/C++ of "random value".

For primitive types, the compiler decides what "zero value" is. For structs, each component is set to its zero value.

For good reasons (language simplicity) Go doesn't allow the user to declare what the zero value is (something that you can do in e.g. C++ via a constructor).

Time is a struct. Its zero value is the same as for any other struct and not meaningful time value.

It's a pragmatic necessity to be able to query "is this time value an unset time value?". A pragmatic solution for this need is to provide IsZero() method on Time struct.

jrockway 2021-08-17 01:03:49 +0000 UTC [ - ]

Go's zero time is not at 1970. It's at year 1.

    // IsZero reports whether t represents the zero time instant,
    // January 1, year 1, 00:00:00 UTC.
    func (t Time) IsZero() bool {
            return t.sec() == 0 && t.nsec() == 0
    }

machinecontrol 2021-08-16 21:45:23 +0000 UTC [ - ]

It looks like the language is really maturing. The changes are all fairly minor optimizations and improvements to the stdlib. Impressed with the Go team’s discipline for simplicity and stability.

est31 2021-08-16 21:57:57 +0000 UTC [ - ]

Between the 1 release in 2012 and the 1.13 release in 2019, this has been the development model of the language. So it's nothing new in Go's evolution.

harikb 2021-08-16 21:57:16 +0000 UTC [ - ]

> Module authors may deprecate a module by adding a // Deprecated: comment to go.mod, then tagging a new version. go get now prints a warning if a module needed to build packages named on the command line is deprecated.

Very good for the ecosystem. Nudge folks to upgrade from broken/insecure versions

leo_bloom 2021-08-16 22:00:34 +0000 UTC [ - ]

> We’ve also introduced pruned module graphs in this release. Modules that specify go 1.17 or higher in their go.mod file will have their module graphs include only the immediate dependencies of other Go 1.17 modules, not their full transitive dependencies.

This is a very welcome change and make the go.mod much more obvious to understand. Hooray!

dang 2021-08-16 22:13:13 +0000 UTC [ - ]

Two recent related threads:

Go 1.17 Beta - https://news.ycombinator.com/item?id=27462884 - June 2021 (118 comments)

Go 1.17 is deprecating the traditional use of 'go get' - https://news.ycombinator.com/item?id=27630625 - June 2021 (215 comments)

peterohler 2021-08-17 00:15:09 +0000 UTC [ - ]

Just tried [OjG](https://github.com/ohler55/ojg) and saw anywhere from zero to 20% faster on the various benchmarks. Nice improvement overall. Hats off to the golang maintainers.

tech_dreamer 2021-08-16 21:53:04 +0000 UTC [ - ]

Golang release no longer excites me in a good way - I know my old aws hosted apps continue to work, as do my pet projects

colesantiago 2021-08-16 21:56:41 +0000 UTC [ - ]

are release notes supposed to be exciting?

staticassertion 2021-08-16 22:01:29 +0000 UTC [ - ]

I think they mean "no longer excites me, in a good way"

xwdv 2021-08-16 22:05:39 +0000 UTC [ - ]

Usually they fill me with dread.

skybrian 2021-08-16 21:56:17 +0000 UTC [ - ]

Apparently type parameter compiler work was supposed to get merged just after 1.17. [1] Does anyone know if it’s still on?

[1] https://groups.google.com/g/golang-dev/c/U7eW9i0cqmo/m/ffs0t...

nappy-doo 2021-08-16 22:01:57 +0000 UTC [ - ]

The merge has already happened. The generic preview is going to be in 1.18 (early 2022).

4ad 2021-08-16 22:06:04 +0000 UTC [ - ]

It's already in, it has to be requested by the user with `-gcflags='-G=3'`. Of course, the feature is in heavy development and not ready for production use, YMMV.

rawoke083600 2021-08-17 06:57:15 +0000 UTC [ - ]

Lol Good Little Go ! :) Still one of my favourite languages whenever "heavy lifting" or concurrency is required (really they make it stupidly easy).

Now add pattern matching please ! Well done to everyone that worked on Go ! It's still a pleasure to use.

kplex 2021-08-16 21:51:13 +0000 UTC [ - ]

Worth it if only for the go.mod tidy-up, how that made it into a release in the first place baffles me.

2021-08-16 22:02:11 +0000 UTC [ - ]

zz865 2021-08-16 21:59:06 +0000 UTC [ - ]

Anyone seen jobs for business applications? Go seems to have a sweet spot for systems. I'd like to replace Java at my work but no one else is doing it.

pgwhalen 2021-08-16 22:36:43 +0000 UTC [ - ]

I use (and enjoy using) both Go and Java at work, and Go is in my opinion very much a Java replacement more than it is, say, a Python or C++ replacement. It is absolutely suitable for business logic, and I've never been quite sure what its designers mean when they say it's a systems language. Yes, you can squeeze great performance out of it, but you can with Java as well.

The way I see it, both Go and Java are crawling towards the same ideal: a compiled, statically typed, garbage collected, modern enough program language targeted for a majority of back-end concerns. Go has to close the gap mostly just with generics; and Java with memory layout (Valhalla), concurrency (Loom), and ergonomics (Amber); but they will both be there in 3-5 years.

Take this with a grain of salt if you like, but while I probably find it more fun to program in Go, I don't think there's a compelling reason to switch a large codebase from Java to Go. It often feels like my company is needlessly divided by programming languages even though they solve fairly similar problems. It's one thing to move off of a dying language, but Java is certainly not that.

zz865 2021-08-17 05:02:27 +0000 UTC [ - ]

Thanks, that sounds about right. Throw Rust in there too, its an interesting time.

theshrike79 2021-08-17 05:32:03 +0000 UTC [ - ]

The difference between Java, Go and Rust is that you can teach the first two to any fresh out of school CS grad in a week or two and have them be productive.

Not so much with Rust.

pgwhalen 2021-08-17 16:32:51 +0000 UTC [ - ]

Rust is a great language too (I'm in the process of learning it), but I would definitely not put it in the same category as Java and Go in terms of the problems it is good at solving. It fits much more with C/C++.

There is a huge class of problems that don't need the precision (especially around memory) that Rust demands from the programmer.

jdavis703 2021-08-16 22:03:07 +0000 UTC [ - ]

While we’re not building a “business” application we are building a large web application on Go. It’s definitely not just for systems work.

debarshri 2021-08-16 22:01:47 +0000 UTC [ - ]

When I used to work for traffic enforcement company, their whole backend was written in golang.

denysvitali 2021-08-17 06:27:59 +0000 UTC [ - ]

> Go 1.17 adds support of 64-bit ARM architecture on Windows (the windows/arm64 port). This port supports cgo.

Yay!

hakube 2021-08-16 23:56:48 +0000 UTC [ - ]

How do you add dependencies and in Go? I'm still confused. I use the VSCode extension and the code I wrote that uses external packages gets deleted when I save my file even though the package is installed

jizzlepizzle 2021-08-17 00:08:21 +0000 UTC [ - ]

the vscode extension automatically formats your go file and removes any packages that aren't used in the code.

https://code.visualstudio.com/docs/languages/go#_formatting

galleywest 2021-08-17 00:03:33 +0000 UTC [ - ]

Are you using the imports in your file? The VS Code Go addon has "format on save" enabled by default and Go will not compile if there are unused imports - so VS Code deletes the unused imports on save by default.

Otherwise try a "go mod init" in the root of your project.

Techasura 2021-08-16 21:52:01 +0000 UTC [ - ]

Never tried to write Go before, maybe it’s time that I stop by.

itake 2021-08-16 21:53:23 +0000 UTC [ - ]

what about this release makes you want to try it?

nwmcsween 2021-08-16 22:33:43 +0000 UTC [ - ]

It's meh for a language, it's simple which is what they are trying to achieve but it all comes with trade offs like all programming languages. I would say a large portion of a language is how you mesh with the syntax and tooling around it.

theshrike79 2021-08-17 05:35:25 +0000 UTC [ - ]

Go being "meh" is one of its best parts.

It doesn't encourage you to do fancy language tricks, it kindly directs you to get your shit done, compiled and delivered.

2021-08-17 02:49:26 +0000 UTC [ - ]