In essence, I don't think it has anything to do with elegance, syntax, or "easiness". It's more about timing + origin + corporate adoption:
* Python was created in Europe, Ruby was created in Japan - when Ruby gained more recognition in English-speaking world (~2004), Python has already been present and used worldwide for a decade
* Ruby was created as a personal, hobby project; Python was created while Guido worked in research institutions, and it helped Python gain recognition in academia environment
* Guido joined Google in 2005, boosting the adoption of Python; Ruby has never gained such a large corporate backing
Eventually I believe it was the combination of the latter 2 that pushed Python to top spot in popularity - scientists all over the world were familiar with Python, and with the rise of Data Science/ML (with Google being one of the main forces behind it), it became the most obvious choice for the new tools.
As a data point, I remember interviewing for an internship in a research institution doing some work on medical images around 2002/2003 and the supervisor telling me that Python was the lingua franca in this domain (from a research and POC development perspective).
Trainings for getting Python skills were a common offering.
GvR’s background probably helped spread the word and thus get feedback, but as often Python’s overnight success was the result of years of work.
So we got at least two major contributions from the CWI. One is Python and the other one is the game of Hack. :)
The father of A*:
https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
ALGOL 60 and ALGOL 68
First node on the Internet outside of the USA
Python
Various contributions to the improvement of encryption.
CWI has a long, long history of quietly contributing to all kinds of stuff the world over and has a pretty good reputation.
Or at least they are similar enough that one pulled ahead and subsumed the other. Like a twin eating his brother in the womb. Maybe it could have gone the other way and we would have ended up pretty much in the same place. Or maybe it is a case of worse is better. But not that much worse/better frankly and here we are. Salivating over rust. ;)
I agree with this point. I learned python years before I learned Ruby and I found them to be very similar. Ultimately I dropped ruby because I didn't care much for the syntax and I like whitespace.
Ruby got popular coz Rails could get barely code-literate graduate to write CRUD app and so it was cheap (famous 15 minutes to blog tutorial) for companies.
JS got popular coz, well you ain't got fucking choice on frontend and having your then-frontend JS/HTML/CSS designer be able to also do backend is, well, again, easy and cheap.
There is clear pattern here...
"Ruby got popular coz Rails could get barely code-literate graduate to write CRUD app and so it was cheap (famous 15 minutes to blog tutorial) for companies."
That's not what happened, instead Ruby got popular with advanced web developers working in Java who saw that Rails was had better answers than J2EE. If you were barely code-literate graduate you wouldn't recognize that Rails was offering new unique ways doing things.
The 15 minute blog tutorial wasn't really indicative of what Rails was like, just a small sample about how it was different.
It was an advanced tool for advanced users with a long learning curve that eventually paid off in better productivity.
I was not "advanced" at web development in 2006, but this is exactly what got me to try Ruby. Previously I had been reasonably happy with JSP and Java Servlets, but this Rails thing promised a better way. And it turned out for most of my needs, it was much better.
Also, the Rails Depot book was excellent, even back in the 1.x days (IIRC). It covered enough that you could build a fully working app with sessions, auth, crud, and reasonably abstracted layouts/views. Oh, and because Ruby was "simple", you honestly could start with that Rails book without knowing Ruby.
We did Rails one decade earlier with our own version of AOLServer.
The very reason we went to .NET (as beta testers during its availability only to MSFT partners) was because we were fed up having to rewrite TCL into C due to performance issues.
All the Active Record stuff was already something that AOLServer introduced, and we had an improved approach on our product.
The founders of that company went on to create OutSystems based on our learnings.
IIRC, a bigger reason for Node's popularity (at least at the very beginning of its popularity) was the fact that it had event-based I/O, which let a single process handle a lot more in-flight requests than Ruby (where the concurrency model was to spin up a bunch of processes and multiplex across them with nginx or similar).
"Why Threads Are A Bad Idea (for most purposes)"
I work on multithreaded software and I get 31,516,776 synchronizations per second with a lock free algorithm and 31,565,052 on the lock benchmark.
When the threads are increased to 100 (contention) the lock benchmark does 3,419,271 requests per second whereas the lock free algorithm does 19,926,428 so the contention hurts them both but the lock free benchmark scales far better.
Threads aren't bad. The domain is not easy.
In terms of pre installed options, especially on older systems, Python is the best choice for integrating and extending systems.
Google used Python very early, but other languages became more prominent in 2005, culminating in the creation of Go, after which GvR left.
Indeed people talked a lot about Google "supporting" Python, but the support was pretty minimal (50% job for GvR, where you cannot really do much).
Python has way better marketing though the PSF though. They understood that you can create a circus around the language that is completely removed from engineering, fundamentals and actual core development. If Ruby had been as shameless, it might be #1 today.
Perl was everywhere (on Linux/Unix). Then Python came along an offered enough net benefit over Perl that it really started to displace it, and became the defacto scripting language that you would reach for when creating programs on Linux/Unix.
Ruby has some niceties over python, but doesn't provide enough of a net benefit over it for that same kind of displacement to happen.
This, plus what other commentors have noted regarding Pythons early developments in academia, have had multiplying effects on adoption.
The alternatives were C, C++, some "academic" language like Lisp (which I wish had really broken into commerce more), Prolog etc., and eventually Java. Python was at such a higher level that it definitely made more sense as a first language for many people... especially since it was capable enough to be more than just a first language.
Today’s landscape is better but also very different. And when looking up how to do certain things in Python, Stackoverflow will show you a multitude of options.
The problem for me is that I dislike most of them.
Agreed. It was a smaller language then, but it's never been as expressive as Ruby.
I never understood how this can be considered consistent at all, and those IMHO language design warts made me look into Ruby at the time.
Has this improved since? I know print was changed in Python3 to make it not-special.
Sure, multi line lambdas might be nice, but equally you can define functions wherever you need them so it’s not really all that different.
fwiw, Ruby does the same thing, but using the Enumerable mixin rather than a free floating function.
> I’d argue that is consistent.
I don’t understand this argument. It’s convenient and more efficient to write, yes. But how is it more consistent to have two different calling conventions?
The claim that there’s one way to do something irks me as well. There’s one way to find the length of something, but that introduces two ways of querying an object. It’s a good guiding principle, but when people use it to make stronger claims, it ends up superficial.
Python's len works by calling the __len__ method, which must be added to every class that needs a len function.
Since you're already defining a method with a standard name on every class that needs it, Ruby just has you call that method directly as opposed to the absurdity of intentionally obfuscating the method name with underscores so that people use a global helper function instead.
Even C++, with all its warts and its 1980s design got this right in the std lib with the size method.
Honestly, I find some of these defenses of Python quite humorous.
`sorted`, `list`, `set`, are more interesting cases where they all work with the underlying __iter__ protocol. You don't also want to add X.sorted(), X.as_list(), X.as_set() etc too. Again, you could have X.as_iterable() to implement these, or you could mixin sorted, which will call the __iter__ function. But honestly, it's really neither here nor there.
For the full avoidance of doubt - I'm not arguing for these other "memes" and, in particular, "There is only one way to do it" has never made that much sense to me.
I am arguing that Pythons `sorted` api is reasonable, consistent and not worth worrying about.
I don't think that's avoidable. Mistakes made early on have a habit of compounding over time and calcification makes it harder and harder to deal with them decisively and in a non-breaking way. Python made a couple of bad decisions but on the whole the language came out relatively unscathed, most of the original design constraints are still satisfied. As opposed to say PHP or Java which ended up very far removed from where they started out.
Case in point: Python's GIL must have seemed like a good idea at the time, a quick fix for an urgent problem. And now that quick fix is the albatross that we can't seem to get rid of.
I think it's definitely worth considering what could / can be done better, but people often argue deeply about things that aren't really a massive deal. You've pointed out examples that are much more interesting than "should __len__() have been called len()".
Certainly makes one appreciate Guido's stewardship in keeping the language fairly clean but applicable to large number of use cases for so long!
The justification for len() is thinner: Guido van Rossum thinks it looks better, and it enforces a consistent name with a consistent meaning (you don't get length methods with different names, or with the right name but strange behavior). Under the hood it just calls obj.__len__(), so it's only the notation that's not OOP.
There are no multi-line lambdas because nobody could come up with an indentation-based syntax for them that Guido van Rossum was happy with.
In short: none of them improved, all of them have reasons, some of those reasons are bad.
res = [x**2 for x in range(10) if x != 5]res = (1..10).select { |x| x != 5 }.map { |x| x ** 2 }
With filter_map:
res = (1..10).filter_map { |x| x ** 2 if x != 5 }
In both cases, I think the Ruby solution is more readable.
Python list comprehensions invert the subject (data) and the verb (action). You see what will be done before you see what the subject is. I would argue that showing the subject first allows easier code review as you know immediately what you are working with.
But beyond that, the first Ruby example tells you in English what is happening. "take this range", "select a subset", then "map some actions to the elements".
And the filter_map abbreviation does the same, telling you "take this range, filter it and perform an operation on the remaining elements".
Python tells you nothing... and what it does say is in awkward order.
As functional and data-oriented programming is gaining in popularity (for good reason), adopting some functional practices in Ruby is a pleasant experience. Doing the same in Python exposes more of these... irregularities.
Edit - I always forget how to format symbols in these comments!
Nope.
It uses what people already know: the for something in somethings syntax of the for loop, and the if syntax. Also it's nice that this works in dictionaries, generators and lists.
It also has the same narrative flow of Haskell's list comprehensions, which I think come from set theory:
[x^2 | x <- [0..10], x `mod` 5 /= 0]
As for your Ruby examples: I think you could argue that the filter_map version is very readable, but not necessarily more so, but the select one looks pretty painful.The select does two passes, which makes it quite inefficient. One does not even need filter_map, since the example is essentially a reduce operation.
res = (1..10).reduce([]) { |a, x| x != 5 ? a.push(x**2) : a }
This works in ruby 2.5.1. Probably works in 1.9 and mruby as well.I disagree and I’ve used both professionally for about the same amount of code.
I do a lot of contract work and chatted with a ton of folks ranging from beginners to veterans. A lot of them (well more than half) avoid list compressions, especially when working with teams because it's such a mixed bag of either being able to instantly understand them or it requires more effort. Personally I don't use them in my code (for both reasons).
Both Ruby solutions are much more clear to me even though I have no functional programming background. I have no preference towards functional styles either, I would say it's the opposite. I struggled with Elixir long enough that I stopped using it.
Coming from a point of zero knowledge of the codebase, I picked Flask. I picked the cli.py module in Flask. And what do I find?
https://github.com/pallets/flask/blob/main/src/flask/cli.py#...
https://github.com/pallets/flask/blob/main/src/flask/cli.py#...
https://github.com/pallets/flask/blob/main/src/flask/cli.py#...
https://github.com/pallets/flask/blob/main/src/flask/cli.py#...
https://github.com/pallets/flask/blob/main/src/flask/cli.py#...
https://github.com/pallets/flask/blob/main/src/flask/cli.py#...
https://github.com/pallets/flask/blob/main/src/flask/cli.py#...
https://github.com/pallets/flask/blob/main/src/flask/cli.py#...
The keyword "for" occurs twice as often in comprehensions and generator expressions as it does in "typical loop iterations!"
[x + y for x in range(10) for y in range(5)]I always have to stare at list comprehensions very closely to understand operation being done. The source of the data is in the middle, where it should logically come first. The filter is at the end, where logically it should come after the source. The mapping is at the start, where logically it should come at the end.
I find the monadic, additive style of Ruby much easier to understand:
10.times.select { |x| x != 5 }.map { |x| x ** 2 }
IMO it's more composable. What if you want to exclude even numbers from the result? Just add another filter: 10.times.select { |x| x != 5 }.map { |x| x ** 2 }.select { |x| x % 2 != 0 }
Incrementally building up a streaming computation this way is much more useful to me than a list comprehension.For example, you can add a lazy to the stream to avoid performing all the operations eagerly, and now you have a way to process sequences without blowing out your memory.
By the Python developers and its wider community. As Python doesn't have anonymous function blocks in the same way as Ruby (only lambda expressions), tutorials, lessons and the Python docs steer users toward list comprehensions instead.
I'm not saying the ruby syntax is not elegant (it is), I'm saying in Python list comprehensions are recommended over filter/map functions.
On the composable front, personally I prefer to breaks these down into smaller chunks with descriptive variable names rather than chaining.
Python also has the sister "generator" (() rather than []) syntax which also ensures it remains efficient as it pipelines the whole sequence of generators. (Lazily rather than eagerly as you say)
Once you start adding more "and" to the if-statement in the list comprehension, it becomes a mess. Breaking them down to smaller chunks is required because comprehensions are messy. You are doing smaller chunks due to a shortcoming of comprehensions. Chaining is nice option to have, especially when the chained functions are straight forward.
> By who?
> By the Python developers and its wider community.
> You're effectively saying something like: Canadians prefer Canada. What about the rest of the world?
They're actually saying that Python developers prefer one particular way of doing something rather than a different particular way of doing the same thing. You're suggesting that they're saying Python developers (Canadians) prefer Python (Canada).
I don't mean to speak against your broader points, just that this specific call out is mistaken.
Looking at it makes me miss Perl oneliners
I also think that map and filter style computations can be much more powerful, there are quite a few things other than just map and filter, like count, take, skip, find, flatten, fold, map-while and quite a few more!
In python I guess you are supposed to use a standard for loop to do these things instead.
The = binding is the chaining/composition tool of choice. This is why generator expressions are so important, relative to list and dict comprehensions. They both defer the allocation of space for intermediate values and allow the space to be bounded no matter what the input length is.
Often the “reduce” step, or even more commonly, realizing the side effects of such a generator composed of generators is a simple for loop—because that’s the most readable way to walk through it.
Python was just clear to read.
Ruby looked a bit cleverer but was not so simple. Coming from working on a large C++ code base, I decided Python was the one to do next. Simplicity, clarity and one obvious way to do things as a philosophy.
It's slower than Ruby, has worse package management and less consistent syntax but if you want to use Tensorflow, PyTorch, etc..., it's the default.
Django was released in 2005 and RoR in 2004. I suspect my journey was similar around then (late 2005), I had used PHP, classic ASP and a little Perl. I was looking for something better, read the tutorials for both Django and RoR. Had a look at what desktop GUI toolkits were available for both Ruby and Python (I wanted to lean one new language), Python had a few more options and so went with Python and Django.
At the same time Python was gaining significant traction in the scientific community with NumPy (2006/1995) and SciPy (2001). Ruby I don't believe had an answer to that back then. I was vaguely aware of that and it somewhat influenced my decision.
Ultimately it was the two pronged (web + scientific) development of the ecosystem that pushed Python ahead of Ruby.
As much as I liked RoR for its scaffolding and easiness of bootstrapping new CRUD projects, over time with larger codebases and teams it just starts to be a mess. Ruby programmers love the terseness of the language and write some of the most ungrokable code I've encountered (second only to some Scala codebases I worked with). That and the magic of autoloading modules which then could introduce a DSL, the usage of mixins that are hard to trace where they are loaded from, and a lot of other magic features that are extremely opaque to parse through and debug is, ultimately, what made me abandon Ruby and RoR.
It has its uses but I've yet to see a decent legacy RoR codebase without these pitfalls of magic, after so many years professionally working with code I prefer any language environment that is explicit, Ruby is not explicit.
Perl!
I think Silicone Valley is, uh, another type of valley.
Any reference/numbers to back this up ? Just genuinely curious about this.
It doesn't really matter though, both languages are used typically for scripting. But just shows it's not really about the "language", it's about the ecosystem.
https://github.com/kostya/benchmarks
Bearing in mind these are probably not even using YJIT, which makes Ruby considerably faster in some scenarios.
When Ruby and Rails started to make an appearance in European circles, around 2007, it hardly mattered when Python was already around.
From my perspective, python was just starting to gain real popularity in the mid 2000s, more than half a decade after Google was founded...
I first started using Python slightly after that time and for years it was a pretty niche language that relatively few people used, especially compared to the other 'P' languages, Perl and PHP. I'd estimate 2001-2002 as when it would say it started to become a 'normal' language and I wouldn't say it really started taking off until around 2005ish.
So 4 years after Google.
Which made my point to the parent.
And apparently everyone's been living under a rock because Python didn't become truly huge until maybe 10 years ago.
When people decided what language to use, it was very much a "X uses this, maybe we should" kind of decision. Still is for the most part.
Zope was a Python project from the late 90s, and had quite a bit of attention and traction in the early cms/publishing days. That brought Python attention, and certainly gave it some mindshare in the late 90s.
Two years later the project had become an integral part of some critical business processes, and IT had to take it on. They decided to rewrite it in C#. I left the company at that point, and almost a decade later I got a call from a former coworker asking for help to fix the system. It turns out it was basically unchanged from when I left, and the address of the Exchange server that it relied on for auth had changed.
That’s like saying that Dodge Neon is Slower than Mazda Miata.
Both are bottom of the barrel compared to any compiled language.
Scripting languages and compiled languages are used for different things.
And you're right, both are slow as hell and speed doesn't really matter for scripting, but when all else is equal, it matters, a lil...
Also JS is still too slow for most things compiled languages are used for.
Which is actually a great analogy for Ruby vs Python!
I know. :^)
Both are shit compared to better cars, though.
sorted(arr)
arr.sort()
versus arr.sort
arr.sort!Someone new to Ruby will the WTF is the ! for?
Every Ruby tutorial explains that an exclamation mark means in-place, and this convention is consistent across Ruby libraries, unlike Python.
In the Python case, `list.sort()` operates on the object, so you can kind of expect it to do mutation (but there's no convention / guarantee). Though it only makes sense as a function that the list object itself implements.
`sorted` is a general function that gives you a sorted list from any iterable, and because working with iterables is such a common thing in software, Python has a bunch of standard functions like that. It's nice because you learn them once and they apply absolutely everywhere.
Ultimately you learn the behaviours of a language and they become second nature. I find both Ruby and Python very clean and easy to read.
the meaning is the same high level: it has side effects, being it a destructive update, an in place operation or throwing an exception.
> `sorted` is a general function that gives you a sorted list from any iterable
iterable in Python is a very loose concept though.
And it's not very consistent too
sorted("gheliabciou")
['a', 'b', 'c', 'e', 'g', 'h', 'i', 'i', 'l', 'o', 'u']
you give it a str it returns a list of charin Ruby it makes much more sense
"gheliabciou".chars.sort.join
"abceghiilou"
You take out the chars from the string, sort them and then join them together againbecause a string cannot be sorted, sorting a string makes no sense, you sort the chars composing it.
Also, Python could not do this for many years, while Ruby could no prob
sorted("gheliabciouß⌚⌨⌛🆔🉑🈶🈚🈸🈺🈷🆚🉐㊙㊗🈴")Edit: ok, some do and some don’t. 🈶 is allowed but :laugh_with_tears: isn’t. I guess there’s a specific list of emojis that are getting stripped.
In that way `sorted` _is_ totally consistent - it always takes an iterable and returns a list of the elements. If we can't agree on that then I don't think there's much of a discussion to be had here.
except Iterable is the wrong "interface"
in Python it is defined as
> An iterable is any Python object capable of returning its members one at a time, permitting it to be iterated over in a for-loop.
which is not always what you want for sorting
quicksort doesn't work one element at a time
so basically you have to extract all the elements before sorting them
what about infinite streams?
At least Ruby Enumerable mixins explicitly states that
> The Enumerable mixin provides collection classes with several traversal and searching methods, and with the ability to __sort__.
Another problem with sorted is that it accepts any iterable, true, returning a list of items, but it is not clear how you can tell if the object is an iterbale or not
>>> sorted(123)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
ok >>> isinstance(123, iterable)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'iterable' is not defined
mmmmmaybe it's because int is not iterable?
>>> isinstace("abc", iterable)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'iterable' is not defined
so the solutions are # fails for strings in Python 2
>>> try:
_ = iter(some_object)
except TypeError as te:
print('is not iterable')
or # if 2.6 >= Python < 3.3
from collections import Iterable
# if Python >= 3.3
from collections.abc import Iterable # <- personal note, abc stands for...? core maybe?
# no idea on how to do it in Python < 2.6
>>> isinstance("abc", Iterable)
True
not only it's very ugly, but it can also fail in mysterious ways. As per documentationChecking isinstance(obj, Iterable) detects classes that are registered as Iterable or that have an __iter__() method, but it does not detect classes that iterate with the __getitem__() method. The only reliable way to determine whether an object is iterable is to call iter(obj).
while in Ruby it's straightforward
[3] pry(main)> [1, 2].is_a? Enumerable
=> true
[4] pry(main)> "abc".is_a? Enumerable
=> false
[5] pry(main)> "abc".chars.is_a? Enumerable
=> trueHowever, the destructiveness semantic has been significantly overloaded over the time, so each Ruby (Rails) project probably interprets them in a different way, and it ends up having arbitrary semantics.
I'm used to it, but I personally think that for the reason above, it's not a good syntax/semantic to have in a language.
I wouldn't go so far to say it is arbitrary (unless someone uses it wrongly :) ).
As far as I know, a bang name only exists if a non-bang name exists as well. And the one with ! indicates an unexpected or comparatively dangerous outcome, such as a mutation, raising an exception, bypassing some normal check, etc.
This takes so little time to learn. Same with ?.
While ! is not always the same meaning, it exists when there is a non-bang version; and it means "watch out, this does some sh*t". Maybe it means mutating the source, or maybe it means "doesn't take the usual path", as with Active Record stuff (raising an exception if validations fail on save instead of just returning false).
And the ? is a godsend, as you can clearly indicate functions which return a boolean (or intentionally truthy) value. As naming functions is a chore, and one which can have significant impact on code readability later, the ! and ? have good value when used properly.
For example, Python's sorted() vs list.sort(). If you are new to Python, and you see sorted(list), you may wonder if that is telling you if the list is sorted or sorting the list. It's actually also very awkward, because most functions are named as their actions in present tense, not in past tense. The built-in Python function "sorted" really should be "sort".
In the list of Python built-in functions, you see present tense verbs like slice(), sum(), round(), etc. Those are inconsistent with sorted() and reversed(). When we see a past tense verb, it feels like a question. With Ruby, if it's a question, it ends with a ?.
BTW, why the hell is Python sorted() and reversed() not just called sort() and reverse()?
And while I'm ranting about inconsistencies, Python my_list.sort() mutates my_list, while sorted(my_list) doesn't. In Ruby, my_list.sort returns my_list sorted, without mutating. my_list.sort!. That is consistent and concise, and the bang warns you that it is destructive.
To me, the ! and ? syntax in Rugby is genius. It is a language wide self documenting tool to explain a dangerous function and a boolean, which make total sense.
One thing I think Ruby gets right is the naming convention for methods that modify the object they're called on (<obj>.<method>!) and callables in general that return a boolean (<callable>?).
My biggest gripe with Ruby is the class implementation. I find metaclasses a bit more difficult to understand in Ruby. I hate that I can't easy determine where a given method of an arbitrary object is defined.
I recall that it wasn't uncommon for popular libraries to "open up" a class that was an integral part of Rails and redefine methods, overriding the original implementation. I recall trying to figure out why the method I was calling wasn't returning the value I expected, even after reading the Rails source. Come to find out, some random library had overridden it. They'd matched the behavior of a previous Rails version, but the Rails behavior had since changed. I spent hours on that specific problem and still resent it to this day :P.
https://tenderlovemaking.com/2016/02/05/i-am-a-puts-debugger...
The relevant bit is under the heading:
> I’m calling a method, but I don’t know where it goes
def index
p method(:render).source_location
render params[:id]
end
It sounds like this is the answer to your monkey-patching woes. my_object.method(:my_method).source_location # returns path/to/file:[line number]Ruby code has no parenthese.
I don't know enough Python to say anything about how easy it is but Python existed for a decade or so before it became popular and that was after Ruby had became popular. That points to it not being popular because it is easy but for some other reason. My guess is BI and AI made Python popular.
[1] https://wbk.one/article/a463c360/the-ruby-tutorial-i-wish-i-...
Ruby more like a philosophy major designing an OOP language - getting done and enjoying getting there were the goals. I took to Ruby more because i never cared about the "engineering" part of languages - just using them as tools to get things done.
This argument is dead the second one stumbles upon the string interpolation/formatting situation. Or the list comprehensions, ew.
who='World'
#1 - string concatenation
'Hello, ' + who + '!'
#2 - % operator
'Hello, %s!' % who
#3 - format() method
'Hello, {}!'.format(who)
#4 - f-strings
f'Hello, {who}!' # In order
'Hello, {}!'.format(who)
# Numbered
'Hello, {0}!'.format(who)
# Named
'Hello, {name}!'.format(name = who)May be now, but for a long time when RoR first came out, many people pick python instead of Ruby because scalability. Maybe a lot has improved with Ruby, and with introduction of Crystal, that may not be an issue any more.
I’ve seen that statement multiple times, but in my experience it’s far from the truth. For every thing I ever had to look up in Python I often found more than one way of accomplishing the same task and all of them were (subjectively) uglier and less straightforward than Ruby.
> on "brevity" and "expressiveness", I don't really see them as an attribute that make a language "better". Sure for experienced developers
It’s an attribute including for inexperienced developers. Perhaps especially for them. Even for something as basic¹ as writing a file, one needs multiple lines of code and know about `with` or opening and closing the file². In Ruby: `File.write("/path/to/write.txt", "file contents")`. One line, immediately understandable.
> that doesn't necessarily translate to code that is ideal when working as part of a team, or code that is easy to follow when a new developer starts on an existing codebase.
Agreed. Though in Ruby I have found myself thinking “I wonder if this keyword will work” and it does exactly what I expected. With Python, the norm is frustration with the impossibility of doing the simplest of things like defining a constant.
¹ From the coder’s point of view, never mind the underlying implementation.
² Failing the goal of one and only one obvious way to do something.
Working with various Ruby-adjacent stuff for 10+ years and nothing about Ruby was even "fun" to work with, it was mix of "WTF" and "who designs language like that!".
Python is also not "easier" to start with, I'd put Ruby/JS/Python to be at pretty similar level there. No worrying much about types (till they bite you, inevitably, of course, and cause untold bugs), simple things are simple and actual proper (i.e. "not send it to some piece of C code to calculate) multithreading sucks donkey balls.
I work in Python now, and I often have to use 5-7 lines for what would have been 2-4 in Ruby. Because you have to clutter the Python with trivial details, not because the Ruby code is "magic".
> Ruby ... its combined attributes of brevity, expressive power, and feature consistency.
> [Python] lacks the elegance and consistency
First I would argue Python is very consistent in its design, it aims to only have one obvious way to do things and so it's easy to guess how apis will work. It also aims for explicitness, which makes code very easy to follow and reason about.
Secondly on "brevity" and "expressiveness", I don't really see them as an attribute that make a language "better". Sure for experienced developers who like a personal game of code golf (don't we all at times!) those are attributes can can make coding more "fun". But that doesn't necessarily translate to code that is ideal when working as part of a team, or code that is easy to follow when a new developer starts on an existing codebase.
Ultimately I think that's why Python has gained more traction than Ruby, it's an "easer" language to follow and start with. Ruby is probably a more fun and intellectually stimulating language to work with though.