Why I switched from Ruby to Go
codegangsta.io
codegangsta.io
Same thing will probably happen to node.js pretty soon. It has already been happening to MongoDB for quite some time.
Go is currently in its hype phase.
Some people seem to make a living by writing post-mortems.
Why does everything always have to be the latest and greatest thing on earth?
Can't we just institutionalize the hype cycle? Like fashion, minus the seasons? One big show every year where everyone can compare their dicks and then let's shut up about it until next year?
While I tried getting into Go, and initially didn't like it but wanted to like it. Seems like the more I read about it, the more I get into it, the less I like it. Which kinda sucks, Go seems like a powerful platform.
"While there was some hype about Rails, it didn't turn out to be a fad. The ecosystem is thriving and not slowing down anytime soon, especially with Ruby 2.0 and then Rails 4.0 coming quite soon."
is a thing someone could have easily said on this site in January 2013.
Just because it's not Java-sized doesn't mean the community isn't there.
C/C++: Easier than most of the other low-level languages. Used when performance and/or memory management are critical: kernel, database, embedded system, etc.
BASH: Dammit, I just need a script that executes five commands in a row!
PYTHON: A scripting language that properly handles gigantic numbers and seamlessly integrates with R. The scientists love it!
And the list goes on. Of course those languages have other use cases, but the point is that they all have at least one use case that they are the best at. However only time will tell if Node.js ends up being the non-blocking scripting language or something else will take its place.
Arrogance to be tolerated needs to be backed up by technical knowledge. Linus Torvalds is arrogant and abrasive. I don't mind it as much because of his experience, knowledge and his contribution.
Some guy yelling about how asynchronous callback programming is the best way to handle concurrency and is the way of the future is doing a disservice to whatever community he belong s to (well node.js in this case), it just deepens the negative stereotypes.
Technology in itself is fairly neutral, tools that work for some use cases and don't work for others, trade-offs and so on. It is the people that use the tools that give those tools a bad wrap usually.
No matter the mechanism, event-driven programming is here to stay. All UIs must be event-driven unless you enjoy pissing users off. Games must be event-driven. Web servers should usually be event-driven for similar reasons. Callbacks are just the most basic way to achieve that when you have an event that won't finish processing for awhile like a DB call.
I certainly understand the desire to not use callbacks since I also loathe them. But they are also much easier to understand than something that looks synchronous but actually isn't. But there are a lot of people who have tried and are still trying now to rid us of the need for callbacks but still allow for async. I certainly hope they prevail, but, historically speaking, I am not optimistic.
I think Go (and heck even Clojure with it's added core.async model) would be better at winning the first award. They are much more general and can be used freely. Node is basically limited to web and web servers (including tcp servers here).
I think Node is turning onto generator mode. Now that generators hit V8 (and Node under a flag), it's much easier to write async code (even easier than callbacks) yet you write in a synchronous style. That'll definitely improve the concurrency model.
Reasonable languages provide actual concurrency constructs, allowing you to write simple, easy to understand code, while providing all the benefits of both threads and event loops. Traditionally you needed to use a good language like haskell or erlang to get this, and that caused problems for people who want to stick with primitive languages. Now go has come along and offers similar benefits in a primitive package, so there is no need for people to have to stick with low level, error prone event loops.
But, Node made it easier to write purely asynchronous code and using a language people were familiar with, that's why it became so popular. While Go is has an insanely good concurrency model, Node has it's quick iteration going for it and a large number of packages.
Now that generators (coroutines) have made it into V8, the concurrency model of Node is changing, for the better. Node is still very much limited in application (thanks to JavaScript and the event loop.). Whereas Go can be used for a lot of different things. But Node specializes in web applications, in web servers. Thus, it's going to be more productive than a general purpose solution.
Big deal. Concepts are being repeated and technology stacks are stealing ideas from one another. Remind me again how that is a bad thing ? The fact is that there is no perfect solution to every problem.
However, the problem is repeating mistakes. Go's nullability, lack of generics, ... Node doing manual CPS where past work (e.g: Haskell) does non-blocking IO without forcing manual CPS on the programmer.
It would be nice if we learned not to repeat past mistakes...
The word "fad" is being redefined as the cycles are getting shorter. Maybe Node, Ruby, Go are/(will be) all just 10 year fads, but when do we redefine the word to be more appropriate?
if (typeof my_var !== "undefined" && my_var !== null) {
// you idiots put Rasmus Lerdorf to shame
} if(my_var != null) {
}I'm no fan of javascript, but I'm also no fan of internet tough-guys who think that throwing a few "fucking"s and "butthurt"s into their prose makes them seem like anything other than bullies. The only thing I take away from that criticism of node is that Ted Dziuba is an awful person.
I really hope that "XXX" mean "a different language" and not "porn". That'd be horrible. :)
(I was also unhappily maintaining legacy php / mysql code 5 years ago and I'm still unhappily maintaining legacy php / mysql code today :( )
Probably because the existing solutions have pain points and the "latest and greatest" technology solves those problems, but introduces new ones, which will be solved by the next big thing. It is a never-ending cycle because people tend to want to improve on what they have.
These trends/fads seem to me to roughly follow cycles where a new "generation" of devs enters working/professional life, seemingly unaware of many solved problems and instead solving them all over again rather than actually pushing the state of the art forward.
Most of the "I'm switching for 10 years ago's hype to Go" articles (including this one) are actually saying "whoops, 10 years ago's technology had drawbacks, I just discovered them, let's use 15 years ago's technology with fancy clothes".
from http://www.freepascal.org/ : "It can target multiple processor architectures: Intel x86, AMD64/x86-64, PowerPC, PowerPC64, SPARC, and ARM. Supported operating systems include Linux, FreeBSD, Haiku, Mac OS X/iOS/Darwin, DOS, Win32, Win64, WinCE, OS/2, MorphOS, Nintendo GBA, Nintendo DS, and Nintendo Wii."
Hype cycles are required for people to write books about, provide certifications, create conferences explaining how the respective technology will save the world, get contracts to re-write working systems ....
Indeed, it's already been done, by Gartner, would you believe?
Maybe three years from now, Rust will be ready for production use and switching from Go to Rust will help people become more productive and we'll see blog posts about it. This is how progress works and it's nothing to scoff at.
The hype cycle has well-known biases. Knowing when a product is peaking is helpful to me, in that I can basically ignore 95% of the noise about it.
This post is a fine example. He could have written exactly the same post about C, because he's basically saying "compiled binaries are easier to distribute". He's been blogging about Go for an entire two weeks. The only potentially new information here is "Go produces a binary."
That's not to say he shouldn't have written the post. He learned something and he shared it, which is great. It's just that nobody would be talking about the C version of the post, because that's not the hyped technology.
Not a groundbreaking revelation, but his point, I thought, was the expressiveness of the syntax PLUS the portability of the code.
If you try to be in for a new perspective, it might be useful, since 'Everything is a tradeoff' (tm ;)[1]. Currently I evaluate tradeoffs between speed, readability and maintainability.
In case of Go, I'm also interested in how someone like ken designs a language today.
[1] http://michaelrbernste.in/2013/11/13/the-only-sure-thing-in-...
On the other hand, when I get to do a short-lived project I tried to use a new technology: that's still the best way to learn, plus it's loads of fun. If it's great then I start using it for longer term projects: that's how I found (and so far sticked to) AngularJS.
In 2013 I shipped small projects built on top of nodejs, chicago-boss (erlang), meteor, but also RethinkDB and coffeescript. RethinkDB genuinely seems like a great document store: I will probably give it a second shot when it's more production ready.
> On the other hand, when I get to do a short-lived project I tried to use a new technology:
Hey, that's a pretty good idea, I like it. So what are your impressions so far on Chicago Boss, and what do you like about RethinkDB? I have been following those as well but haven't got a chance to try them yet.
The less impressive part is the general lack of polish: for instance the Django templating system is quite forgiving when it comes to types and data you feed it with. Chicago Boss not at all, which can be frustrating. The lack of community around Chicago Boss is probably the biggest downside though.
Anyway I got a working demo product, which was good enough to land a bigger project. For that one I moved back to my usual stack, to avoid codebase fragmentation. I'll probably give Elixir and Dynamo a try if I get an adequate project...
It's important.
There are good reasons to switch to Go (mainly: fast compiles, WYSIWYG syntax, tooling, standards, a good library, and static compiles). In some contexts, it is clearly better. It isn't bloated, and unlike Java, the authors don't seem inclined to make it so, nor do they have any axe to grind except simplicity.
To me, Go feels like an old friend that I've been missing for many years. I've always had C and Python, they serve me well, but there was a big gap of uncovered space in between them - you know, when C is too fine a tool for the job, while Python is a bit too "dynamic" - until Go came along.
I tried filling that gap with Scala, and while the cleverness of the (most of the) language did have its appeal, eventually I stopped using it, since it was just too much of a tool which got in the way.
Go hits the sweetspot. Everything about it screams unix. The toolchain, the speed, the simplicity of the language, the powerful concepts delivered in a dead simple to use way (goroutines/channels)... It's like a breath of fresh air whenever I have to work with Go.
Just like C and Python, it's a keeper.
Go is less than the "latest thing" and more of an evolution of some old ideas, especially when compared to other "latest things".
Anywaay Go looks like a good solution for what he's doing. I tried distributing a Ruby-based app years ago (maybe around 2008 or 2009) with a pre-compiled version of Ruby for Windows bundled as part of my programme. The Nullsoft installer package I wrote took ages to write all of those .rb files (that make up the standard lib). It sort of worked OK, but the project didn't go anywhere -- that was probably lucky -- it'd have been a nightmare to maintain.
Granted now you've just punted the problem of Ruby version to JVM version (which I've found to be less of a hassle) but at least took care of the nightmarish management of gem dependencies without something like bundler.
$ gem install warbler -v 1.4.0.beta2
$ warble
This creates a fat jar which is runnable with the JRE. It gets you out of RubyGems/Bundler/RVM hell.
Go's official docs and standard libraries seem to reinforce this style choice, and most 3rd party code follows this general C++ style inspiration too. I know I can do what I like in code that I would write, but it seems like I'd be swimming against the current.
database *Database.
So db *Database
looks a bit nicer in my opinion while it's still very clear what it is.No it doesn't. It involves naming things appropriately. If you have a 4 line function that does something with a string, that string argument should be named "s". You aren't making it clearer by giving it a longer name. The length of a variable name is proportional to its scope.
's' has no meaning by itself, which means that you have to read all of the context surrounding 's' into your short term memory just to gain any understanding of the single line you may be interested in. That's slow and more work than necessary most of the time.
Also, naming things is a habit and a style. Short variable naming leaks out of 4 line methods into any 40 and 400 line methods you may eventually write, and it leaks into the ABIs/APIs of your application. I've rarely seen any code that would use 's' appropriately in a 4 line method that wouldn't hesitate to use it everywhere else inappropriately.
That is exactly the point. It is just a string. That is the entire context of the variable, there is no more insight to be gained. The more generic a function is, the more generic the argument is, and thus the less context exists to be put in the name. Naming it "thisIsAStringJustSoYouKnowTwiceThatItIsAStringLikeTheTypeAlreadyToldYou" instead of "s" does not provide any benefit.
>I've rarely seen any code that would use 's' appropriately in a 4 line method that wouldn't hesitate to use it everywhere else inappropriately.
You should read more code. Both go and haskell have tons of examples, as this is standard practice in those languages.
Maybe we should do strlen(const char* theStringThatIsBeingMeasured), that's a lot clearer. Maybe we should expand char to character, and strlen to stringlength, because it might not be clear enough.
Need some underscores in there to separate the terms, maybe a prefix to indicate the type, and a url to the parameter documentation.
http://ieeexplore.ieee.org/xpl/articleDetails.jsp?tp=&arnumb...
You can contrive simple examples about basic data types all you want, the simple cases and built in types are precisely where it's not a problem. Go read the src for Go's Unicode package as an example, you get 60 line methods with stuff like 't1' used all over the place and the value of t1 is assigned pages away from where it's actually used.
Because that is the example I used.
>It could just as easily be a struct or anything else imaginable, in which case, you're back to not knowing anything about it from the name 's' without going to look it up.
If there were more information that could be conveyed, then it wouldn't be named "s". That is the entire point.
That's an opinion a lot of people don't and won't share.
Again, an opinion others don't necessarily share.
> Thus a longer name is not beneficial.
Again, an opinion others don't necessarily share.
You seem to have a habit of stating your personal taste as fact; it is not. Some of us always prefer full words as names as a matter of style.
When you stick to these rules, short names for variables become a no-brainer and arguably make code easier to read.
But I still use a lot of Python and I'm sure this guy still uses a lot of Ruby. Everything has its place.
Look, I've never been able to understand the people who think RubyGems is a good distribution mechanism for end users either. But switching to another language altogether seems to throwing out the baby with the bathwater. You can just create a Debian package or something that depends on Ruby. That's exactly what we do with Phusion Passenger.
Have gem dependencies? Vendor them. Not that hard.
But if you have gem dependencies that contain native extensions that aren't distributed by the OS... well then switching to Go starts to make sense.
He actually did mention that in the post, though -- packaging Ruby into the installer -- and mentioned how difficult it is. I've never tried it myself, but I imagine it's pretty tough if, for example, you're distributing to Windows as well (although who uses CLI apps in Windows anymore...?)
Many configurations are done via command line tools actually, more so since PowerShell exists.
EDIT: Strike that - I just realised one of my apps gets 117,000 ENOENTs from trying to handle require's during startup....
EDIT: I keep wanting to write something to cache the paths, but haven't had time. I'd be perfectly happy to be forced to regenerate cache on first run after any gem update. The 117,000 ENOENT's above comes from ending up with a $LOAD_PATH of about 100 entries, where the worst case causes almost every one of those directories to be checked for both foo.rb and foo.so. Something like 99%+ of startup time of most of my Ruby code is overhead added by rubygems way of handling require.
EDIT: Actually, a lot of this might be down to bundler rather than rubygems in some cases.
EDIT: This is not a robust solution, but this little ugly helper combined with wrapping only the two require statements shown, reduced the number of failed stat() calls for my app from 117,000 to 104,700 on startup...
$orig_loadpath = $LOAD_PATH.dup
require 'bundler/setup'
$full_loadpath = $LOAD_PATH.dup
def with_gems(*gems)
filtered = []
gems.each do |gem|
filtered.concat($full_loadpath.grep(/#{gem}/))
end
$LOAD_PATH.clear
$LOAD_PATH.concat($orig_loadpath)
$LOAD_PATH.concat(filtered)
yield
$LOAD_PATH.clear
$LOAD_PATH.concat($full_loadpath)
end
with_gems('require_relative') { require 'require_relative' if RUBY_VERSION =~ /1\.8/ }
with_gems('amalgalite','arrayfields','fastercsv') { require 'amalgalite' }
EDIT:
Wrapped a handful more require statements with "with_gems". Down to 76,000 failed stat()'s... I should have done this before.EDIT: Yikes. 32,000...
[0] Ada, OCaml, Haskell, Rust, ATS if you're really perverted?
- static linking only (unless you 1. use cgo and 2. dynamically link against a native lib)
- trivial cross compiling (but no cgo)
If I wanted to distribute a Ruby app to non-Rubyists today, I'd likely end up packaging up the Ruby interpreter of my choice and all dependencies in a single archive rather than trust a sane environment on the users machine.
And that obviously limits the type of situations you'd want to use it substantially. For my part it's not really an issue, because of what I'm using it for, though.
Makes you wonder if all the people dismissing everything as 'fads' even have jobs. Because I can't imagine anybody hiring someone in IT who is so proud to dismiss any new technologies not on merit but because it is somehow fashionable.
We don't rip off the majestic buildings of Manhattans because they are more than 50 years old. We preserve the buildings that survive the test of time and demolish the ones that don't.
It's the same principle here. The COBOL system that is existing right now is the survivor of brutal business changes in the past 40 years. You don't kill a survivor - you maintain and enhance it.
But I do agree. There is a in inherent higher barrier of entry when you force your users to install third-party libraries or even your deliverable from an outside ("fourth-party"?) distribution source. Be it ruby gems, CPAN, Pypy, cabal, whatever.
So, the articles will slowly turn from "why X is great" to "I don't want to move to X because...", but the meta-message is that you get a survey of tool usage.
Always look on a bright side of hype, tu-dum, tu-dum-tu-dum-tu-dum.
And for those who want to do cross compilation _with CGO_, it's definitely possible and I put a little tutorial to do it: https://gist.github.com/steeve/6905542
The problem is now these hipsters are moving to nodejs so that community has exactly the same problem too.
Go community,while little, is more like python's, mature,respectful(most of the time),that's important on the long run,to build a community around positive and cosntructive thinking.
NodeJS will burn itself like rails if it goes on that way.
Fuck. Yeah.