Hacker News new | past | comments | ask | show | jobs | submit login

This is good stuff, except that he seems to misunderstand the ()-less method call in Ruby. There's no "function" or "object" being "called" with that syntax, as in Pascal or Visual Basic (his chosen analogies). There are no functions in Ruby in that sense, and callable objects are explicitly called (with a "call" method). The expression "foo" in Ruby evaluates to the value of the local variable "foo" if any, or else sends the message "foo" to self and evaluates to the result. So yes, the parentheses are optional, but the ambiguity is with local variables, not with any other form of function call--so the ambiguity is hardly noticeable.

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.




Yes, there is a semantic difference here. In Python, you can do things like this:

  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.


Yep, the equivalent would probably be something along the lines of:

    if baz > 10
      func = :foo
    else
      func = :bar
    end

    result = self.send(func)


That may accomplish the same thing, but the direct translation would be:

    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.


I HATE dealing with first class functions in Ruby. I understand that it was a design choice, but man, in Python you can pass a function or a lambda and you just don't have to worry about which one you're calling. In Ruby, () will explode on a Proc object. What is with that?!!?


Because () doesn't call a method, it's simply used where a method call occurs. To call a callable object, you use #call or (as mentioned by epochwolf) #[], the latter of which I personally detest.

It's fun to contrast this with Lua, by the way.


It's the inconsistency that bothers me.

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.


You can also make anything callable in Ruby: just provide a call method. The parenthesis will still work as may be expected in Ruby, but, as several folks said before: in Ruby parenthesis are not used to call anything. A method is called simply be mentioning it. Anything that follows is an argument and sometimes parameters are needed for disambiguation of the parameters. This is counterintuitive, only because it differs from most other languages. If you want to postpone evaluation, you have to wrap the method call in a closure that you .call later: in Ruby a method is not a closure and not a function pointer. It's different than in many other languages, but that alone doesn't make it inconsistent.


Thank you--this is a good explanation of why the parentheses don't work on anonymous functions: Ruby's immediate calling essentially forces you to choose between "objects that you can call like methods" and "objects that you can easily pass around." This hearkens back to carbon8's comment (http://news.ycombinator.com/item?id=1141245); viewing Ruby's handling of first-class functions as "inconsistent" definitely betrays a Python-centric view of things.

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.


Procs (lambdas, procs and Proc-wrapped blocks) and Methods are callable and all respond to #call.

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.


Lua is a great little language... I only really 'discovered' it a few weeks ago and have really enjoyed what I've seen so far. I don't think it will replace Ruby for me, but I like it's minimal feel for comparison...


Are you actually passing around methods in Ruby and, if so, why? As I've pointing out in the past (http://news.ycombinator.com/item?id=1141245), passing around methods is very rare in Ruby due to the flexibility of Procs.


Interesting! I am a half-breed; I write Python at my day job and Ruby in my personal projects. You seem to be suggesting that I am sort of making a mountain out of a molehill. Can you perhaps elaborate on this? I have definitely felt this pain before; I'm not just complaining about it because I "like the Python way more." But if I'm not using Ruby "as intended," I would like to know how I should be using it instead! I feel like the "Python way" is powerful because you don't have to think about whether you're dealing with an anonymous function or a function, whereas in Ruby you must know for sure which one you are dealing with, because they are invoked differently. I'm interested to know how it isn't really a problem, if that's how you feel.


It's not a problem in Ruby because you don't pass around method instances, you pass around Procs (wrapped blocks, lambdas and procs), so if you are passing a callable around, you know it's a Proc. If you are passing around a method instance, you should almost certainly be using a block, lambda or proc.

Also, see wycats post: http://yehudakatz.com/2010/02/21/ruby-is-not-a-callable-orie...


If you have a method that in python would take a function as a parameter then it usually will be passed as a block in ruby.

This doesn't really work if you would want to pass several procs into a method, but honestly, that is not very common.


Well, a Method object and a Proc are two different things, but somewhat similar, so it still makes sense to discuss Proc behavior here. I would like it if this worked:

    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.


You can call a proc with []

    p = Proc.new {|str| puts "Hello #{str}" }
    p["World"] # prints "Hello World"
:)


I'm aware of this, but it's still not (). You still must be aware that you are dealing with an unbound proc object, and it must be handled differently than usual!


This discussion really makes me appreciate clojure.


Why is that?


You certainly could solve that with a symbol, I suppose, but that's a bit like doing a word-for-word translation of 'it's raining cats and dogs' into a foreign language. Better to translate to the local idiom than to try to blindly write Fortran in any language, as the saying goes.

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.


Or a slightly more direct translation is:

    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.


I like your perspective on this design decision as it seems to make clear what the intent could be. Alex reveals that the difference in Python is not so much the syntax as much as it hints at Python's treatment of functions as first class objects. This is really similar to Javascript where you can pass functions around for later evaluation. It adds a different level of complexity, of course, but it is something that I have appreciated in Python a great deal.




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

Search: