Live data from Hacker News

The State of Go

talks.golang.org

361–370 of 402 posts

Re: The State of Go

#361

Earlier quoted context omitted.

Not that I know of, but Go is super easy to learn. You will get a grip of the basics and be productive over a few hours or weekend at most, and one or two hobby projects later you'll learn to appreciate the stuff I'm talking about. BTW I've only felt I really knew the language and its inner workings and idioms deeply - after much longer, maybe a year and 2-3 big projects. But you get very productive very fast.

Not only that, but rarely am I supporting a library that I'm the sole developer on. Go takes away so much "individuality" of code. On most teams I've been on with Python and Java I can open up a file and immediate tell who wrote the library based on various style and other such. It's a lot harder with Go and that's a very good thing.

Yeah, at times the obsession of gophers with "idiomatic code" is annoying. But when you read someone else's code and it's idiomatic, you almost immediately have an intuitive sense how it works and can understand and modify it easily. Compared to Java OO code where you have to start chasing inheritance layers to find out where the piece of logic you're actually interested in resides...

Re: The State of Go

#362
post #340

Earlier quoted context omitted.

> So it's not constrained to operating systems but it's pretty much constrained to operating systems. You can see how I'm less than convinced. It's just that operating systems is not just the kernel. The POSIX userland of tools (ls, cat, ps, etc) are also systems programming, and essential part of an OS. And of course device drivers (which they even get linked or loaded directly to the kernel). Postgres, Varnish, red…

The definition of 'systems programming' is certainly wooly (and predates all of your examples) but I don't think it's an iterative stochastic hairsplitting fractal, like you're proposing. You've basically argued your way to a corner in which varnish and apache are not 'systems programming'. That's not a sensible use of the term.

If Apache is "systems programming" then why not one of the various web servers written in Lua or Python or whatever?

Are those then "systems programming languages" too?

Re: The State of Go

#363

Earlier quoted context omitted.

It does though. Maps and folds - and functional programming in general - focuses on the /what/. For-loops on the other hand tell the compiler / cpu the /how/. Summing is a nice example. A naive for-loop will just iterate through the list, keep a variable around, add the next item in the list to it. In functional programming (or with a reduce), you tell it to sum in a more concise way - but more importantly, you don't…

Consider product as a counter-example. With an imperative loop, it's easy to add an early-out condition if zero is encountered. But this is harder to do in a (strict) functional language.

This is exactly why lazy evaluation is often described as control-flow. Lazy evaluation permits efficient composition, without doing extra work (although the constant factors become much larger).

Re: The State of Go

#364
post #359

Earlier quoted context omitted.

Functions are trivially composable, while imperative loops are not. The difference that makes in terms of maintainability and readability of non-trivial applications is hard to overstate. Writing code in terms of small, composable, reusable functions with a clear single intent (through good naming), that you can then mix, match, and reuse to compose into larger functions is what makes good functional code so much eas…

"Writing code in terms of small, composable, reusable functions with a clear single intent (through good naming), that you can then mix, match, and reuse to compose into larger functions is what makes good functional code so much easier to write, test, and reason about than imperative code." Absolute, that holds for non functional code as well. It just does not matter whether the smallest function inside that system…

> It just does not matter whether the smallest function inside that system of functions has a loop or a map inside it. Loops are as easy to tuck into reusable functions as maps or anything else.

This is certainly true! You can definitely wrap all your loops in functions that take in a collection as argument, and return another collection or value, and as long as you don't produce any externally observable side effects with your loops, these functions are as good as any other as building blocks of a functional program.

Consider the age old adage: "If a tree falls in the forest and nobody hears it, did it actually fall?", similarly, "If a function mutates some internal state only visible within its own scope (a counter or accumulator in an imperative loop, for instance), did it actually mutate anything?" The practical functional programmer would answer with a resounding "no". In fact, RamdaJS itself is implemented mostly imperatively internally for performance/compatibility reasons, but exposes a functional API for the consumer. Similarly, Clojure is implemented the same way for many of its core functions, and exposes an easy way for users to perform mutations for similar purposes within the functional, immutable-by-default language using transients: https://clojure.org/reference/transients

If you use imperative loops by wrapping them in this way, however, note that you're essentially implementing the same function signature of a function that calls map/filter/reduce on a collection, but with imperative primitives, and that's definitely a valid approach. If you build your programs by writing and composing these kinds of functions, you are in fact doing functional programming, and can reap all the composability, maintainability, and testability benefits that come with it.

I don't think there's any room to debate that most developers don't use imperative loops in this way, however. And my point was that the way they're usually used was not trivially composable. Of course, you could always refactor them into small functions that are trivially composable, but that they need to be to become composable is why I prefer using trivially composable primitives like map/reduce/filter to implement my programs as a default, and optimize specific functions with imperative implementations on an as-needed basis after profiling and identifying all the critical paths (premature optimization, and whatnot).

RE: `var sumOfSquares = pipe(map(square), reduce(add, 0));` not reading fluently.

This is where we'll have to agree to disagree. To me that reads quite literally as "given a collection, return the square of each item, and add the result of each to the next, starting from 0, to return the final value". The functional approach could definitely look more intimidating to readers without any general knowledge of functional primitives and what they do, but that's an issue of familiarity, rather than one of inherent readability.

Yes, people can get crazy with nesting multiple inline composed functions inside of other inline composed functions on one insanely long line, and that can quickly get out of hand in terms of maintainability and readability, just as people can get crazy with nesting loops. But that's just a case of bad functional code that needs to be refactored using a composition of smaller, well-named functions broken down into multiple lines. Nobody is claiming functional programming is a panacea for bad code.

Re: The State of Go

#365

Earlier quoted context omitted.

> When I moved from Python to Go not only did my code become more safe, I actually became a better programmer. This is probably more due to the fact that you moved from a dynamically typed language to a statically typed one rather than this new language itself.

The type system is an integral part of the language.

That was true until things like optional typing as seen in flowtype/typescript came along.

Re: The State of Go

#367

Earlier quoted context omitted.

Because the stdlib is using them more and more? As someone mentioned, even closing an already closed channel can panic. Calling the random number generator with a negative limit panics. And there’s nothing like checked Panics so you’d know if one will happen or not.

As I said, if your code is plainly wrong then I see no problem in it going haywire. The channel rules are widely known -> you know, it takes more than a few days to learn a language, there are a few rules to learn too. Calling a number generator with a negative limit is also a programmer fault, so no reason to provide an error here.

Yes, there is no problem as long as you agree with X about what is a "real" error and what isn't.

X includes

the language authors (who are using this more and more in the stdlib as pointed out elsewhere)

the authors of any library you use ... but

transitively, so this includes the authors of any library you use indirectly as well

Hmmmm ... what was the problem with (unchecked, or python's) exceptions again ?

Re: The State of Go

#368
post #340

Earlier quoted context omitted.

The definition of 'systems programming' is certainly wooly (and predates all of your examples) but I don't think it's an iterative stochastic hairsplitting fractal, like you're proposing. You've basically argued your way to a corner in which varnish and apache are not 'systems programming'. That's not a sensible use of the term.

If Apache is "systems programming" then why not one of the various web servers written in Lua or Python or whatever? Are those then "systems programming languages" too?

Nope.

Re: The State of Go

#369
post #250

Earlier quoted context omitted.

Well Apple stopped supporting 10.8, so why should Go continue to support it? ARM6 support is a lot of work, actually, as IIUC a lot of stuff that the chip doesn't support needs to be done in software, and we don't have a reliable platform for testing against ARM6.

I guess I am a bit more upset about ARM6. How were you testing before? Have you considered using an emulator? Even Linux still supports ARM6.

You can't test properly against emulators like QEMU. Emulators are written to be fast, not accurate (although in absolute terms they are pretty slow), e.g. they execute correct code quickly, but they don't fail to run incorrect code the same way real hardware would do. For example, when I wrote the arm64 Go port, I quickly discovered that the emulator would not cause a trap on unaligned memory access! Incorrect code executed just fine on the emulator. Even worse, I found several other bugs in a span of few days. In the end, I ended up writing my own arm64 emulator before hardware became available. I finished the port on real hardware.

Of course, in principle an emulator could do perfect emulation, but in practice testing Go on an emulator would mean testing and maintaining an emulator as well.

Apart from all that, running the Go test suite in a reasonable time requires a reasonable fast (e.g. server class) computer. Emulators just don't make the cut. And neither do small/old ARM systems.

We could reduce the scope of the tests for embedded platforms, but then somebody would have to step in and work on and maintain those platforms. People always complain when the minimum hardware requirements for ARM are increased, but never offer to step in and help... Personally, I am interested in Go on embedded systems, but I don't have the time to maintain this as well.

Re: The State of Go

#370
post #250

Why are we getting rid of OSX 10.8 and some of ARM6? Are they _really_ that much extra work to support? I have a hard time believing so.

Well Apple stopped supporting 10.8, so why should Go continue to support it? ARM6 support is a lot of work, actually, as IIUC a lot of stuff that the chip doesn't support needs to be done in software, and we don't have a reliable platform for testing against ARM6.

> Well Apple stopped supporting 10.8, so why should Go continue to support it?

I don't know, but Microsoft stopped supporting Windows XP many years ago, and it's still supported by Go. (Not that I care about Go on Darwin 10.8; just making an observation...)

Post reply on HN