Hacker News new | ask | show | jobs
by burke 3382 days ago
While I understand the argument for simplicity and agree with the premise in most cases, I find it pretty hard to argue for this code:

    ys := []byte{}
    for _, x in xs {
      ys = append(ys, f(x))
    }
    
over something like this code, which is more obvious, less error-prone, and, though it relies on an additional concept, it's an extremely broadly-known concept:

    ys := collections.map(xs, f)
I don't think a proper collections library would require very invasive runtime changes -- though, once you start down this path, you and up at "but what if `f` returns `(myType, error)`, and then you're wishing for Option/Maybe types (which is another feature that I believe Go should have implemented, but is more obviously outside of the apparent mandate).

EDIT: It would make static analysis significantly more complex too.

2 comments

Go can't do this, because you can't define `map` as returning "a collection of whatever `f` returns".

If they build a collections API like this now, without generics, and they then add generic types later, they will be in the same position Java found itself in - lots of painful API migrations.

While the generics debate is legendary in go, it does still seem very much like the intention is to eventually include it. Given that, I think it makes perfect sense to wait to introduce more special cases (like `append`).

In the meantime:

    func Map(in []interface{}, f func(interface{}) interface{}) []interface{} {
    	out := make([]interface{}, len(in))
    	for i, v := range in {
    		out[i] = f(v)
    	}
    	return out
    }
:)
But there is literally the `map`, `slice`, `chan`, and `append` which are generics.

The fact that append exists and the language would be extremely convoluted and inefficient to use is a testimony to how useful generic programming is.

> While the generics debate is legendary in go, it does still seem very much like the intention is to eventually include it.

Yes, when this topic comes up I think it helps to remember that the Go devs are in no way opposed to generics. They have no philosophical "thing" against it, and it's not about "our ideology and where we want to go with Go".

https://golang.org/doc/faq#generics

It's more like... It's complicating the type system and a hard problem to solve. If they do it, they want to do it right, because a main goal is to not get complicated.

It looks like you're making the same point as burke and the original article: Go lacks common-sense features that would make writing safe, simple code easy.
I guess we're agreeing on the fact that Go lacks generics. I don't know that I agree that those makes "writing safe and simple code easy". As we both know, simple and easy are two very different things.

Generics do make a lot of problems easy, but I don't think they inherently make problem solving simple. I absolutely miss them in Go - but I also remember how confused my high school programming teacher and I both were about them.

So far, Go seems to err on the side of Simple, if a choice has to be made. And, so far, the proposals for generics in Go seem to force that decision. I'm glad to see how seriously the Go authors take weighing the design and decision to include them.

Well I guess I misread your comment, then. I had assumed your code sample for a type-unsafe implementation of Map, followed by the smiley, was a recognition of the major lacking on Go, not just of generics but of the ability to operate on collections. I read burke's comment as suggesting a privileged collections library that doesn't go all the way to user-space generics, but perhaps I misread that, too.

I would strongly disagree with the definition of "simple" that you seem to be using. I don't think it's important for a language designed for software professionals to have only features that are easy for high schoolers to understand. It's far more important that experienced developers be able to write a common idiom like map with as little cognitive overhead as possible, that's the meaning of simple that I find valuable in this context.

I happen to agree with most of the points the author makes--that those features would make Go nicer. I think I disagree with the author (and probably you) about the degree to which these features matter. I write a lot of Go code, and the times when I really need those features or feel that those features noticeably reduce my risk of error are few and far between. Go code is not as DRY as Rust or some other languages, but I'm not sure that the extra DRY-ness would make me significantly more productive or correct. If someone wanted to fork Go and implement these features, I'd love to be proved wrong.
No offence but it only seems "more obvious and less error-prone" to you because you likely have experience with functional programming and dealing with maps (typical of functional programming).

There are plenty of programmers for whom this is cryptic, and they prefer an old-school "simple and straighforward" for loop (typical of imperative programming).

This is absurd — this mentality can be used to argue against literally every form of abstraction ever. The entire reason we have abstractions is to avoid writing the same redundant code over and over, with the associated likelihood of bugs due to typos.

What makes map more cryptic than literally any of the abstractions already in that example? Make is abstracting away GC-based memory allocation, indexing is abstracting away pointer arithmetic, and so on. Why are those abstractions fine, but "apply this function to everything in a collection" is somehow impenetrable voodoo?

"Simple and straightforward" often just boils down to "reams and reams of boilerplate code", where catching bugs is hard because it's impossible to notice a slight errors in one out of a hundred reimplementations of the same function.

> There are plenty of programmers for whom this is cryptic

I've never really done functional programming, and map was cryptic the first time I saw it, but it's literally a concept that can be understood by any competent programmer in under 30 seconds - apply a function to every element of a collection and return a collection containing the results.

I don't have any sympathy for programmers who find simple abstractions like this cryptic but then don't try to understand them.