Hacker News new | ask | show | jobs
by hhsnopek 3382 days ago
> First, functional programming is particularly difficult in Go. In fact the language discourages functional programming.

Then it seems that Go isn't the language for you. With that Go nor any language, unless intended, is suppose to be a one-all solution for every problem. We can accomplish a majority, if not all, problems in a language, but this shouldn't assumed that a language is intended to do so.

4 comments

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.

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.

His point is that golang makes it unnecessarily hard to accomplish what really ought to be simple tasks inherited from functional programming ideas. Other languages, like Rust and Ruby, have adopted some of the really simple and powerful functional idioms like Map, Sum, Select, Reduce, etc., whereas golang makes it impossible to abstract away any sort of iteration logic. He even shows how this lack of abstraction power has knock-on effects in the form of dramatically-increased difficulty of parallelism and concurrency.
I don't think the tradeoffs made by Go have been explained in a way that makes sense to people who find value in the techniques and idioms described in the article. I agree, if Go lacked these features because they were consciously traded away for something else of value, then it would only be half an argument to point out that they have value.

It would only make sense to make that point if they were intentionally left out of Go because they were regarded as having no significant value or even negative value. And that, I think, is the widespread perception of why they aren't supported in Go.

We can accomplish a majority, if not all, problems in a language, but this shouldn't assumed that a language is intended to do so.

The programming methods described in the article can be applied to almost any problem that Go is a good fit for. If that's in question then I think it proves my original point — this argument is not about arguing the tradeoffs; it's about whether anything of value was left out.

In Dlang you can do functional programming, although it's not as pretty as a pure functional language.

http://dlang.org/phobos/std_functional.html