Hacker News new | ask | show | jobs
by tshadwell 3745 days ago
> How to make it reusable? `<-chan interface{}`? Welcome to the land of types casting and runtime panics. If you want to implement high level fan-in (merge) you’re losing type safety. The same (unfortunately) goes for all other patterns.

I can understand arguments for generics, but they're complaining about trying to warm a whole pizza with a toaster. You need to put individual slices in, it doesn't work like an oven. Cook it with an oven if you want, but don't try to make it out like the toaster is offensive and useless.

Yes, you might need to re-implement these patterns each time with separate types (slice up the pizza), but it's not really a lot of work for the extra speed and crunchy pizza it gives you.

I'm pretty tired of the frequency of these articles where someone takes a concept from a language, tries to force it into a different language with little care for its idioms and then complains that it doesn't work.

Maybe I like digging holes with a trowel, maybe I like the precision it gives me, maybe I've worked out a way to with a little extra effort accomplish the same work. If you work with a spade and a bucket all the time don't complain that you can't throw a trowel around like a spade.

4 comments

Language idioms are not set in stone (mostly). You have functions, variables, containers etc there. It's not like they invented a whole new way of computing. Not unreasonable for people to ask for some decent ideas that solve problems in other similar languages. Like generics.

Maybe if enough people complains about their toaster's pizza heating capabilities, manufacturers would release pizza heating toasters. Or at least explain why, technically, it's not a good idea. Not yell people "you would burn down your houses! idiots! we are looking after you."

At least it feels like that to me, the condescending tone of designers / advocates is irritating in Go land.

All understood, and you're right, but the tone of this article and that of its kin is at least equally bad at idea discussion -- it's not weighing out positives and negatives or trying to get an idea of why things are how they are, it's just angry that it can't do the thing it wants to do, and it wants you, and everyone else to know.

I'm saddened that you feel like the Go community doesn't answer these questions, and I'd love to write an article on why these things are how they are and how they can be useful, but that's beyond the scope of a HN comment.

Yes, I disagree with the author of the article, but the primary thing I intend to satirise is his angry tone. If you've felt that the Go community has been condescending, I hope the condescending articles get equally as many critics.

The Go community seems to throw "you're using the language wrong, do it like this..." or "copying code is the way to go" or "those things you use in other languages shouldn't be used in ours." or "I have never needed to use that in my code"

There has never been an argument on how concepts like .map, .reduce, .filter, functors, promises, futures are inferior to what Go programmers currently use. Things like simplicity are thrown around but calling these proven abstractions "complex" is wrong. That's why they're useful fundamental abstractions -- because they make code simpler and reusable.

Generics have been around for a very long time. They have been studied in-depth in academia and industry. They're universally accepted but in Go.

Generics need not be like C++ templates. There are simpler implementations of generics. They are a solution to a real problem, specifically in libraries and creating type-safe abstractions. If you don't want type-safety, then why are you using a language with static types?

- Code copying is a workaround, not a solution. - Code generators outside the compiler is a workaround, not a solution. You're essentially transpiling a language that is no longer Go into Go because of Go's lack of support for such things. - interface{} is a workaround, not a solution. It automatically shows that Go cannot express a generic variant safely. - reflections is a workaround, not a solution. See previous point.

If the Go community is happy with these workarounds than that's cool, but be honest that's it's a workaround, a stop-gap to the lacking of Go's type system.

I think they are not workarounds so much as compromises made with a deference to the language as a whole, which fall at a different optimization point than what a lot of programmers are used to. Succinctly, features in Go are chosen so that they compose well with (i.e. are orthogonal to) all other features. The bar for new features is thus very high. They must be backwards compatible, and they must interact cleanly with the language as it exists. To focus on any individual missing feature in Go as evidence of anything is to miss the point. Quite literally to miss the forest for the trees.

That said, I think the authors and the community learned a lot since 2009. I think a Go 2, correcting the mistakes of the initial implementation, and adding important missing features like parameterized types and native functional transforms, would be very compelling. But to do it without sacrificing any of the other good things about Go, like the compilation speed, or the easily parseable source, or the concurrency story, or interfaces... well, I'd love to see the (concrete, specific) proposal, because I don't know if it's feasible.

I think the mistakes Go authors recognize and Go critics talk about have very little overlap. So I won't be surprised if none of those complains get addressed. Especially since those problems can simply disappear by moving to numerous better languages available to all.
There has never been an argument on how concepts like .map, .reduce, .filter, functors, promises, futures are inferior to what Go programmers currently use.

With regard to futures and promises, I find the article What color is your function [http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...] very convincing with regards to their problems. The article touches on part of why Go's concurrency model is superior, but for an even more compelling argument, you should watch Rich Hickey's talk[http://www.infoq.com/presentations/clojure-core-async] about core.async which is Clojure's library implementation of CSP[https://en.m.wikipedia.org/wiki/Communicating_sequential_pro...], which is the concurrency model which Go has language level support for. The argument briefly is that CSP allows for concurrent code that reads sequentially and is therefore easier to reason about.

I don't think there's the same feeling of "community" with Go as with other languages, in the same way that there's not really a C community - more a bunch of authors, evangelists and contributors who have a "stake" in the language. That's sort of the point, though. Go doesn't really need much more than it has already. It's a deliberately simple language, with standard format and documentation, a plethora of examples and a comprehensive stdlib. It was designed to serve particular purposes, and it does what it does really well. All it really needs in the way of community is a bugtracker and a StackOverflow section, and I suspect that the a lot of the "Go community are arrogant/negative/dismissive" type complaints can just be attributed to it being the same few people hearing the same complaints about lack of My Favourite Feature X over and over, despite the many blog posts, release notes and list discussions explaining why it's not there.
> but be honest

You've elaborated on the benefits. What do you think the costs of generics are?

As long as we're talking about purely generics with no introduction to type classes, the obvious ones people point out:

- More compiler complexity - Compilation speeds might be effected - Without type inference, type signatures could be a pain point - Basic type parameters are not able to express all intents. Until you implement more complex like higher-kinded types.

Go's compiler already works with generics, although it might be built in an ad-hoc way. Either way, the complexity is seemingly already in the compiler.

Before saying "Compilation speeds might be effected", we should probably get some numbers to back that up. So I don't buy this cost right now.

The third reason is very valid as Go doesn't have type inference. You probably won't get Java's verbosity with signatures but you'll be specifying type information in a few places, at least.

> Either way, the complexity is seemingly already in the compiler.

I do not agree with dismissing this complexity on the hunch that the complexity is already in the compiler. Implementing generics for a few select types/functions is a completely different story than real useful generics in the hands of a user.

Consider, for example, how much larger the language specification would be. I think you've neglected this cost.

I also find your dismissal of type classes to be curious. I wonder how useful, for example, parametric polymorphism would be without some constraint based polymorphism, even neglecting higher-kinded polymorphism. I suppose ML might fall into this category at first glance, but its module system makes up for its limited parametric polymorphism in many ways.

> Before saying "Compilation speeds might be effected", we should probably get some numbers to back that up. So I don't buy this cost right now.

I think there's a mountain of real evidence that generics impacts compilation speeds, assuming monomorphization as an implementation strategy. Exactly how much isn't clear. There is a comparatively smaller amount of evidence that suggests compilation can still compete with Go's compilation speed. (I'm thinking of D's compiler, although I've never seen any rigorous benchmarking.)

My suggestion to you is: instead of being so quick to imply those that disagree with you are dishonest, perhaps you might consider that there are indeed trade offs involved with more sophisticated forms of polymorphism that Go doesn't support, and therefore, the costs may not necessarily be worth the benefits. Not only is this a valid and honest position, but it's a reasonable position. Abstractions aren't always free, and too many of them can become quite costly.

I think you are right. And I feel like if I'd read something like this about any other platform I'd feel the same as you.

However, when I think about it, If I was moved enough to write something about the pain points of Go I would probably do it like the author did here. Not because I think the tone is correct in general but because I've seen that constructive criticism got thrown out by the Go community (I mean the people in decision making, not the entirety of users) over and over. I'd just be following their lead on the tone.

Also, this article does not have the same tone as the infamous PHP article (https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/) as in mocking it. It feels more like someone passionate about Go getting angry because they can't use it in it's full potential. It's good to have people like that.

Not the best way to do it though. You are right. But as I said, this has been the way Go people have been treating criticism.

It reminds me very much of the history of the Java language. The interesting thing about Go is increasingly the runtime/VM and decreasingly the language itself. The same became true with Java vs the JVM. Remember that people like Gosling and Van Hoff used to proudly proclaim how hard they worked to keep out features from Java? Remember how eventually languages like Ruby, Clojure and Scala kept getting more interesting while Java festered (and eventually began stealing from these other languages)?

It sounds a lot like the Go culture. Go 1.6 has no new features. Ho hum. Eventually Gisp will get STM, refs, agents and atoms. Just like no sane person would start a new SaSS using Java when they could choose other languages to run on the JVM that are much better, for example; eventually nobody will write new code in Go when they can use Gisp or some other language that has the missing features. It will happen.

For me (today) I just want to be able to add an element to the middle of a slice in Go without having to read the documentation.

Go runtime is not independently available. This is first time I heard that people are more interested in Go runtime than Go language. It is likely many people care about language being interesting but, where I work, most debate / arguments are about product features not language features. We have been using Java quite productively for long time and do not see any need to change.

> Just like no sane person would start a new SaSS using Java when they could choose other languages to run on the JVM that are much better..

I think Scala is 12 yr old and fair to say it has not taken software industry by storm e.g like Swift. It has maybe few per cent point market share of all JVM languages. And the superiority of their language that Scala developers claim all the time may ensure that Scala will not get any more popular than it currently is. In my interaction with Scala developers are they are more interested in talking how cool is Scala rather than what cool software they have developed with it.

Swift isn't a fair comparison at all because it's a mandated language by Apple. For me Scala is just an improvement on Java. It's valuable to people who fetishize complexity, IOW, people who use Java and like it.
Scala is a highly versatile hybrid language that can be used in a way that doesn't promote needless complexity. Just like you need not use the most advanced OOP features, you need not use advanced FP features, nor do you have to use macros. If you don't want to.

This choice is what makes it an interesting and pleasant language to work with. I've often dealt with Scala code that is much simpler to understand than comparable Java code, but still more often than not, Scala code is made needlessly complicated.

That choice implies a certain kind of respect for the programmer: here's a very, very powerful tool, and use it wisely.

Go is the antithesis of this, and I get its point, it's the opposite philosophical direction. I think it's a direction that will not move the industry forward, it's a direction that will undoubtedly help us produce value---software, reliably---instead of doing something amazing.

Scala has taken the industry by storm, in the sense of the influence it has had on Swift, Rust, TypeScript, Kotlin, etc.
> Just like no sane person would start a new SaSS using Java

Woah you're way off here! Java is still used very heavily in new SaSS. Very very heavily. Easily in the top 5 languages used by new SaSS companies for backend servers.

'sane' is operative word here:-)
And yet Swift can implement generics with just as much performance as Go. Swift has had limited forms of generics in the first few releases and in the 2.2 developer releases there are full generics; yet they compile to the same performant native executable code that Go does.

In fact, under the hood, Swift uses a similar error handling model to Go, in that multiple values are returned (normal/error). The difference is that in Swift, the syntax and semantics of the language handle that for you instead of requiring the programmer to type in "if err != nil { return nil }" repeatedly.

It's one thing to argue for performance and conciseness in a language, but being dogmatic to the point of refusal to adopt anything that was invented in anything more recent than the 1980s is really going to hinder Go in the long term.

Its not about performance, its about keeping the language simple. As someone who writes in Go, I really find this an attractive feature. I learned it in a few days, which is pretty fantastic IMO. Although I'm not sold on the Go shouldn't have generics argument, it does seem like Go has more pressing issues than implementing generics tho. Like bring the compile time back down to near instant instead of 300ms. lol

Also Swift's compiled code does not get near the same performance as Go's compiled code. Even when you unsafely compile Swift's code, it doesn't get near. When you start embedding C function calls into Swift and turning off Swift's safety features then it has been shown to get near Go.

I agree that systems and libraries you don't deal with every day should strive for simplicity and learnability, but I think simplicity is a much lesser virtue in programming languages.

A programming language is something you use every day, so you'll inevitably invest time in learning it properly. A language you can learn completely in a few days is unlikely to give you as much power, convenience and maintainability as a more complex language. Choosing Go might get you and your team started faster, but wastes a lot of potential over time.

(Of course complexity on its own is not a good thing, some complex features may be prone to abuse, orthogonality is still important etc., but Go is way too conservative to be competitive long-term I think.)

To be simple is wrong, what is needed is to be minimal and to the point. Most of the time, the minimal tool is simple enough, but sometimes complexity is a necessary evil.

Driving a car is more complicated than running. Why do we invent cars in the first place?

> Also Swift's compiled code does not get near the same performance as Go's compiled code. Even when you unsafely compile Swift's code, it doesn't get near.

Do you have a source for this?

Swift had the long term in mind anyway. The effort to create SIL, along with the industrial-strength LLVM pipeline, has led to a rock-solid foundation. It's hard to compete with a backend that has 4 separate IRs, each with its own suite of optimization passes, including hundreds of algebraic simplifications, a highly tuned register allocator, an instruction scheduling pipeline, and an autovectorizer, just to scratch the surface. The stage is set for an excellent compiler architecture, even if they're not there today.

Heres from a quick Google

http://benchmarksgame.alioth.debian.org/u64q/compare.php?lan...

You can see like most Swift benchmarks, its compiling with the -Ounchecked flag and using C.

-edit- I have no doubts of LLVM's capabilities. It just seems like Swift is probably already taking advantage of its optimizations.

One surprising performance issue with Swift was identified (and fixed) by IBM recently:

https://github.com/apple/swift/commit/259d57bbe7784955108caa...

The problem was that the print operations was being done character-by-character and it resulted in the printing being a significant overhead in the execution of the tests themselves. With this change they saw a speed up of many times simply because it was using buffered output.

> It just seems like Swift is probably already taking advantage of its optimizations.

The Go compiler performs nowhere near the level of optimization that LLVM does; LLVM is years and years ahead. However, LLVM is fundamentally a C compiler and so there can be impedance mismatch between Swift and LLVM. Hence SIL. This is what you're seeing, and once the gap is closed then Swift will reap the benefits of the hundreds of optimizations in LLVM.

You did say "Even when you unsafely compile Swift's code, it doesn't get near." ;-)

fannkuch-redux looks near, mandelbrot looks near, n-body…

Swift is new to Linux; there's a new compiler snapshot about every 10 days. Swift has only been available on the benchmarks game 3 months for people to contribute programs. Early days :-)

Generics do not have to be complex and they allow abstractions that simplify your code.

Please explain how .map, .filter, .reduce, .pmap are complex?

Swift is also built with LLVM, a backend that is far superior to Go's handmade one. I doubt the performance issues will persist long-term.

There is a large cohort of programmers that don't understand map/filter/reduce. They love Go (and C) for not allowing it and forcing everyone to think purely imperatively.

Go makes it an enormous effort to make something complex. You can do it, but you'll be typing a LOT. I think this is a disadvantage from both a writing and reading code perspective, but the Go mailinglists keep on repeating how it's a huge advantage. I may see a small advantage if you're trying to prevent simple programs from turning complex, but if you're trying to do complex things Go simply makes you suffer.

Writing out map/filter/reduce code invariably leads to 10-15 lines and 2-3 functions per map call, and more for reduce. You'll constantly be reading a lot of text and reducing it back to the map/filter logic in your head. Meanwhile there are a lot of things you need to remember.

I don't like it either. Things that are a 2-3 line map/list comprehensions in python and haskell are 2-3 files with 100 lines each in Go. And God help whoever needs to have map/filter working on things that come in over channels, then the complexity truly goes through the roof.

(for C: yes macros can "fix" this. If you don't mind horrible code)

Drinking the golaid will turn you into this, extreme conservative programming. Polymorphism is too progressive. golang is a cult.
Go is heavily based on polymorphism. It just doesn't make the mistake of thinking that inheritance is the proper way to achieve it.
> Go is heavily based on polymorphism

Go has very limited polymorphic capabilities (implicit interfaces that have huge limitations). And struct embedding isn't composition. Now if you're talking about interface { } everywhere, sure, Go is as polymorphic as Javascript.

Go is also based on limitations. The job is to write programs that work, not to create little framework empires.

I don't know why you claim that struct embedding isn't composition.

What does that have to do with generics?
Nothing. But at the same time, the comment I replied to made no mention of generics. Just polymorphism.