Hacker News new | past | comments | ask | show | jobs | submit login
Ruby 3.0.0 RC1 (ruby-lang.org)
327 points by chenzhekl on Dec 21, 2020 | hide | past | favorite | 131 comments

I'm glad to see they're picking up native type-hinting. I don't like many of the existing bolt-on type systems for Ruby and the whole thing feels so much more kosher when it's official.

The concurrency stuff is cool but I can't really comment on it, my rule is "if you're going to even think about threads, just use Java or go" since concurrency is so nice there.

I really wish ruby had won over python as the "general purpose scripting language", I like it better. For example, I wish all the "big data" tooling was written in Ruby. But python is just the lingua franca. PySpark, Pandas, Airflow, etc are all python. I'm not really going anywhere with this, just lamenting I don't get to do more Ruby in my day job. I recently switched to writing python at home just because my productivity is so much better because I've memorized the standard library better from using it for years at work.

I worked with both Python and Ruby daily for several years, both quite a few years ago and now just write the occasional scripts in those languages (small scripts, maintenance of older projects, patches for various projects when needed, etc.)

I much prefer Python for this. As an occasional Python/Ruby programmer I find it's just so much easier to work with. Ruby has a lot of features, which is part of its appeal, but that also means a lot of remember and ... a lot to forget. If you're not steeped in it, then that makes things quite a bit harder.

I programmed some Ruby a few weeks ago; it had been quite a while since I last did any Ruby, and ended up having to look up the syntax for blocks. This is really basic stuff, but I had written str.gsub!('pat', {|m| ... }), and the correct syntax is of course str.sub!('pat') { |m| ... }. Kinda silly I guess and a bit of a "oh, du'h" moment; I spent two years with Ruby as my day job, but that was also five years ago, and I think these kind of mistakes aren't that unusual if you're not programming Ruby too often.

And then there are caveats like using "return" where you should have used "next", and quite a few other things.

Don't get me wrong, I like Ruby, and there's also a bunch of things I don't like about Python. But I can definitely understand why people settled on Python. I think Matz once said "Ruby is 80% Perl and 20% Python"; I feel that with a slightly lower Perl-to-Python-ratio Ruby would have been better.


There may also have been implementation considerations by the way. I believe that historically at least, Ruby has been a lot slower than Python, although I think the differences today aren't very large. And a lot of these tools are written as native extensions, which Python seems to support a bit better.

> But I can definitely understand why people settled on Python

I think people settled on python because for a spell it was really hard to install ruby (1.6-1.8), it got worse around 2.0, (by the time python also ran into these issues, it had way more momentum) and:

- schools (high schools, colleges) were teaching python, because "it forced students into code indentation discipline"

- linux distros shipped with python because core features were being written in python

After five years without programming in a language, one will inevitably forget syntax and/or concepts, that's not something specific to Ruby - there isn't a presence of "sharp corners" more than any other language.

Actually, Ruby's selling point, during the golden times (I guess it was around the mid-2000s), was actually the simplicity and human friendliness of it.

I guess (but it's hard to prove anything in this sense) that Python came out earlier as human-friendly scripting language (1991 vs. 1995), and Ruby didn't provide any killer feature - until Rails came out, but that caused Ruby to "be" Rails, while Python had other areas of application, for example operating system tools.

To be clear, it's not that I didn't write any Ruby for 5 years: I just didn't write a lot of it.

And yes, I've also forgotten plenty about Python; but in Ruby there's just so much more to forget. I've retained much more of my Python knowledge.

> I wish all the "big data" tooling was written in Ruby.

On a practical note, Ruby's heavy use of reflection, inheritance and method_missing style dispatch currently makes performance for big data tasks less than ideal. Python is less expressive but its "one true way" makes things like vectorization and type specialization easier for data tasks.

Sometimes you really just need a better Fortran.

I believe you’re confusing Ruby with Rails.

While these can eventually come in handy sometimes, they are by and large seldom used, but Rails heavily leverages such patterns for flashy yet ultimately questionable magic.

As a long time Rubyist, I believe Rails, for all its innovation, has been warping the view of what Ruby is, and when and how to use its features responsibly.

> While these can eventually come in handy sometimes, they are by and large seldom used

I don't think they are seldom used in practice - I think large parts of the Ruby ecosystem use them pervasively, even if you disregard Rails.

But even if we don't agree on that - it's missing the point. The point is you pay for much of Ruby's metaprogramming, even if you aren't using it. So not using it doesn't help you.

A concrete example is that you pay for `Kernel#binding` every single time you finish using a local variable but Ruby retains it for the binding, even if you never ever call `Kernel#binding`. Even JITs cannot remove this cost.

> The point is you pay for much of Ruby's metaprogramming, even if you aren't using it.

I don't think there has been any meaningful attempt at assessing the performance of Python vs Ruby.

If we consider the benchmarks game, their performance is comparable.

I assume that PyPy is faster, but whether to consider it representative of Python's general performance is arguable.

Which cost do you mean? Needing to keep the value alive for deoptimization if Kernel#binding is actually called or?

I concur.

The dynamic inheritance part of Ruby is still core even in a non-Rails ecosystem. That does make optimizing performance more difficult.

But on a much simpler level, a lot of Pythonic data code is just functions + values, without them being complected in an object. That makes a lot of stuff like wrapping numerical libraries in C very easy in Python. I think you'd still have a bit of a culture shock writing procedural, return-by-value code in Ruby. That's pretty normal in Python and I think one of the (many) reasons it got picked up by STEM disciplines in academia. Looks over shoulder at awful code in Numerical Recipes in Fortran book.

Different strokes for different folks and all that, I just think there are probably constraints (and cultural values) that Python has that make it more suited to data problems than Ruby.

I'm inclined to agree.

One of Python's strengths in this space is that it's a procedural language with just enough in the way of object-oriented facilities. That makes it possible to write reasonably object-oriented libraries with easy-to-learn interfaces, while still sticking to procedural idioms for the actual data hacking.

Which, in turn, means makes it a comfortable environment and interfacing mechanism for both engineers and analysts.

That said, sometimes I look at tools like https://moosetechnology.org and desperately wish I could be using something like that at work.

Just enough?

Python has the full program with multiple inheritance, metaclasses, abstract classes, slots, attributes, properties and decorators.

One can go wizard level with Python's OOP facilities.


Another thing is that outside of scientific data applications, ordinary data processing apps at scale have been map-reduce for a long time. That's an inherently functional paradigm that's closer to Python.

Having parallelized heavily OO Ruby code across machines by unrolling it into functions that could be sent data payloads... that's not a fun thing to sell in an OO culture. In Python you're kind of already writing code that way, so increasing performance by splitting the data onto different machines is simple.

Functional paradigm closer to Python? I think you mean procedural as Guido has openly professed his distaste for all things functional to which Python's crippled lambdas bear witness. Ruby, on the other hand, with its procs, blocks and lambdas, supports an elegant functional style even if it is implemented in OO.

So I'm using a simple, deliberately not correct version of "functional" because a lot of people don't know the difference.

I don't like it when people break out their monocle and pipe and complain about this or that being true functional :-). I really do just mean "procedural functions + return by value instead of mutation" here.

It is completely normal to have a python script that is just ordinary functions, without state being enclosed in a class and accessed via `self`. This "My First Program" behavior scales extremely well, both from a parallelism perspective, as well from a complexity perspective. OO features can be applied iteratively on top of that.

With Ruby--syntax aside--it is just very rare to see people only write functions or private_class_methods. The "Everything is a Class" mantra is very strong, and there's lots of rich behavior in Ruby attached onto what would be considered primitives or basic structs in Python.

With data science specifically, this natural way of pushing simple procedures, primitives, structs from Python down into C has lots of performance benefits. Because those functions are "flat", you don't have to traverse the call stack routinely and lose performance there.

All of which go back to Smalltalk blocks and collections.

As for Python, Guido might not like it, but Python has enough features to do FP, in spite of crippled lambdas.

I don't think that would really be too bad, TBH. Don't forget that the first project to popularize distributed map-reduce, Hadoop, was a Java project.

All you really have to do to translate map and reduce into an OO paradigm is have the map and reduce operations take objects that implement specific interfaces instead of functions that have a certain signature.

> Don't forget that the first project to popularize distributed map-reduce, Hadoop, was a Java project.

Yeah, but that required everybody learning a different style of programming that wasn't OO ;-).

The problem is for "map", you have to get rid of shared state and the interleaving of state and time. Interleaving state and time is basically the only thing objects do beyond providing a namespace and polymorphism. There's also an inherent 1-ary behavior in most OO code. If you've got a complicated object graph, making something a bulk N-ary operation is non-trivial. A lot of ORMs have this problem when trying to optimize queries. When you're passing a vector of "state" to a function, it's much easier to make things bulk. (Anyone who's had to make legacy code provide a bulk API with an efficient DB insert operation knows what I'm talking about).

What you usually get in Ruby is a bunch of code that's like Duck.quack() and a complex set of things below that interface. What you need for data processing is something like quack(ducks[]). The way of parallelizing real-deal objects keeping them objects is with actors, and then you need to worry about distributed state a lot more.

Anyhoo, that's a long way of saying that the much-derided procedural or functional style of Python avoids this problem from the get-go. You can just slap the "map" step onto a stream processing system without cutting your graph into pieces that don't share state.

Smalltalk collections have map reduce, and it is as OOP as it gets.

Parallelize smalltalk collections across multiple machines without a distributed coordinator (reduce). Even the pedantic original version of smalltalk OOP has this problem.

map-reduce and functions do not require coordination until reduction. map doesn't require a serialized mailbox (like actors), or worrying about ordering in time. Smalltalk requires a consistent state and the location of where to send messages is important. Distributed map is stateless. All versions of OOP concern themselves with state encapsulation, whereas map is state elimination. map is functional.

Smalltalk map reduce works across VM processes, there is nothing in CS literature that requires map reduce to be a cluster only algorithm.

I don't think that's been true for quite a while. Even a function like len('abc') is implemented as 'abc'.__len()__ under the hood so no different from Ruby really.

Well, the problem is that Rails (and ActiveSupport) is borderline idiomatic Ruby. I imagine the vast majority of Ruby codebases use them. And honestly I don't even know how vanilla Ruby stacks up to Python, it's almost an inconceivable idea (though I left Ruby behind some years ago.)

Rails is dominating Ruby less and less every year. There are plenty of alternative web frameworks to use, and Rails is a really heavy-weight thing to include if you're not building a website. I'll pull in an ActiveSupport module every once in awhile but so few of the things I write are websites that need Rails.

There's no evidence for this in the job market. Most Ruby jobs are Rails jobs.

Python isn't less expressive for the most part except for lack of blocks(although can be emulated with decorators and def _), and ruby's strategic lack of parentheses and not being to add/change methods in c based classes.

Python has just as much access to reflection, it has more powerful and arguably easier to shoot yourself in the foot inheritance (python has multiple inheritance while ruby only has mixins). Python also has method_missing in __getattr__. Arguably python tends to use these less often then ruby but I've definitely used both reflection and __getattr__. I've also once had to debug a really hairy multiple inheritance issue(in a test framework that used base classes to test different hardware features where you might want to have both you'd need to use multiple inheritance and then make sure they were initialized in the right order, multiple inheritance can get really annoying)

> On a practical note, Ruby's heavy use of reflection, inheritance and method_missing style dispatch currently makes performance for big data tasks less than ideal. Python is less expressive but its "one true way" makes things like vectorization and type specialization easier for data tasks.

FWIW the things you mention are available in Python. There are things Python doesn't have like singleton objects but I'd say those are syntactic sugar & conveniences (not unlike classes existing from the start of the `class` statement) rather than a lower level of expressivity.

Python absolutely does have extensive reflection, inheritance, and "method_missing-style dispatch" (cf `__getattribute__` and `__getattr__`), and so has to accommodate the possibility of them being used… unless it doesn't want to which specialised Ruby implementations (à la numba and friends) could certainly do as well.

I've found Ruby to be far more "learnable", in that once I learn how something works in Ruby, it will work that way in most every situation going forward, whereas Python has a lot of caveats to its functionality. This might just be due to my use cases, but the Ruby documentation is 100x more useful for getting me from point A to point B than anything I've seen from Python's docs.

I still use Python daily, since its so ubiquitous, I just find myself having a difficult time using it for anything remotely complicated without a lot of extra research.

> my rule is "if you're going to even think about threads, just use Java or go" since concurrency is so nice there.

I could see reasons for preferring Java over Ruby but concurrency isn't one of them considering that Concurrent-Ruby running on JRuby gives you access to all of Java's concurrency tooling.[1]

1. https://github.com/ruby-concurrency/concurrent-ruby

you can still go with jruby and have best of both worlds.

I've tried on a project, but incompatibility with some libraries (gems) made it impossible. I guess it's very much like PyPy.

I think parallelism is just not part of the Ruby ecosystem anyway, at least in the present (possibly in the future, who knows).

Rails released officially support for multithreading long ago, but it was broken. And there is a Ruby global lock, anyway (3.0 will have true parallelism, although in alpha).

Well the parallelism issues doesn't exist in the jruby world, but yes, gems with C extensions normally don't work out of the box with jruby, but in another hand you can call java straight from ruby. That's so powerful and often better than the gems - if you know how to right ruby and wrap the java classes with ruby! So as many times said along this thread, ruby isn't rails. I have a project running in a government agency with jruby. It was a port of an old Java SWT application and we rewrote it in jrubyFX. The project was such a pleasure!

Or bake it into a task graph and use the gmake executor.

It is a pity the type hinting is done in a separate file though, as it leads to a lot of duplication. In this case, I pretty much prefer Python’s approach.

You can still use the inline type hinting from Sorbet. Sorbet is compatible with RBS and you'll probably want to check types via Sorbet anyway.

FWIW Python does also support type hinting in separate files, although the use case is mostly in adding annotations to existing code bases.

> "if you're going to even think about threads, just use Java or go" since concurrency is so nice there.

Concurrency in Java is not very great although there are some improvements in these years. The actor model (Ruby 3 / Erlang) is on par with CSP model (Go) though they have different trade-off and scenarios.

I'm really really glad it didn't. Once you get into looking at advanced ruby with all the implicit eccentricities it starts looking a whole lot like the Perl of old.

Ruby should be one VM per CPU with message passing like I did for the Blue Gene/L port. Threading can't scale between cgroups let alone kernels.

It's a mish-mash language like perl and should die the same death.

Ruby might have some qualities but overall it's less polished than python

Just as a small example, Edit: .methods, "the Ruby equivalent of 'dir'" doesn't present the results as a sorted list

Also the optionality of parentheses sounds like a good idea but it actually makes things more confusing and less parseable

I would argue that is a good thing since it allows for performant access when you do not care about order. If you do not care about order you do usually not even have to allocate an array which means lower memory use. And how hard is it to write.

Ruby lacks polish in certain areas but this is not one. A d I would argue Python can be pretty tough in places too.

Not that Dir, I mean as in .methods/.instance_methods (or dir(instance) in python )

I fixed it in my original comment

Then I understand your complaint even less. I almost never sort the output from methods and I do not think I have ever sorted the output from instance_methods during my over 10 years as a Ruby developer.

Python orders it. And when you want to look up a method by name you do a binary search by fact of being a sorted list

Anyway you can just .sort

> .methods, "the Ruby equivalent of 'dir'" doesn't present the results as a sorted list

This seems like an odd complaint: Both languages use hash maps to look up methods/attributes, which aren't inherently sorted. Looking at the implementation `dir` in python does an explicit sort, which just seems like arbitrary behaviour.

It's not an arbitrary behaviour if you want a well behaved result from it. Same with Python dictionary keys which used to have no intrinsic ordering.

Especially when dir() is more used in interactive applications (and sure, sometimes programatically like in plugin architectures, etc)

Because Ruby uses fairly deep metaprogramming much more, idiomatically, than does Python, while “dir() is more used in interactive applications” may be true, that does not imply that “#methods is more used in interactive applications”. And, anyhow, an interactive application that wants the information it asks for will quite often wan’t to impose an organization other than alphabetic ordering.

First pattern matching, and now type-hinting and message-passing actors. Is the goal of Ruby right now to claw back all the people who moved to Elixir by adding Erlang features to Ruby?

Not to say I’m not grateful! I’m mostly an Elixir dev, but since the Erlang Runtime System sucks at POSIX (bad at pipelines, bad at exit codes, bad at trapping signals, etc.) I personally switch to Ruby when I need a glue language. It’s nice to see the available paradigms in these (actually very different) languages “coming together”, in a way where I can spend less time mentally mode-switching, and just keep thinking in (functional, pattern-matching, concurrent-actor) terms.

Just think how pleasant it will be to write a “show a spinner as you poll a resource in the background” code in Ruby, now that we’ve got actors. :)

> Is the goal of Ruby right now to claw back all the people who moved to Elixir by adding Erlang features to Ruby?

One has to wonder.

I don't think I'll go back. I found that I was coding almost exclusively in a functional style, in Ruby (because it was easier code to test and it seemed to produce less bugs/fewer mental-model problems), then realized I was still missing out on the guarantees that Elixir gives you, then I thought, what's the point in staying when I could get guarantees that eliminated entire classes of bugs for free?

Having done Elixir for a few years now, I RARELY have to go into "debugging mode", I've never even had to fire up Elixir's version of "pry", and my tests have caught all but a few bugs. All my logic lives in sensible locations. It's pretty nice! At least for web dev.

I had the same epiphany; the hard-won "best practices" I had discovered in Ruby/Rails ended up looking a lot like second-rate functional programming.

Having spent the last year with Elixir in production, I don't plan to go back. Things like Ecto, ExUnit, error handling using "with", Phoenix Presence, domain modeling using Phoenix Contexts, Absinthe for GraphQL have made programming a joy.

I love that phrasing: “hard-won best-practices in Ruby turn out to be merely second-rate functional programming”

That, plus Elixir was literally 5 to 10x faster at a sampling of tasks I threw at both (but this was a few years ago)

I see a lot of Elixir lovers but I just can't wrap my head around a lot of the idioms. Is it bad I much prefer loops to reduce? Also modelling complex domains things just flow better in my brain with OOP, whenever I go to draw things I put them in boxes with as little state as I can.

I would love love love to see two people approach the same exact problem with OOP and FP and see the rewards of both.

However I really love pattern matching from Elixir real game changer of a idea, excited to see it come to Java and Ruby soon and the lightweight threading models come to both of those languages also.

I always preferred Brian Gotez's argument that you should be functional within objects. https://www.youtube.com/watch?v=8GWZE2Y2O9E

> Is it bad I much prefer loops to reduce?

No. That's the reason that we have multiple different tools at our disposal. :)

My rule of thumb has always been to use the tool that I, myself, find more effective—not what others say would be the most effective in an ideal world where I know everything about every programming language in existence. That means that I've written a handful of shell-scripts in PHP, some CLI-tools in Ruby (that would probably run a lot faster in Go), and a couple of "API"s in Bash.

Even in Ruby, no one uses loops, they use Enumerable. Eliminates off-by-1 errors!

Reduce (inject, etc.) are tricky to understand at first but once you do, you realize the power in it. Granted, it’s harder to understand than loops, but loops with iteration and mutability get you into a lot more trouble.

It sounds like you’re simply not over the learning curve yet.

> Even in Ruby, no one uses loops, they use Enumerable.

Enumerable#each is quite deeply equivalent to a for…in loop.

But no one uses for loops in Ruby because not only are they basically an alternate syntax for Enumerable#each, but they became non-idiomatic because in earlier versions (through at least 1.8.x, IIRC; I think this may have been resolved by the switch to YARV in 1.9) “for” had some implementation inefficiencies which made it notably slower than #each.

> Is it bad I much prefer loops to reduce?

It…depends what for.

There are things loops are more clear representations of intent for, there are things map is a more clear representation of intent for, and there are things reduce is a more clear representation of intent for.

FWIW I'm balls-deep into Elixir, pardon my language, and I almost never write recursive functions.

Do you have any tips for people coming from Ruby land on how to model things with a more functional approach? For example I've been watching some of 'On Writing Software Well' series https://www.youtube.com/watch?v=D7zUOtlpUPw by DHH and found it really useful for OOP land.

Also any idea on how StimulusReflex / new Hotwire compares to Phoenix LiveView if you have had a chance to look at it yet?

Reduce is just a loop that's already been written for you!

Much as I'd like to switch to Elixir I see hardly any contracts calling for it right now. i.e. for every 300 or so Ruby gigs there'll be about 2 or 3 Elixir ones.

Ruby will never be Elixir or Erlang but if you can improve an existing thing why wouldn't you? Adding useful features from Elixir/Erlang to Ruby seems worthwhile, even if it doesn't get you all the benefits.

There will likely to be much work that won't be rewritten from scratch that would benefit and money to be made doing so.

In case someone is interested in the (JIT) performance of 3.0 RC 1 compared to version 2.7.2, here are some measurement results based on the are-we-fast-yet benchmark suite: http://software.rochus-keller.ch/are-we-fast-yet_crystal_rub... and http://software.rochus-keller.ch/are-we-fast-yet_crystal_rub....

I see only a slight speedup of the 3.0 (JIT) compared to 2.7 (less than 15%).

See here for more information about the benchmark suite: https://stefan-marr.de/papers/dls-marr-et-al-cross-language-... and https://github.com/smarr/are-we-fast-yet. In contrast to other benchmarks the focus is on ideomatic language use and representative results (i.e. not just some random micro benchmarks) suited for inter-language comparisons.

Meanwhile I was able to run the benchmark on MRI Ruby 2.1.0 and compare with 3.0.0 RC1; here are the results: http://software.rochus-keller.ch/are-we-fast-yet_crystal_rub...

Ruby 3.0 RC1 without JIT is factor 1.2 faster than Ruby 2.1.0; Ruby 3.0 with JIT is factor 1.4 faster than Ruby 2.1.0. So definitely less than factor 3.

Unfortunately the benchmark didn't work with Ruby 2.0.

15% is a decent speedup.

When implementing a JIT I would hope for a much higher speed up than 15%. In comparison: LuaJIT with the JIT switched on runs about five times faster than the most recent PUC Lua interpreter, see http://software.rochus-keller.ch/are-we-fast-yet_lua_results.... Compared to that 15% is nothing.

The JIT efforts for Elixir/Erlang apparently offer 30% speed up, worst case, and while I'm way out of my depth here, apparently it's a relatively 'naive' implementation. I'm very curious why there isn't as much of an improvement for Ruby. Is it something to do with the extreme dynamic nature of the language?

> Is it something to do with the extreme dynamic nature of the language?

Smalltalk/SOM, JavaScript and Lua share the "extreme dynamic nature" but still have much faster JIT implementations. From my reports you can see that V8 (Node) is about ten times and LuaJIT about five times faster than MRI Ruby 3 JIT. Here are some other performance results using the same benchmark suite: https://github.com/smarr/are-we-fast-yet/blob/master/docs/pe.... Also TruffleRuby is reported to perform much better than MRI Ruby JIT (see e.g. https://pragtob.wordpress.com/2020/08/24/the-great-rubykon-b...).

True, but I would imagine that a lot more time has been invested in LuaJIT, since this is just the first release of the Ruby JIT. Plus isn't Lua better suited for optimization, from the get-go?

I have no information on how much time was invested in the implementation of either of the VMs. Lua has many similarities both with Smalltalk and Ruby. It has also a very powerful closure concept, which unfortunately is not yet supported by the tracing JIT.

What I got from this is how fast Crystal is :).

If you want to split the difference there's compiled mruby.


Crystal is a very interesting technology indeed and with my 2020-12-10 and 2020-12-15 measurements I was mainly interested how fast the compiled binary and the compiler work in comparison. With the 2020-12-20 measurement though I was interested whether MRI Ruby 3.0 was indeed significantly faster than 2.7 which doesn't seem to be the case. It's also interesting to note that even if MRI Ruby was tree times faster it still would be half as fast as LuaJIT (considering that LuaJIT would be even faster if the tracing JIT would also support closures).

Anecdotally, since I got away from Ruby, working on software became way more productive and tolerable.

Maybe it was something with the corner of the Ruby world I fell into, but the syntax sugar options and “cleverness” that enabled was maddening to deal with, and permeated that crowd.

Code bases with mixed styles are a thing in any language, but the Ruby-ists around me found a way to make one project look like half a dozen languages were involved.

Insert Confused Jackie Chan meme.

Interesting, I'd say the opposite. In a Rails app that was written consistently with the styles and APIs documented in the Rails docs I have never been more productive. In other micro-framework type environments, regardless of the language, Ruby/Sinatra, Python/Flask, Javascript/Express. Most codebases I've encountered are a mess of inconsistencies introduced by various developers over the years. With a lot of the mess, effectively re-writing the core features of Rails and it's most popular gems.

Rails, if done correctly, is a remarkable tool to allow developers to focus on the business logic and UI of their app and forget about the "filler features" that pretty much every app needs implemented. On the other hand if you write Rails code without reading the docs and re-invent the wheel rather than doing things the Rails way, essentially running an app in the framework without using the framework methods, you can create yourself a monstrous unmaintainable codebase like no other.

Rails is not vanilla Ruby. And web apps are not the only software that needs to be written.

I moved on from Ruby land right as AWS was blowing up, and admitted I have never worked in Rails.

You're correct, Rails is not Ruby. But in my experience 80%+ of the Ruby ecosystem is Rails. But now I'm even more curious what problems you encountered with Ruby because I find it to be an excellent scripting language, especially if performance is not a major concern.

To be fair, it’s probably not Ruby, but the clever linguists I was working with 2008-2012 era.

Everyone brought their preferred syntax and there was much less of a “let’s solve the problem, not be clever” sentiment back then.

That said, Python almost makes such things impossible. Decorator patterns are the only thing I can think of in Python that ever made me really think.

6 to one, half dozen to another. How we start out thinking about problems has an impact on what syntax works for us best later on. I’m in my 40s and started in electronics, designing boards that shipped in Nortel kit.

I started with C and little else, not digging into OOP until hardware work went overseas. Perhaps my brain is over specialized to prefer a particular way of visualizing code I need to write.

Could you please stop creating accounts for every few comments you post? We ban accounts that do that. This is in the site guidelines: https://news.ycombinator.com/newsguidelines.html.

You needn't use your real name, of course, but for HN to be a community, users need some identity for other users to relate to. Otherwise we may as well have no usernames and no community, and that would be a different kind of forum. https://hn.algolia.com/?sort=byDate&dateRange=all&type=comme...

Rails is great if out of the box with devise + active admin it gets you at least 80% of the way to what you're trying to build. Once you start writing loads of background jobs and business logic it becomes a quagmire pretty quick. I've seen this happen over and over again on projects I've worked on.

I mention Rails specifically because I can't think of any other reason I'd use Ruby these days.

> Anecdotally, since I got away from Ruby, working on software became way more productive and tolerable.

Oddly enough I found myself becoming less productive when I tried Elixir and Phoenix for a bit (compared to Ruby).

You could look at code written by 10 people and end up with 10 drastically different looking code bases. Not just the organization of files but the logic behind the functions. It makes it very hard to read other people's code unless you know every pattern they use very well.

Here's an example of that where I wrote something one day, asked if there's a better way to do it and someone else wrote their own version[0]. Take a look at the 2nd code snippet vs the 3rd. The first code snippet happens to be Python btw. Even after a year of casually working with Elixir I can't understand the 3rd snippet of code at a glance. When I first tried to parse it mentally I spent literally an hour picking it apart just to see what everything did and I forgot almost all of it a week later.

I also found myself much less productive mainly because the language tends to be quite verbose compared to Ruby, especially when you compare things like ActiveRecord vs Ecto. Ecto is one of those tools where it sounds unbelievably good on paper with great abstraction ideas but in practice it's pretty complicated because there's tons of different syntactical ways to do the same thing, the docs aren't inconsistent and every blog post you read has people implementing things in a different way. Then you combine that with it being fairly low level and you end up having to write huge amounts of very tricky code to do the equivalent of what you can do in AR.

At least with Rails, most projects look the same and unless someone dives off the deep end with meta programming, you can generally follow the code. I guess this is a testament to how good Rails is when it comes to developing its APIs. You can go such a long ways and develop fully featured apps based on Rails without really having to do anything crazy.

[0]: https://nickjanetakis.com/blog/formatting-seconds-into-hh-mm...

I've done Rails full time for around six years on two different teams. A big chunk of that time involved working on a codebase whose size rivaled some of the largest in the world.

Rubocop (with some tweaked rules; it's IMO too restrictive out of the box) was a big lifesaver in terms of steering both teams toward a common, readable coding style and mostly eliminating debates over style at PR review time.

> since I got away from Ruby, working on software became way more productive and tolerable.

Did you have a look at https://crystal-lang.org/?

I agree 100%. Ruby is my favorite language but for anything that isn't purely a solo project I've moved to harder-to-misuse tools for exactly this reason.

Using RuboCop should help a lot having an homogeneous codebase.

Love the move to gemify standard library. This is a perfect mix of batteries included but also makes it easy to upgrade bits of the standard library over time between releases. Great way to prevent stagnation.

prevent stagnation

Or irritate a long term user base? PHP has been very successful long term, largely I would argue because of maintaining almost complete backwards compatibility.

Preventing stagnation and maintaining backwards compatibility aren’t the same thing. Go has a very strong backwards compatibility guarantee, and since they made the guarantee the entire compiler has been rewritten and there have been plenty of additions to stdlib.

Yes, it's possible just not usual to replace everything without effecting interfaces.

but gems in stdlib work fine even for that: if the next version of the gem breaks compatibility the next version of ruby is free to import it, and you can keep using the old interface with new ruby by locking the dependency to the old gem version.

> Or irritate a long term user base?


> PHP has been very successful long term, largely I would argue because of maintaining almost complete backwards compatibility.

Gemification of the standard library has very little impact on backwards compatibility; moving out of stdlib into default gems. Moving to bundled gems is transparent to code but has to be taken account of in build infra, moving out past bundled gems is where it would become visible to consuming code, equivalent to being evicted from stdlib before gemification.

I've been writing Python for close to a decade off and on. I'm just now dipping my toes into Ruby and have found it quite fun- excited to dig deeper into this and Rails :D

Be sure to check out Jeremy Evans on GitHub and his whole suite of tools: Rosa, Sequel, etc.

Rails is good, but the ecosystem beyond Rails is better in many cases.

Ah sequel. I did a stint of ruby on Rails work many years back, and remember seeing sequel for the first time. It's documentation web page is honestly probably the best "what is this" and "getting started" page I have ever seen for any library.

I don't know if it's the library itself or the documentation contents or the documentation website theme, but it jives absurdly well for me. I also really like the simplicity+flexibility of the library itself, and have yet to find anything even close to it in the c# or c++ world.

Does some of it feel kind of redundant? I suppose that people have different reasons for learning languages but as someone who knows Python, Ruby is one of the languages on my not-to-learn list since it would neither allow me to work on anything that I can't work on now or introduce some new and interesting language paradigm.

> as someone who knows Python, Ruby is one of the languages on my not-to-learn list since it would neither allow me to work on anything that I can't work on now or introduce some new and interesting language paradigm.

Python is a pervasively procedural (to the extent that it likes hiding OO features behind procedural interfaces), statement-oriented programming language with multiple-inheritance style OOP, and a structural aversion to functional idioms.

Ruby is a pervasively OO, expression-oriented language, with single-inheritance + mixin OOP, and an affinity for functional idioms expressed through fluent OOP APIs.

Ruby is very much not redundant with Python.

Meh, as someone with quite a bit of experience with both of them they are very similar, you don't learn much from switching from one to the other, compared to switching to something like C/Haskell/Kotlin.

Learning languages which are reasonably redundant is much faster, because there's a lot less to learn - mostly syntax, and keywords.

So, yeah, although there's less gain, there's less cost, and you might come across patterns you can transfer.

Ruby just never clicked with me, and I feel like if I am going to use something other than a scripting language, like python, it should be something that has insane native performance.

I really dislike this:

  {b: 0, c: 1} => {b:}
  p b #=> 0
It is so weird that a variable named `b` is initialized after an hash-key-like symbol is referenced (`b:`). It's something you've never seen in Ruby before and that I hope I'll never encounter along my career as Ruby on Rails developer.

It's becoming quite common in JavaScript, but usually with a const/let keyword in front so it's easier for the brain to understand that you are reading a destructuring assignment.

I believe you are misunderstanding how this new feature works in Ruby. You've brought up destructuring assignment in javascript which flows right-side values by key to left-side variables. In Ruby, this new syntax is "rightward" assignment, values flowing from left to right:

    3 => x # assigns 3 to x
    [4, 5] => [x, y] # assigns 4 to x, 5 to y

Indeed. It's very different. Thanks for the clarification.

Exactly that, I use it all the time in JS. I can't see why Ruby can't adopt something like:

  params = { a: 1, b: 2 }
  { a: a } = params
  p a #=> 1

Does anyone know the reasoning for adding this? The link doesn't discuss that. As someone who worked primarily in Ruby ~5 years ago, it just seems like useless complexity to me.

RC1 is a little bit late for a Christmas Day release (which is a long-running Ruby tradition). I wonder if they’ll make it, and if so, if 3.0.0 will be less stable than usual.

Anecdata: I’ve been running both previews daily (when I could, some gems need updating), and they seemed quite stable to me already, moreso than some other previews or even RCs.

Consider Shopify and Github both have been testing on 3.0 and Rails Master.

I say it is pretty good.

Could someone familiar with the Fiber/Async thing explain how does this differ from Python's (or Node's)? How come Python (and Node) needs explicit awaits but Ruby seems to "just work"?

From an implementation perspective this has much more in common with Go than either Python or Node.

In Go, you simply write a blocking IO call, and the scheduler in the Go runtime pauses your current lightweight thread ("parks the go routine" in their terminology) until the IO operation has some results to read. While waiting, the runtime is free to run any other go routine(s) on any number of OS threads.

What is coming to Ruby is a similar system, but where a library can provide this scheduler functionality. So when a lightweight thread (a "Fiber" in Rubys terms) calls a blocking operation, the runtime is notified, and can manage the IO operation while running other fibers.

As another commenter mentioned, there was a similar thing named gevent implemented for Python some years ago, but it didn't become "the way" to do things. I feel there are three main reasons it didn't catch on:

1. A lot of Python programs had blocking IO being performed inside native extensions (e.g. bindings to C/C++ database drivers). If you depended on such a library, the benefits of gevent were severely limited because it's runtime couldn't intercept the IO operations.

2. It was never part of the stdlib, so there was not a huge amount of incentive for the aforementioned driver authors to support it explicitly.

3. There were not that many examples of successful implementation of this model at the time. Go was around but pre 1.0 and not nearly as popular as it is now. Lua was also around, but again, not extremely well known.

I expect that Ruby will also suffer a bit from #1, but hopefully, by providing this concept as part of the language and standard library, problem #2 will be mitigated. #3 is IMO a thing of the past, while I'm not a huge Go fan, it does serve as a compelling argument for an implementation of runtime-managed lightweight threads.

Edit: I should mention I'm in no way involved in this, just interested in what will come of it over the next few years.

So the Ruby runtime has some kind of internal list of operations that are blocking, and can optionally be executed asynchronously when called from inside a fiber?

Well, it's not a list. It's just a set of hooks. But that's basically it.

Fibers are more like Python green threads, or generators when you abuse them to do coroutines, where you explicitly yield from one to the other. The thing is, if your Fiber calls a blocking IO operation you can’t explicitly yield to another Fiber unless IO unblocks. The scheduler solution attempts to yield to another Fiber when IO blocks.

Ractors resemble Node workers: you get true parallelism, but the two worlds are entirely isolated, sharing nothing. You communicate by passing messages between both, where message content is either copied or moved (ownership change) if it’s not immutable.

(What I described is not 100% accurate, more of a flawed bird’s eye analogy for Ruby foreigners)

Python Zen: Explicit is better than implicit

There's a python library gevent which experimented with a more implicit design. Or more, this is about green threads vs async/await

It can also be a bit confusing when you don't allow separating await from the call: how do you implement Promise.all?

For official rationale see https://www.python.org/dev/peps/pep-0492/#why-async-and-awai...

async/await is a huge anti-pattern IMHO.

Not an implementer but I would say those are just blocking method calls, like Thread.join. No magic happening.

There now is:

- Fibers

- Also fibers, but scheduled

- Threads (Not what you'd normally calla thread, because GVL)

- Ractors (what you'd normally call a thread)

I wouldn't say this is a bad thing per se, but I imagine it will be quite confusing for people trying to learn the language.

I look at Ruby and I think of Dart.

Not from language semantics perspective..but specialisation in a very narrow usecase and around DX. But really unbeatable in that usecase.

all racehorses are one-trick really.

I feel sort of similar for most languages,

low level? Rust/C/C++

'corporate' work? Java/C#

Android? Kotlin

iOS? Swift

ML / Data statistics? Python

Which is why the best option is to be polyglot and not silo as "X Developer".

I hadn't been following the proposals for and development of Ruby 3 very closely, so Ractors were a nice surprise when I finally did check in.

Well done to all contributors, looking forward to giving it a spin and learning about the additions. Has anyone had experience using RBS yet?

This seemed sparse on JIT updates, I read the linked NEWS page and while it had some feature updates, I'm curious how this is going. I believe JIT went from "likely to speed up most applications significantly" to "may sometimes speed things up, default is off and needs more work."

Anyone know the status of using JIT in something like a rails app?

I did some measurements based on the Are-we-fast-yet benchmark suite, see my post https://news.ycombinator.com/item?id=25495450

Awesome early Christmas surprise!

Just to clarify for those who might not get it, a new major-ish version of Ruby releases every Christmas.


I tried Ruby over 10 years ago and while I really enjoyed the language itself (vastly more than, say, Python for instance) I was completely put off by the insanely poor performance of the interpreter. I remember trying to get it to run on a rather slow FreeBSD box back then and just loading a simple script with a few dependencies would take literally seconds! Perl and Python were easily an order of magnitude faster.

Have things improved on that side?

Yes, it's on Python level. There is also JRuby and TruffleRuby that pushes the limit further.


Applications are open for YC Winter 2024

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact