What's better about Ruby than Python? - comp.lang.python
groups.google.com
groups.google.com
My two big take-away points:
1) It's very nearly a wash. Ruby and Python are so close together (beneath any superficial differences) that distinguishing is almost an exercise in futility.
2) The key thing about Ruby he doesn't like is its "TOTAL, unbridled "dynamicity", including the ability to "reopen" any existing class, including all built-in ones, and change its behavior at run-time." To M's way of thinking, this makes Ruby less "suited for use in large production applications" and more suited for "tinkering."
I don't buy the argument myself, but I will admit that when I first started learning about Ruby, everything I read went on and on about how cool it was that you could open everything up and play. But as I read more, I discovered that the community seems pretty heavily tilted against monkey-patching now. (I may be wrong about this, but it's my impression.) I don't think that means Ruby is for tinkering only, but it does suggest that Martelli's concern is legitimate.
He also offers some nice pasta recipes along the way. (So much so, that now I'm hungry.)
It's absolutely crucial to teams that internalize it's power and pitfalls.
As an aside, I worked on a very large Ruby app (built by about a dozen developers over the course of years) and then later worked on another very large Ruby app of similar size. One of the employees there remarked that it was the largest Rails app built. I think the people who write very large Ruby apps tend to be very quiet about it (no blogs, no open-source, less community participation, etc.) so that if you're one of the ones writing a very large Ruby app you think yours is the largest. :)
The more dynamic (powerful?) the language the more leverage good, experienced developers have. They can achieve results in 10 lines of code that are hard/impossible in more static languages (closures, meta-classes etc.). The other edge of the sword is that one dud developer can bring your whole stack down by being too smart for their own good. The more dynamic the system the greater the risk. In Ruby as is pointed out you can have a developer on the other side of your code base royally screw you because he wants feature X of sequences to act differently.
As with all things it's a balancing act, how smart are your devs, how much time can you spend testing and how strict are your coding standards.
The problem appears if or when you want to share the code between departments or give it to the world. Give "clever code" to someone who simply doesn't understand the whole process and they turn into a "dud developer" no matter how otherwise intelligent they are.
(And what's with down-voting the parent? It's a fair point even if I'm not in agreement)
Still worth it though :)
Python is not in the middle of ruby and java. Python is virtually on top of ruby.
(On a side note - your definition of 'extreme' rather depends on what outliers you have. I do love it when the US media refers to the Democrats as 'left of center' ;)
On a serious note, I've been doing ruby as my job for about 5 years and I work at a giant corporation with a few hundred programmers. In those 5 years I've never been bitten by someone changing a core class that breaks my code. Maybe 3 times I've been bitten by someone changing a non core class, and it usually takes me about an hour or two to resolve it.
So that's maybe 6 hours in 5 years where I've lost time due to this, but the amount of time it has saved me is far larger than this.
You should blog :)
I view this situation as somewhat analogous to having guards on power tools: even a highly skilled operator doesn't want to rely on always doing something perfectly.
In my experience, teams building Ruby apps tend to be much smaller than teams building Java apps ..
That said, this is a problem for any libraries that you might want to include, you have to trust that the authors followed conventions and didn't do any thing 'evil'. Ruby Rewrite (http://rewrite.rubyforge.org/) was conceived to specifically avoid this sort of problem.
What other "someone else's" Scala code have you actually had to look at and maintain that you had problems with? In my experience, I only have a hard time understanding Scala code when it is doing something I'm conceptually unfamiliar with, usually something in the functional world that I know little or nothing about.
In regards to monkey-patching, while I'm not a Ruby programmer by any means, I sorely feel its absence in other languages. It's in JavaScript (expando objects as well as being able to override and overwrite built-in methods of existing types), and to some extent available in C# (extension methods are a sort-of expando object, but there is very little ability to overwrite a method). Like operator overloading, sure, don't build a project completely off of it (I'm looking at you, C++'s iostream), but when you need it, damn you need it. I'm still delving into Python (late to the party, whatever), but I definitely use Python in much more of a tinkering fashion, so monkey-patching is something I would desire in the language.
I don't entirely disagree with the larger sentiments, but I worry about the tone of the word 'tinker'. That is, the Ruby world is full of playful characters, and often displays a spirit that is far from corporate. However, many Ruby (and RoR) apps are also serious businesses at this point. There are also many libraries that don't charge money, do exhibit that playful or exuberant spirit, but also provide hard-core real-world functionality (e.g., Nokogiri).
tl;dr The word 'tinker' is a bit trivializing (perhaps unintentionally on Martelli's part). The division between 'tinker' and 'enterprise' doesn't fully or properly cut up the problem space. It's a binary view of a multi-faceted world.
(For whatever it's worth, this is my view as someone on the outside of this whole situation. I use Ruby and Perl about equally, primarily for systems administration and personal projects.)
Most of it makes sense, or seems obvious, but your SAAS companies don't typically offer Active Directory (or LDAP) integration, as most companies aren't willing to expose their directories outside of their firewalls.
Auditing levels tend to be more paranoid, and need to be structured so that they can be fed into (or are at least sane enough to be read by) the industry-standard log analysis tools.
Admin accounts are typically transitioned to in-house users, or employees of the company, instead of being managed by the man behind the curtain (you).
So, perhaps it's that my experience is different than the majority of HN here, but to me, 'enterprise development' is a dramatically different process than what those guys at 37Signals do, for example.
But seriously, at his Q&A session on EuRuKo 2010 Matz mentioned that monkey patching issues are going to be addressed in Ruby 2.0. http://nuclearsquid.com/writings/euruko-day2.html
It's just that Python hasn't ever had an ethos of monkey-patching.
This feature allows you to transform things like "1, minute" into "Minute(1)", enabling dsls, and "wrecking havoc" all over. In ruby, you don't have to do this, as there are simpler and less powerful metaprogramming tools.
So yeah. Just because you have a shotgun, it doesn't mean you can't use it to kill a fly. Of course, the really important question is not if you can, but if you should. And it is always better that this decision is left for the users of the language than to its creators.
I highly doubt he's "ignorant" of anything when it comes to Python...
A better theory is this article is from 2003, but the ast module was added in Python 2.6, which was released in 2008.
(I agree with everything you've said in the second and third paragraphs, but, man, try to give people a little credit before you assume they're ignorant, 'k?)
I seriously did not want to imply (the incorrect fact) that the author was ignorant in general, but of a single matter (which is kinda unknown, so I'd expect most be to be oblivious to it).
I am equaly sorry for the bad choice of words...
In Ruby, meta-programming is always powerful and always so easy to use. I don't have problem using these methods, but it pains me when some crappy monkey-patching-flavored gem (For example, cache-money) decided to bite me intermittently.
Or when debugging the code of some kick-ass-ninja-rockstar programmer where he decided to generate methods using both class_eval and instance_eval (along the way, generating the "def statements" using string interpolation) inside a module that supposed to be injected into ActiveRecord object.
I do agree with your last paragraph completely. The real ninja is capable of stopping himself from beheading anyone he sees just because he can.
Is there a good guideline for static method vs. instance method distinction?
a.inverse() -> mutates 'a' to its inverse
MatrixMath.inverse(a) -> returns the inverse of 'a', leaving 'a' unchangedBTW the ruby equivalent for the above convention would be (maybe inspired by scheme?)
a.inverse!() -> mutate a
a.inverse() -> return the inverse
Which one is more consistent? 1 isn't a matrix
A Fixnum could be interpreted as a 1x1 matrix. For some recursive solutions, it would probably be nice of MatrixMath.inverse accepted it as such, as well as [[1]].However, it doesn't take much to "fix" it the way you like it. You need to patch Numeric. Whenever you call a method that doesn't exist, it calls the method "method_missing". Hence you can monkey patch Numeric#method_missing to call the equivalent Math function.
In ruby you dont. It's #size and ruby is quite consistent in this respect. So it's remembering a method name vs remembering a function name (in a supposedly OO language).
It's part of the Ruby's philosophies to have synonyms like that.
Example: If __len__ is defined on an object x, and __nonzero__ (Py2) or __bool__ (Py3) is not defined, then "not x" will test if x.__len__() == 0.
Similarly, __contains__ interacts with "foo in x", and __eq__ and __hash__ are expected to play well together for hashable objects (e.g. unique members of a set).
The double underscores are a useful code smell that indicates you're changing how an object interacts with Python's syntax.
I still think it's the wrong design, mind you, but I no longer think it's a crazy one.
Why use x.__len__() rather than len(x)?
I think of Ruby and Python as two languages standing back-to-back in the same spot, mostly unaware of each other, greeting developers who come from different directions.
I started out with Ruby, because of Rails, and just never seemed to quite make it jive with me mentally. When I found Python (because of Django), everything clicked more. I'm not even sure it was the language differences that clicked more for me, or whether it was the documentation, or the code samples, or some other intangible that made it all work.
Just a guess, of course.
The reason I referenced Rails and Django as motivators in finding those languages is because after having built and then forced to maintain an enterprise-class application in PHP, I REALLY liked the notion of an MVC framework, and things like Cake and Symfony weren't out (or at least I didn't know about them) yet.
So, yeah, I wasn't a programming newbie altogether, but I wasn't as comfortable in just picking up new languages either.
Also, the parentheses are always optional, not just for zero-argument calls, so he also missed the opportunity to discuss "poetry mode Ruby": a bunch of method calls with no parentheses. Stylistically, "poetry mode" enables the pseudo-DSL style that Ruby frameworks use frequently, but can't really be done in Python.
if baz>10:
func = self.foo
else:
func = self.bar
result = func()
Here func gets assigned something called a "bound method", which has a reference to the object and the method, which you can then apply the () operator and call. I am not a rubyist, but my understanding is that you would use a symbol to do a similar thing. if baz > 10
func = :foo
else
func = :bar
end
result = self.send(func) if baz > 10
func = method(:foo)
else
func = method(:bar)
end
result = func.call
Here, we actually deal with the Method objects, rather than just sending a dynamic message. Although the send() approach would be closer to idiomatic Ruby, I wanted to emphasize that dealing with methods as first class objects in Ruby is indeed possible.It's fun to contrast this with Lua, by the way.
In Python, you can make anything callable by providing a __call__() method, and the parentheses will work as expected. This makes functions fungible with anything, including any object or anonymous function.
In Ruby, only methods are callable, and everything else must be called with the call() method, which means you can't use one in a place where the other might be expected. They're two concepts with the same verb--call--that require different syntax. It's inconsistent.
In that sense, I see why it's not so bad; the difference between methods and anonymous functions is very explicit, and when you want to pass things around, it's likely that you will have target code which expects a Proc object and source code that generates it. Perhaps it's not so bad because there are few legitimate use cases, if any, for transporting a method when an arbitrary block is expected.
As wycats pointed out in a post earlier this year (http://yehudakatz.com/2010/02/21/ruby-is-not-a-callable-orie...), the reason you don't need to explicitly call #call on methods is that Ruby is designed for the common case of calling methods. In addition, it's uncommon to even explicitly use #call or #[] on Procs since calling them is built into the language via yield.
Also, see wycats post: http://yehudakatz.com/2010/02/21/ruby-is-not-a-callable-orie...
This doesn't really work if you would want to pass several procs into a method, but honestly, that is not very common.
a = lambda { |x| x + 1 }
a(3) #=> 4
But since it doesn't, the alternatives are not too bad (though likely far too numerous for Python tastes) a[3] #=> 4
a.(3) #=> 4 (on Ruby 1.9)
I think that Ruby's ability to call methods without () make for better readability in the case of writing domain specific interfaces, but come at a cost in other places. Personally I think it's a worthwhile tradeoff but I can see the other side of the argument. p = Proc.new {|str| puts "Hello #{str}" }
p["World"] # prints "Hello World"
:)So, for instance, you might solve it with:
result = case
when baz>10 then foo
else bar
end
This is synthetic, of course. If the goal is to pass around methods, there are lambdas and codeblocks that can be passed around and may fit the problem more naturally than sending yourself a symbol as a message. result = if baz > 10
foo
else
bar
end result = case
when baz>10 then foo
else bar
end
is a translation of if baz>10:
result = self.foo()
else:
result = self.bar()
while result = if baz > 10
foo
else
bar
end
is a translation of result = self.foo() if baz>10 else self.bar()
It's true that some things have to be changed when translating, but you can't ignore the deliberate structure of the original. Let's say I want to look at how inter-sentence objects work in two languages. I give you the phrase: "Raining cats and dogs. This is what the weather is like." You can't translate it as if it said "It's raining cats and dogs" without losing the basis of comparison entirely. foo { |x| puts x }
Whilst in Python, I need to write: def tmp(x): print x
foo(tmp)
It's not really very nice.The other thing is that Python has no concept of context. In Ruby, I can type:
foo(x)
And it will execute foo(x) for the containing object. In Python, the closest equivalent would be: self.foo(x)
This syntax difference may seem trivial, but it allows you to do some very interesting things to classes. For instance: class Foo
def self.property(name)
define_method(name) do
instance_variable_get(name)
end
end
end
class Bar < Foo
property :x
end
Now the class Bar has a method called "x", without us needing to explicitly define it.Ruby's instance_eval method extends this further, allowing you to execute a block of code in the context of any object you wish.
myfunc <- lambda x: (print x) or x*x # SyntaxError
myfunc <- lambda x: print(x) or x*x # OK in Py3Rather, I'd say that part of the zen of python is: implicit is better than explicit, and I prefer python's behavior in this instance.
Python: http://docs.python.org/release/2.5.2/ref/grammar.txt
Ruby: http://web.njit.edu/all_topics/Prog_Lang_Docs/html/ruby/yacc...
ANSI C: http://www.lysator.liu.se/c/ANSI-C-grammar-y.html
Python and ANSI C both have a similar organization of productions and names for statements and expressions. It's pretty easy to follow the grammar to obvious points where the language differs. What are valid unary expressions in Python and ANSI C? There's a production in each grammar that makes a convenient starting point and comparison is easy. What is a valid unary expression in Ruby? It's much less clear.
The Ruby grammar isn't large, so I'm sure some people prefer the way it's organized. Once you've absorbed the whole thing questions like "what's a valid unary expression?" don't matter anymore. But I think it's an interesting distinction to someone trying to decide which language to learn.
Working with Ruby on a day-to-day-basis, I have never reopened core classes and changed anything. Rails comes with some common extensions which simply add new methods, which are less error prone (although I avoid Rails & ActiveSupport whenever possible).
The only things that are really "different" between Python and Ruby are topical features of the grammars of each, that mostly disappear once the code has been AST-transformed. These could likely be expressed more succinctly in terms of a simple common base grammar, and two small, modular sets of reader macros.
I have been successful at learning a variety of languages. Ruby was difficult for me because it is so similar to Python and yet so different. You have almost the same syntax getting very different results. So if you come from Python you get the exact opposite experience to the principle of least surprise. On the other hand, if you move from OCaml or Erlang there are no surprises because the syntax is different enough that there are no pre-expectations.
Also, metaprogramming in Python and Ruby are quite distinct. The way Python went about it using metaclasses and decorators is very different to the monkeypatching you get in Ruby, so I don't expect the AST results to be that similar for idiomatic code in both languages.
When two species fill a different niche in nature, they can co-exists. When they fill the same niche, it is a bitter fight for survival.
More succinctly: "Ugh! Python? I hate the idea of syntactic whitespace," and "Ugh! Ruby? I have the idea of using @ for instance variables," are both cursory judgments that mask deeper preferential foundations therein.
(But these days, I'm mostly hacking with Arc :p)
I put this question also on Stackoverflow: http://stackoverflow.com/questions/3584945/non-technical-ben...
+ Newspapers require me to register and login - I go somewhere else.
+ Commercial sites require me to register and login - I go somewhere else.
+ Google requires me to register and login, guess what happens.
I can't be bothered. I've got stuff to write, products to ship, and a life to live. It's just another article. I've saved time by not reading it, and the chances it was useful are small. Besides, skimming the comments has given me enough of the flavor that I don't really think I'm missing much that I didn't get from the comments themselves.
Score another one for the HN community. Thank you.
I must admit that I use Python more than Ruby. I like both, but know Python better. I feel Ruby is more consistent though. Either is a good language!
* list(reversed(a_list)) * "".join(reversed(a_str))
and the like.
But if I wrong about this, please let me know.
...suddenly wonders if there's a language where both case and indentation are significant.
values, keywords: all lowercase modules, classes, types, constructors: initial capital
For example, it's impossible to declare a class named "string", or a value named "String".