Another go at Go failed
oneofmanyworlds.blogspot.com
oneofmanyworlds.blogspot.com
- Why pick Go for this project in the first place? Those days the biggest productivity difference that arises from language choice comes from the availability of libraries, and it does not take much research to see that Go wasn't particularly targeted at numeric computing and that library support for those kinds of things is very poor. There is a number of mature platforms for this class of problems, SciPy, MatLab/Octave, C/C++ with various BLAS-derived libraries etc.
- The bit on poor performance is unconvincing because the source of the difference has not been identified. The speculations about Go and Java that follow are so poorly backed by any evidence and so wild they should have been cut out from the article. It's clear that the author has no clue what really happens in either case, and he proceeds to draw conclusions anyway. This kind of "magical" thinking about black boxes one doesn't understand is unfortunately all too common across software engineering.
- One place where I agree with the author is that Go's zoo of builtin data structures is really, really poor compared to Java. I mean compare:
http://golang.org/pkg/container/
http://docs.oracle.com/javase/tutorial/collections/interface...
http://docs.oracle.com/javase/tutorial/collections/implement...
I really like the language, enough to use it and work around the lack of generics, but that's a glaring weakness. They need some way to enable container classes.
[1] http://concurrencyfreaks.blogspot.com/2013/11/stampedlocktry...
[2] http://mechanical-sympathy.blogspot.ca/2013/08/lock-based-vs...
But the major problem is that many of the Java bashers tend to criticise without knowing what is possible to do in the Java ecosystem.
To answer your question, yes: my own library, Quasar[1], provides lightweight threads for Java.
The first set of JVMs did implement green threads, which are what goroutines are. Shortly thereafter most of them switched to red threads, aka real threads.
You can still find a few JVMs that use green threads, like Squawk.
https://java.net/projects/squawk/pages/SquawkDesign
Other than that, java.util.concurrent provides mechanisms to map multiple tasks to single OS threads.
Operating systems have been able to handle an order of magnitude more pthreads than that since the day pthreads were introduced.
Channels are a way of queueing work, which is great for many use cases.
You can use mutexes and shared memory if your situation calls for it. For example, if I am doing a graph traversal concurrently, I can use channels to send the work to many listening threads as I traverse that graph synchronously.
If for some reason, I need to update that graph's representation asynchronously, I may need to use mutexes to lock the graph since the graph is now shared memory.
> Go's zoo of builtin data structures is really, really
> poor compared to Java.
This is a poor comparison because idiomatic Go typically doesn't use the `container` package.On another note, I built an algorithm using a bitset. At first, I used one of the libraries the author linked, but then I saw that it was easier to just do the bit-bashing myself for my particular use case. It's not that difficult if you are familiar with C and C++ programming.
If, however, you are coming over from a language like Python, Ruby, and even Java to criticize Go is an easy thing to do. Those are very high level languages that protect you from needing to understand types. The object oriented parts of Java languages give you the ability to use polymorphism, which is not as straight forward in Go. You don't have a common base class. You only have interfaces. Trying to recreate generic structures and then blame the language for not having them is foolish. If you can't program without generics, stick with Java from the beginning. Or better yet, don't do a project in Go until you understand how to approach problems the Go way.
I happen to like Go for the very reason that you don't deal with generics. Seeing the types make it easier for me to follow as opposed to following a chain of inheritance. Getting rid of all the factory code trims my code down. This makes things easier to read when you are not the author.
Comparing Java's compiler to any other compiler is going to have predictable results. Java may have the best compiler out there.
I think that person was just frustrated.
Inheritance is overrated, and the factory pattern is often overused. Both of those happen with generics.
Generics have nothing to do with inheritance and the factory pattern. There are lots of languages that have generics but neither inheritance neither factories: SML, OCaml, Haskell, etc.
Inheritance makes generics harder, in fact, because type inference becomes undecidable in the general case.
Factories are basically a workaround for functions not being able to be freestanding (unattached to a class) in Java (the "kingdom of nouns"), a problem that Go doesn't have.
Where do I say such a thing? I'm saying that generics are both overrated and overused.
Factories are basically a workaround for functions not being able to be freestanding (unattached to a class) in Java (the "kingdom of nouns"), a problem that Go doesn't have.
Being the "Kingdom of Nouns" is kinda the point of Smalltalk/Objective-C style OO. (Though Alan Kay once famously said that when he thought of OO, he was not thinking of Java.) That said, while it is nice to have someplace to put functions, there are times when it's also nice not to be forced to attach them to a class. Not so sure it's a problem as much as it's just a particular approach with its advantages and drawbacks.
Where do you get that Factories are "a workaround for functions not being able to be freestanding?"
> Where do I say such a thing? I'm saying that generics are both overrated and overused.
It looks like that's what you're suggesting here:
> Inheritance is overrated, and the factory pattern is often overused. Both of those happen with generics.
---
> Where do you get that Factories are "a workaround for functions not being able to be freestanding?"
A factory is, conceptually, a function of some set of inputs that returns a new instance - which is, of course, exactly what a constructor is. Now, it's not always appropriate for a class to know every single way in which it might be assembled, so such responsibility is usually lifted out - the question is where to put it. We already have constructors, and those are functions, so it would seem reasonable that we could just write method similar to a constructor, place it in some other namespace (perhaps in a completely different package/module), and then pass that along to the component that needs to create these new instances.
For languages like Java, the problem is that functions are not first class - you can't directly pass along a method/function like (Conf -> SomeService), so instead, it's common to create some special type, like SomeServiceFactory. If, for comparison, you take a look at Scala/Haskell/C#/etc, you'll wont often spot /.*Factory/ anywhere, because a function (with the ability to close over free variables) is much more concise, and doesn't require defining new types (e.g. SomeServiceFactory, versus Function<Conf,SomeService>).
I think that, seen in this light, the practice of defining dedicated factory classes is a rather clumsy approach necessitated by the design of the underlying language.
> Inheritance is overrated, and the factory pattern is often overused. Both of those happen with generics.
Non-sequitur here. To me, it's clear that I'm saying that generics are overrated and overused. Your interpretation makes no sense!
For languages like Java, the problem is that functions are not first class - you can't directly pass along a method/function like (Conf -> SomeService), so instead, it's common to create some special type, like SomeServiceFactory.
Another non-sequitur here. Factory pattern was originally popularized in the GoF book as a way of providing an opportunity for polymorphism.
If, for comparison, you take a look at Scala/Haskell/C#/etc, you'll wont often spot /.Factory/ anywhere, because a function (with the ability to close over free variables) is much more concise*
Old hand at Smalltalk here -- over 10 years. You don't often see Factory classes in Smalltalk. We also have very easy and nimble closures over variables in the context they're defined. I still don't get what you're on about.
You are making the non-sequitur. His interpretation is the only one that makes sense. It is not clear at all that you are saying what you claim you are saying. You are in fact not saying anything like it. You are saying two things you don't like happen with generics, but that is completely false.
So you are trolling, and you still haven't pointed out such a rule.
> Non-sequitur here. To me, it's clear that I'm saying that generics are overrated and overused. Your interpretation makes no sense!
Ok, now I understand what you're saying. As an aside, I would suggest that most people (as is evident in this comment thread) would not interpret your statement the way you intended.
>> For languages like Java, the problem is that functions are not first class - you can't directly pass along a method/function like (Conf -> SomeService), so instead, it's common to create some special type, like SomeServiceFactory.
> Another non-sequitur here. Factory pattern was originally popularized in the GoF book as a way of providing an opportunity for polymorphism.
I agree (re: polymorphism), but I don't think that contradicts what I'm saying; functions with parametric polymorphism (with support for {co,contra,in}variance) provide the same benefits of class polymorphism in this context.
> Old hand at Smalltalk here -- over 10 years. You don't often see Factory classes in Smalltalk. We also have very easy and nimble closures over variables in the context they're defined. I still don't get what you're on about.
You're absolutely correct. However, my point was that in statically typed languages, generics are conducive to favoring closures over factories classes and inheritance. But, now I realize that we were arguing two different things.
How/why are they overused? How are they overrated? How can you claim there's any benefit to static typing but say generics are overrated?
No, both of those have absolutely nothing to do with "generics".
I like the fact that you are forced to look at the algorithm vs the class hierarchy. I like that you don't have to read through factories.
I do think that generics would be great if they found a way of implementing them, however, I do not think that you need generics in order to write this kind of project.
I've demonstrated this myself by comparing the goroutines example prime number generator to a similar version written using Python generators.
The python generator code was faster, but there was a hard limit to how many primes could be calculated this way; I hit the stack limit.
[Edit] Code & times: http://pastebin.com/CqkMcxdK
I like Java as well. It can be difficult to read the implementation when you are surrounded with classes. That was the point I was trying to make.
If you are thinking in generic containers and are programming in Go, you either chose the wrong language or need to change how you are thinking.
Lots of languages are not object oriented and still have parametic polymorphism. Like SML. In 1973.
>If you are thinking in generic containers and are programming in Go, you either chose the wrong language or need to change how you are thinking.
If you are repeating the same baseless assertion routed in ignorance, then you either chose the wrong forum, or need to change how you are thinking.
Yes, with the exception of that being a story about some random idiot with an axe to grind, and this being a totally competent guy, that goes into detail about what he did and how, what trouble spots he found etc.
I guess, it being a hired project, he can't share the actual code. But he provided tons of pain points, well articulated.
https://github.com/cosn/collections/
That being said, I completely agree that the author chose the wrong language for the problem at hand.
I hate this type of non-productive comment.
"Why he picked Go" is beside the point. That was his personal choice, and it's of no concern to us if it was a good or bad choice when he made it.
What IS of interest to us, is his findings, ie. about how (non) fitting was Go for the kind of work he describes, and for what reasons. Because that can helps us access the language ourselves for such uses.
>Those days the biggest productivity difference that arises from language choice comes from the availability of libraries
Again, not true for his needs. For what he needed, core library built-ins have sufficed in Java (Set etc). As for Go, he did find 2 libraries implementing them, but he still wished for a good core implementation.
>The bit on poor performance is unconvincing because the source of the difference has not been identified.
No, but it's well known that Go lags behind the JVM, something even the Go team aknowledges and attributes to maturity of the compiler/GC.
Plus, from the writeup, he sounds like a thoroughly decent programer to not be able to code his way out of Go performance bottlenecks. Doesn't sound like the guy who can't use a profiler (and in fact, he points in his article that he DID use one).
>This kind of "magical" thinking about black boxes one doesn't understand is unfortunately all too common across software engineering.
Random rant unrelated to TFA.
As for achieving `generic' code through static generation using an external tool, I had had considerable success with it in some earlier projects. Except that the number of types is much higher in this project, and things could not be as simply - or clearly - generated statically.
I cannot publish the code because of the client's IP involved -- not because I fear criticism; see, I was ready to face _this_ criticism :-). Those of you who read my original post in golang-nuts may have already read that, of course!
BitSets: look at math/big - not that intuitive, but what you need is probably there.
Sets: I wrote https://gist.github.com/arnehormann/6234573 while thinking about an api. It's not a regular repository because I never needed a set type myself, but it should work.
Like another commenter said, the cpu usage statistics in the Java program are most likely due to Java threads you didn't start yourself, e.g. the garbage collector. If your code involved creation and disposal of a lot of Objects, Go is at a serious disadvantage compared to the JVM. But that can be mitigated with a little effort and will probably disappear in due time.
Exactly. I am reminded of Rogue Wave's[1] generics before STL became part of C++. I remember achieving that effect using a paradigm of macros and code generation provided by Rogue Wave libraries. It was during mid 1990s.
[1] roguewave.com
Edit: Added reference to Rogue Wave
After reading it, I thought it was really insightful and useful, and concluded you're clearly a good SW engineer - was surprised by the excess criticism here. Anyway, thank you for writing and publishing this.
BTW - how would you compare Julia to languages like Python, Ruby or Go? If you ignore the libs and maturity, is there anything about Julia that makes it relatively less suitable for, let's say, writing a web app, a web server or a desktop application?
Never select a language based off hearing about it on HN or blogs. Either stick with what you know or use a tool that has very specific optimizations for the problem you're going to tackle (and no, concurrency is not a good option to jump off the JVM as it has incredible concurrency support already: see https://github.com/LMAX-Exchange/disruptor for the fastest concurrent bus I've seen in any language). Of course, there is nothing wrong with testing out golang or haskell - just don't force your paying client to test it out with you or you might be one less client shortly.
Quite powerful GUI desktop systems in the early 80s, using language capabilities that are yet to be fully usable in the mainstream.
Lisp on one system, and a GC enabled systems programming language on the other one.
Every time I discovered such systems being ignored most developers, I remember of Bret Victor's talks.
(Incidentally, my mobile phone calls are quite rare. Voice communication sucks. If your intent was to show that Erlang is important to me, you chose a poor example.)
Author of the blog post here. I agree, in retrospect!
The genious behind LMAX is the way they bend Java's object layout features; they achieve nice performance in spite of using Java, not because of it. Some decade old message passing libraries (OpenMP et al.) will probably outperform LMAX without even trying.
Because there are "brand new languages" out there that are more mature than Go, and better than it for his needs. Scala and Clojure come to mind, but even something like Nimrod would be perfectly capable.
You mention it as if being "new" is some kind of excuse.
Now I think Go actually has some very good use cases. While I would never dream of developing a large, complex or critical app on any platform other than the JVM, the fact that Go produces self-contained executables makes it very convenient sometimes. Fast command-line programs are actually not too narrow a use case, and Go is not useful just for those. I can see myself using it for other small web or network services as well. I still think Go is a JVM-less Java, or a Java Lite, but apparently Java Lite is a very useful thing to have.
Even that can be solved when using one of the many native compilers available for Java.
As the OpenJDK does not have one, people tend to forget other vendors have them.
It may reduce compile times, but ultimately the reason startup sucks is because the JVM is huge and has to load everything and the kitchen sink in order to function for even the smallest programs.
1 - There is no JVM
2 - All the code that is not used is not part of the binary, if you do a static build
2.1 - There are native compilers for Java where you can give exclusion lists to allow certain code to be kept even in static builds to allow reflection use
I've experimented with gcj in the past. It still suffered from excessive overhead, still had it's own runtime requirements (although obviously not the same as the JVM) and had a lot of serious compatibility issues. Not something I would have based a serious production deployment off of personally. Maybe the state of the art has changed since then.
It was never at the same level as commercial Java native compilers.
Besides Excelsior, there are Aonix PERC, IBM J9, Codename One, JamaicaVM, RoboVM.
There are probably a few others.
Mark Reinhold mentioned on his "post Java 8" talk at Java One, that having an AOT compiler as part of the standard JDK is part of the wishlist.
Or Python+.
Interesting all the comparisons to Java, with me coming from an environment where Java is never used, never considered, largely never thought about. If Java is mentioned at all, it's usually something like "my kid took a programming class in college, and they used Java, why would they use something like that?". I suppose Cobol was like that: widely used, very widely used, but not used at all in many circles.
I was making the very opposite point. I think Java programmers tend to underestimate the circles where it is not used. In particular, it seems that those who would not describe themselves as professional programmers but who nevertheless need to program tend to not use Java. And it's not all Python/Perl/Basic/Tcl/Bash; C++ (and C) are also widely used.
(By "not using", I mean for instance that in Python, like Javascript, at any moment, you may set a new method on a particular instance, and the Python runtime must deal with that, such as by spending a lot of time looking up references that never actually change in a real program. This is a really expensive feature that you are paying for all the time, yet rarely using. A great deal of the JIT speedup you can get in such languages is working out how to skip those lookups optimistically until someone actually changes them.)
type fooParams struct { Name string Age int Address string }
foo( fooParams{ Name = "Bob", Age = 24 } )
Defaults are a lot harder to do in a way that doesn't just suck. You can make a DefaultFooParam that has all the defaults... but it's not pretty.
List comprehensions never seemed like a big deal to me. It's 3-4 lines for a loop, which is probably easier to read than the list comprehension anyway.
that's totally a nope.
def qsort(L):
if len(L) <= 1: return L
return qsort([lt for lt in L[1:] if lt < L[0]]) + [L[0]] + qsort([ge for ge in L[1:] if ge >= L[0]])This is classic "I'm trying too hard to make my code look clever rather than make it readable and easy to understand".
Go is what you'd get if python and C had a bastard love child that turned out different from either, but still retained some of the beauty of each of its parents.
The other semi-interesting things it has are (a) channels + parallel functions and (b) the return of error codes for error handling.
Java things it doesn't have start with reflection, bytecode (CPU independence), dynamic runtime code generation, inheritance hierarchies, generics, "everything is an object", array covariance, exceptions as an idiomatic error propagation scheme, scoping based on nested namespaces, no code outside of classes, nested types with scoping to match, inner classes with implicit outer class self pointer, only value parameter semantics, and only reference user-defined types.
In fact most of what makes Java distinct from the subset it shares with C is missing, except for a garbage collector.
Also... Go has OO features. You can make an object-like thing by tying data (a struct) with behavior (methods tied to the struct). It doesn't have inheritance which is not the same as not having OO features.
Pretty much any common language other than C/C++ could be (and probably has been) called a blue-collar language - C#, Java, Javascript, Python....
But you're right: "white-collar" languages have the habit of never being too popular.
There are places where C++ is blue-collar. (Games, for one.)
Rust is meant to be white collar, seems to me.
The paradigmatic blue collar job to my mind is not taking orders at McDonalds, it's welding.
But regardless, most of the time it's still used to mean something lesser.
Not to pick on you, but people say this all the time, as in it's probably the thing people say most often on this subject, and I wonder: do we have any evidence for it? That is, do we have any evidence that the mid-tier languages which eschew more powerful abstractions (Java and its peers) lead to either more readable or more shareable code? Or is it one of those things that everybody "knows" but ain't so?
Personally, though, I think that's the wrong issue. Whatever extra productivity is gained by language magic, we're not talking orders of magnitude here. Usually it's mostly about developer enjoyment, lack of annoying boilerplate etc.. These are annoying issues, but not critical ones. I think the most crucial issue is state management, which can have an order-of-magnitude difference for some projects, as well as a significant effect on performance and scaling.
I'm not sure I like your distinction between developer enjoyment, lack of boilerplate, and state management. To me those things all have to do with good design.
That's a bit disingenuous, since Haskell typeclasses go hand in hand with parametric polymorphism, which Go doesn't offer.
Typeclasses plus existential types allow you to implement subtype polymorphism. But it is generally frowned upon in the Haskell community and certainly not widely used.
Disregarding implementation, if you drag in typeclasses + existential types, Java's interfaces are also comparable (yeah, they don't offer compile-time duck typing, but neither does Haskell).
Let's be more specific: instances should be declared with the class or with the data type. Orphaned instances (that go with neither) are discouraged and with good reason - instances cannot be imported explicitly [1]. It is all to easy to have two conflicting instances if people start defining instances apart from the type class or data type. Don't do that! Luckily, -Wall will complain loudly about orphaned instances.
where you don't have the ability to freely modify any and all source included
Given the above, this is not really true in Haskell. Since orphaned instances are bad, people often wrap a data type using newtype and define the instances with that type. Of course, this is not so much different from creating a wrapper class in Java with another superclass or interface. Except that defining new types in Haskell comes with far less ceremony ;).
FWIW, I was more interested in the implementation details of existential types, and how similar it is to Go (except that Go does it dynamically using reflection, last time I checked). You know more about Haskell and its idioms than I do, I just know about the features and make inferences from how they may be combined :)
I see Go as a C-like language with some of the annoyances cut out and a garbage collector added. You don't have pointer arithmetic, but you still have pointers.
When you look at it that way, the lack of generics is not so annoying.
The best part of Go is the fact that it forces error checking.
Except that it doesn't. Otherwise you would be forced to check the return type of fmt.Printf() and gang.
For the most part, it does as well as C does.
Which isn't saying much really :) There is absolutely no way to force you to handle errors in Go.
> Except that it doesn't.
Depending on the value of "forces" used. Maybe "forces" in the sense of "only viable option for someone not programming foolishly."
I guess that his code allocates a huge number of objects as all input and output classes are final. The JVM's garbage collector is able to run concurrently, so I guess it's the GC that takes so much CPU. AFAIK Go uses mark-and-sweep collection which is very slow.
That would explain the unexpected difference in running time as the JVM's garbage collector is very mature and fast.
Still pretty early though, I don't think it can do dynamic sizes?
"You already have this []byte, don't throw it away, give it back to the pool so you can reuse it."
It does dynamic size but at the moment I don't have separate per-bucket pooling settings; it's the obvious next feature. The stats collection is intended to show me whether that's a useful feature. In testing so far it hasn't been in my specific use case, but I'm still only hitting my application with load testing, not real load.
The use case for gomempool is relatively transient buffers, where that is a win vs. generating a lot of []bytes. Rounding to power-of-2 is a traditional, theoretically solid answer. There's also as somewhat more unusual one using Fibonacci numbers, which Erlang uses to size its memory hunks. Fortunately the interface is agnostic, if that's advantageous someday user code doesn't need to notice the difference.
I couldn't resist the first time in my career in which I could justify some bit bashing: https://github.com/thejerf/gomempool/blob/master/pool.go#L39... :)
Go has a stop the world collector, hence the poor performance and single core usage.
Both versions would benefits greatly from reusing instances instead of generating huge amounts of garbage, however the Go version would benefit the most. With the Java version he is simply throwing hardware at the garbage.
Know thy garbage collector!
The problem with heavily hyped languages is that some people jump on the bandwagon without really thinking about the problem they're having. And this is exactly what the author of this article seems to be doing. If you're having to convince your clients to maintain a project in a language neither of you have developed in and you guys already have experience in other languages, then the question should be "what will I gain from switching to Go". The author's justification seemed to be "It's hyped online" rather than any technical justification; and that strikes me as a poor decision from the start.
The author clearly knew about Go's lack of generics and was quite familiar with the language. After stepping away from the language for a while he decided to give it another try in a project where he thought the lack of generics could easily be worked around via Go idioms. He found out that the lack of generics _did_ hurt more than expected and wrote about that part in a way that projected his frustration.
- New technology is introduced. Nobody's really heard of it or used it, so beyond a few "Look at this new thing!" articles, nobody talks about it.
- The technology gets some penetration. New users pick it up, probably because it solves some issue or irritation they have. They write about how the technology helps to deal with it, or why it's interesting; if it didn't/wasn't, they'd probably not bother using it, and would be pretty quiet about the whole thing.
- If the technology made it this far, there is an explosion in popularity as more people pick up the tools and use the technology. Typically this results in quite a few people using it in the wrong way, or because it's the current 'hot stuff' (rather than because it will solve a particular problem), or maybe picking it up a little bit too early. Lots of these people will write about how the technology sucks, or how it doesn't solve particular problems. That's fine; it's the public knowledge-gathering phase, and although there will inevitably be a lot of negativity, other people will discover the benefits, and the technology will frequently evolve to address popular concerns.
- The fourth step, that you didn't mention, is that the technology and discussion around it becomes more "mature" - it's widely-used, everyone's more-or-less aware of the pros and cons. There will be the occasional post called "X Sucks, I'm Moving to Y!" or "Why I'm still using X and not Y".
tl;dr: that's the way it goes - it takes a while for new technology to work out the kinks and for the community to understand where and how it should be used. And while that happens, they'll talk about it.
I have not touched C in almost 20 years, but I was able to write a concurrent program in Go that outperformed a similar multiprocessed Python script by about 5X in roughly one day. Go does not have the same library depth as Python, but the stdlib is pretty good.
The thing about Go that is similar to Python is _my_ performance is good, and I don't have to expend much effort in boilerplate code, or learn a coding approach that is unfamiliar, yet I still get relatively high performance.
Go won't replace Python for me, until things like matplotlib and Pandas become available, but it make an excellent addition to the toolkit.
The author chose a newer language to build a real project in and then gave us an evaluation of how that language worked as a tool.
This is far superior to the typical benchmark-game blogs that try to compare a few languages using only a small test program.
I'm not sure I understand the post author's complaint here. What's wrong with writing your own SetDiff, SetUnion, and SetIntersection functions and doing stuff like the following?
baz = SetUnion(foo, bar)
(I have done very little programming in Go.)Edit: not that we can conclude anything from the lack of performance, though, we don't know how it was written.
Some of us came of age before the tyranny of King Java, learning such obsolescent tools as Pascal, Lisp and C back in school. (I'm gonna ignore BASIC and FORTRAN, other than as examples of what not to do) We learned how to pass around individual functions/procedures to support library code.
http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom...
That sucked in Java at the time, though, when you had to screw around casting Objects in your code, I don't see why people would prefer that.
22m-31m http://www.youtube.com/watch?v=on5DeUyWDqI#t=22m
(I give myself and Google a pat on the back for being able to remember and quickly find this!)
Generics would absolutely be nice for Go, primarily because they would allow the core libraries to provide all of these rich collections. However if we're discussing the notion that someone can write these themselves, it just seems contrived that people think generics are critical for any specific app -- in the example he described, it sounds like he would need one single concrete implementation.
new_set = old_set.union( another_set, element_comparator_func);
If you allow some helper functions or an interface to do things such as compare/hash members, it's totally doable. Extra points for passing the helper(s) into the constructor.Yes, you can still mismatch set types, but the casting/type-assertion in the helpers will fail with an explicit reason.
We used code-generators or pre-compiler macros or dynamically typed languages or we hand-wrote heaps upon heaps of boilerplate.
It sucked.
And regardless, Go only offers the last option.
This is where code generation can work really well, especially for boring crap like set functionality... but he's right that rewriting it each time you figure out you have a new set type is a pain in the butt.
I say this as a former C programmer used to doing such things to make library routines like "bsearch()" work.
Too many things in Java rely on subclassing, rather than functional composition.
(I won't post the Go code for criticism, and I couldn't stand golang-nuts)
I think that's a fair conclusion for him to have drawn. He certainly wasn't hating on go.
(golang-nuts is openly hostile to any kind of criticism, so much so that there's been talk of a community code of conduct; which the community didn't want. I don't blame him.)
That's sort of what you expect from any given implementation of a data structure and its related algorithms, though I imagine that some specialized implementations benefit from having them tailored to their concrete types. Examples, anyone, please?
Yeah it's magic, it multithreaded my code, or um I don't know the garbage collector maybe?
func test(myVar interface{})
will accept any type at all. You can then use Go's type switch or a call to reflect.TypeOf() to invoke different behavior based on the parameter's type. To my way of thinking, it's pretty simple.It is incredible how, regardless of programming language and UI toolkit, people still take the easy root and do heavy work on the UI thread. And don't make proper use of widgets customization capabilities.
Yes, I'm not a Java programmer, but I'm convinced it can be very fast.
But throw a rock and you're bound to hit an insanely slow Java GUI application or a web service that needs 10 times as many servers as any sane implementation would use
(and cost X million of my tax-paying money.. fucking consultants :)
Then the types of the hashmaps would all be the same (all map key->integer), and I could develop reusable procs for each set operation.
I believe, it is the same in Java, too: http://docs.oracle.com/javase/tutorial/java/generics/erasure...
Author of the blog post here. That is one of the reasons I shifted to using bit sets: specific element types can be elided.
I totally agree with all the points which most of you make. Firstly, you should almost never choose a language / technology which you want to learn when you are being given paid contract task to accomplish. It might be different at your full-time position, when picking new tool (after agreeing with the rest of the team or people making decisions) might really be a viable choice, giving you an edge in the end.
Beside that - if you're being paid by the hour, if you had been able to find a client who is willing to pay you for you to learn new stuff - stick to them as them as long as possible. Most of the clients with whom I had been dealing with would just fire me or terminate the contract.
As for the performance - and I'm not defending Go here - with discrepancies that big, I'd just assume that I don't know language and all of it's quirks and it's just me not using it properly. I know that you might had spent time profiling and optimising stuff, but sometimes the overall design of your program might be violating some of the rules of given language, punishing you with performance issues in the end.
(check http://www.techempower.com/benchmarks/ and the difference from first iteration to the last one, when people knowing each framework tested started pushing their favourite toys to give them an edge with misc optimisations)
From the two language which you had tested, Java was a way better choice here - no amount of hype around a language should win over mature libraries which were just suited for your job.
PS: and yes, I'd love to see generics in Go and can't understand any of the reasons for no.
Which in turns means that the most of the "computation" performed is actually the creation and collection of the always new objects, which people who ignore the lower-level aspects of their programs consider being "zero-cost." Which is not.
I suspect that what he observes is the garbage collector running on more cores at once (AFAIK it is designed to run so now) and that the garbage collection was the cause of the slowness in Go too. I persume he managed to have a heck a lot of allocatations and that caused the slowness of Go and the full-throttle GC run in Java. But this is just a guess. If anybody knows more exact details please write!
A lot of the standard operations under the hood are in fact executed in goroutines in a pool of threads. IOs are, for example. So you found yourself gaining all the advantage of asynchronism without supporting the cost in the code... exactly like this example in Java.
Why doesn't my multi-goroutine program use multiple CPUs?
You must set the GOMAXPROCS shell environment variable or use the similarly-named function of the runtime package to allow the run-time support to utilize more than one OS thread.
Programs that perform parallel computation should benefit from an increase in GOMAXPROCS. However, be aware that concurrency is not parallelism.
Go is different and maybe there is a good representation of the problem in a pattern that is different of what he is used to. Or maybe not.
We'll never be able to find out, as the article has no details at all on the actual data models and transformations he was trying to perform. Just rant.
-> Several people have suggested that JVM's mature GC may be using other cores, resulting in the > 370% CPU utilisation. I admit that I do not understand the internals of OpenJDK's GC. However, I would like to point out that 100% of the roughly 1,200,000,000 statistics results instances created during one run are not released until the program terminates. They are all stored in statistics results containers until the very end of the run. However, see below.
- From a processing perspective, other than the manipulation of containers and bit sets, almost all of the remaining processing is numeric. It is full of integers and doubles getting added, subtracted, multiplied, divided, square roots calculated, etc., as part of various statistical computations.
- Where I needed sets was in preparing the several million working sets over which processing iterations occur. Working sets are prepared based on user's query criteria, several thresholds for filtering elements under a variety of constraints, etc. From a garbage perspective, these working sets are the biggest garbage generated during a run.
- Go's numeric performance is certainly very good. I have had good experiences with it in other projects.
- It is true that I could not identify the causes to which the performance differences between the Go version and the Java one could be attributed. Was I being naive? Am I incompetent? Possible. As I concluded, this behaviour may be obvious to more informed people, but it surprised _me_, and, that is what I admitted.
I totally agree that programmers need to use the right tool for the job but;
Most of the programmers will not know the core implementations of a programming language when they started using it.(even after a couple of years) This leaves them only one choice to decide if it can replace their primarily preferred language or not. Trying out the language.
If it gives a bad taste from the beginning, doesn't ease the implementation, or better performance, then programming language won't have much chance, will not get attention and growing community.
This is why Ruby got a popular community with the help of Rails framework. Developers found it very very easy to develop apps, it was very much readable code, and you save lots of time in order to get the app up and running. This advantages just made people ignore all the disadvantages of the language. (concurrency, being hell slow, etc.)
As much as i want to try out Golang one day,code examples on internet doesn't blow my mind like "wow, how smart that is!!"
But yeah, I have always liked the argument of throwing a curve ball at the kids who were already programming in imperative languages. It also levels the playing field and those who hadn't had any prior experience tend to perform better without feeling so inadequate next to their peers.
Could someone give a brief introduction to what this sentence means? I have absolutely no idea about any of it.
1. leave generics out : the c way (slows programmer)
2. do it at compile time: c++ way (slows compilation)
3. box/unbox everything implicitly: java way (poor perf)
Microsoft's current JIT/NGEN, generate a single version for reference types and a separate version for each value type. So a mix of Java/C++ approaches.
Not sure what the new JIT/NGEN compilers, RyuJIT will use.
I also don't know what approaches are taken by Mono, Bartok and IL2CPU, but they might be similar.
I was going to say that the c# compiler is fast enough despite this, but then I remembered that one of go's selling points is that the go compiler is blindingly fast compared to languages such as c#. Perhaps maintaining that performance with generics is a real issue.
Many old timers like myself can remember the blazing fast compile times of Modula-2 and Turbo Pascal compilers in MS-DOS systems.
Go compilers also lack a strong optimizer, which is tends to slow down compilation.
Using a half-baked model and implementation now boxes the language in later on.
I'd be more worried about having two programmers write two implementations in the same project with subtly different interfaces, and having different parts of the code use each one. This is a cost and maintenance problem, but that's also a social and communication problem common to all projects.
Anyway, have you seen the map[T]struct{} idiom for Go? Decent enough for the basic case.
This doesn't mean you're always safe even on a language's home turf (Rails and Node each had significant problems as a server platform for their first, hyped, years), or that it's never worth stretching the domains that a language is used for (that's where Ruby's Rails and Python's Numpy came from), but you should recognize that you're out on a limb when you do so.
I'm actually dizzy from shaking my head too much while reading this.
https://gist.github.com/mediocregopher/8319986
The items in the sets don't even have to be the same type
As for poor performance, that is almost certainly a result of rampant object creation, rather than reuse. Not sure how much of his program was concurrent, how sophisticated it was, etc.
Ultimately if the data set fit in memory, Python would probably be the appropriate choice for this kind of computation.
Replicating the C++ code generation tools of the mid-90's when templates were started to be discussed at ANSI/ISO.
As for sets, why not use a custom data structure like a balanced binary tree (such as a red-black tree)?
see also: https://github.com/kisielk/bigset/blob/master/bigset.go
I would also say if your handing over code to another team to support and they don't know the language and the only reason you are allowed to do it in that language is they couldn't think of any objections that you can't bypass then don't use that language!
personally i'm waiting for rust, go is nice but not better enough than python to justify switching - especially when you consider maturity of third-party libraries.
> personally i'm waiting for rust
See, this seems weird to me because Go is actually a language that you can build nice things with right now. Check out coreos's etcd[1] for example.
Rust on the other hand gets a lot of adulation in this echo chamber but is so, so far from completion that you're going to be waiting like a decade or so before you can do anything networking related with it. And personally the varying clumsy degrees to do the same thing many different ways in Rust (the various boxing and unboxing and other memory handling options) are a real turn off.
Comparing Go to Java is fair, and of course Java will come out on top given its maturity, but Go offers a lot of good things. Comparing Go to Rust is not, Rust is not ready for any kind of real world use.
Servo’s net component would like a word: https://github.com/mozilla/servo/tree/master/src/components/...
Yeah opening up TCP connections and reading plain text http isn't hard, but there's no way to do any kind of SSL https requests with servo, which is kind of a nice thing right. And that's going to be a long long time before you ever see SSL. Have fun with those C libraries.
We'll see, though.
Not true. My 'learn rust' project is pcap-based and has a trivial websocket implementation, and (after the basic learning hump) it's dirt easy. Aside from how simple it is to interact with C, Rust's current networking stack is libuv under the hood (i.e. same as node), and if you don't want libuv, native implementations are landing now.
EDIT:
It's not petty to point out that a brand new repo that doesn't look like it works, has zero documentation has been seen by a handful of people is not a good example. This is for secure code. SSL. It's pretty important to get right. This isn't a a repo for a JavaScript accordion widget. It's SSL in Rust. I'd like to see SSL in Rust. I want Rust to do well, there's nothing petty, you're reading my tone wrong. It's hard to read intention over text, assume better than you are. I've been sitting in #rust on mozilla IRC for nearly a year watching them develop the language. I'm not just catching up on the latest by reading HN.
As for jfager's a decade vs more than 3 lines rebuttal: read my other responses where I specifically qualify on using C libraries. I am going to going to go ahead and guess you haven't done much raw SSL stuff. It is a ton of work. We're not talking 5 more lines of code. We're talking thousands of lines of code, and that it will be a decade before rust catches up with go. That's just an anonymous commenter making a shitty prediction that nobody will call me out on. My point is that Rust isn't ready. Go is. Stop comparing Go to Rust. Rust doesn't work yet. Don't use it unless you want to help develop the language. All you're going to learn is how to work with a specific half implemented language that can't do basic things yet. Read my original post. I don't hate Rust, I am contrasting the HN echo chamber to love of a language that you should not be using for anything right now. This is not a controversial statement, it's right there in a big highlighted disclaimer on the rust reference manual[1].
[1] http://static.rust-lang.org/doc/0.8/rust.html#disclaimer
i'm not denying that! in fact, i've written a couple KLOC of go code to make sure my gut feeling is right. it's a nice language and i kind of like it, but i'm much more productive in python where pretty much everything has a library written for it.
(python would certainly benefit a lot from channels/select as a langauge construct, if the GIL could be worked around.)
> there seems to be quite a few people suggesting that go
> is the correct choice for every task.
Literally nobody has said or implied anything like this.IMHO golang is extremely orthogonal, that it tries to live to an ideal instead of being most practical (ie generics).
btw, folks should give Elixir a shot (pun intended).
template <typename T>
T min(T a, T b)
{
if (a < b)
return a;
else
return b;
}
This code works for any type T (ints, strings, floating point numbers) that can be compared with less-than. In a language like Go that does not support generics, you would have to write a min function for ints, a different min function for strings, etc.Not saying it's a bad thing, just that most other languages would show something like "a.compare( b) < 0" to do something like this.
- The compiler will fail to compile your code if you put anything in the list apart from a Transaction
- The compiler will fail to compile your code if you try to do anything with an object from that list which a Transaction can't do
- It also makes the code more readable as if my function takes a List<Transaction> you know what you can pass to it. Does it take a List<Transaction> or a List<TransactionId>? It's obvious.
- If you try to pass a List<TransactionId> then the compiler won't compile your code. (Otherwise you'll only find out about it at runtime.)
This means a lot fewer fails at run-time.
Which in turn means a lot less time writing (and maintaining) unit tests.
I think this attempts to make the practical state of generics in Go into some sort of religious disagreement, which is unfortunate, as that isn't the case whatsoever.
Whenever the topic of generics come up, the prevailing sentiment is that they would be great, but that the Go creators have some implementation difficulties and some competing goals that make it impractical at this time. I don't believe I've ever seen anyone of consequence in the Go community treat generics as anything other than "would be really nice".
It is a gap in Go and is widely treated as one.
Go absolutely needs more robust collection types, however. Total agreement on that.
Generics have been discussed to death regarding Go, and there is a long and complex history of the choices and realities in Go. So it is understandable that people can get a little irritated when someone stumbles in and declares that maybe Go should add generics, usually then decorating their claims with lots of exaggerated hysterics (most recently that Go is an "embarrassment" because it lacks generics. Quite the accolades for the people who work long and hard on it).
The most popular response to any language debate seems to be "it's not a problem in practice", implying all critics just "stumble in" and don't know what they're talking about.
They have debated this many times over in the past.
What they don't do is repeating that debate every week. It would take a lot of their time and is largely pointless (in the sense that person that starts the debate with 'go sucks because it has no generics' will not be convinced that it was the right (or at least practical) decision to make regardless of how cogent your arguments are).
That said, that's when a community should write up an faq[1] and then simply point to it in a polite way that avoids engaging in the same conversation again while still being welcoming to people thinking of joining that community. If people aren't doing that, it's somewhat understandable, but long term the acrimony can be poisonous.
I fully understand that the Go team and long time forum dwellers are not inclined to discuss the same generics proposals over and over again. So don't!
New ideas should be welcome though.
As an example look into the way sorting is implemented in the standard library: http://golang.org/pkg/sort/
I had to look at this example for a long time to get it... And now I have forgotten it again since it was months ago :-(
I still thought it requires too many lines to sort stuff, but it is doable.
>Except that abstracting things into functions that take the `old' graph as input, and answer the `new' graph as output are not very convenient or easy.
Is just crazy. Yes, it is very convenient and easy: http://hackage.haskell.org/package/mtl-2.0.1.0/docs/Control-...