Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Ruby is not a callable orientated language (yehudakatz.com)
71 points by adam-_- on Feb 21, 2010 | hide | past | favorite | 30 comments


This is something that pisses me off about Ruby -- it has four goddamn callable types [Methods, Procs, Blocks, Lambdas] that all turn into one another at different points, but you still can't pass around non-anonymous callables directly!

I really love how Python's approach is structured: Methods are just Functions accessed as properties of an instance Object. Lambda expressions just return a normal Function. Functions are just Objects with a __call__ method, metacircularly. It's turtles all the way down!

In Python I implement __call__ methods in Classes and add instance properties as metadata on function objects all the time. In Ruby it seems like I always have to wrap a callable in another callable to be able to pass it around without accidentally calling it -- goddamn no-paren calling! At least lisps have quote operators to solve this problem transparently.

Ruby's block syntax is rather nice, it just solves a completely different problem: you don't actually use it to make functions to pass around as objects -- you're really using it to make control structures directly.


"it has four goddamn callable types [Methods, Procs, Blocks, Lambdas] that all turn into one another at different points, but you still can't pass around non-anonymous callables directly!"

Aside from the errors in this sentence (blocks, procs and lambdas are all just Proc objects, and you can pass around methods using method(:method_name)), assuming that passing around methods is supposedly common really betrays a python-centric point of view.

In Ruby, passing around methods by name is very uncommon. Running ack through 407 gems I see 116 instances of it, period. That's 116 times in some huge number of lines of ruby code that someone typed method(:some_method).

It's clear why this wouldn't be a common thing in Ruby: all of the Proc types are flexible enough that it's rarely necessary to pass around methods. I find it incredibly hard to believe that people who complain about this are actually encountering this while using Ruby as Ruby.


"Aside from the errors in this sentence (blocks, procs and lambdas are all just Proc objects"

No, they're different: http://innig.net/software/ruby/closures-in-ruby.rb


They are indeed all Proc objects:

    Proc.new {}.class
    => Proc
    lambda {}.class
    => Proc
    proc {}.class
    => Proc
    def block_class(&block) block.class end
    block_class {}
    => Proc
That write-up does have some great info in it, though.

Edit: there's an interesting comment on wycats' post that suggests the &block creates a Proc, and that blocks themselves are neither objects nor callable, ie, they can only be used directly with yield. That makes sense.

Even in that case, the original point still stands; we're still only dealing with Methods and Procs.


My link empirically demonstrates that all 7(!) constructs behave differently, regardless of what foo.class says. I personally have been burned by the differences between them, so I'll continue to support people differentiating between them, even if the interpret lies.


"My link empirically demonstrates that all 7(!) constructs behave differently"

It explicitly doesn't. Read it again. Immediately after listing his 7 constructs he points out that a number of them are actually the same thing, and in the summary he clearly explains that there are only 4 different behaviors. His 7 constructs (which include methods and bare blocks) are actually only 3 or, arguably, 3.5/4 (his numbers in parens):

blocks, which aren't callable without being wrapped in a Proc in CRuby (1, 2)

Procs (two types: Proc.new/&block and lambda) (3, 4, 5, 6)

methods (7)

There two types of Procs, plain procs and lambda, with lambda's differences being how it checks arguments and its return behavior, as I noted yesterday in another comment in this discussion (http://news.ycombinator.com/item?id=1141030).

So, again, the original comment, "four goddamn callable types [Methods, Procs, Blocks, Lambdas]," neglects the fact that #call on the last three is actually just Proc#call.


Wrong. Ruby code blocks can be Methods, Procs or Lambdas. Methods are the hard-coded blocks of code on a Class or a Module (or in the global space, which actually is in the Kernel module). A "block" is simply a contained/scoped bit of code.

Procs and Lambdas exist because they exhibit different scoping rules. If you want local scoping only, use a lamdba. If you want access to information outside the block, use a proc.

Of course, your rant sure sounds like you didn't actually read Katz's blog post, or you wouldn't be using terms like "callable types." The post explains exactly WHY Ruby doesn't have an equivalent __call__ structure...


>> lambda {} == proc {} => true

Can you shed some light on the difference? I always thought that lambda is simply a synonym for proc - has this changed in more recent versions?


See here: http://www.robertsosinski.com/2008/12/21/understanding-ruby-...

Proc objects created with lambda check arguments and have "diminutive return" so they behave as anonymous methods.

Also, Kernel#proc is an alias to Kernel#lambda, so the only way you get the bare Proc behavior is by calling Proc.new directly.


Ah, I didn't know that Proc.new != proc/lambda :)

They both return Proc objects though, so I wonder if it is possible to "convert" them.


One small note, as pointed out in http://innig.net/software/ruby/closures-in-ruby.rb posted by arohner above: although in 1.8 proc is an alias for lambda, in 1.9 it's now an alias for Proc.new.


The empty lambda or empty proc is a special object that always points to the same instance. Probably some kind of optimization.


What exactly is your complaint about Ruby? Yeah they are different types but they all respond to #call. How is this less structured than Python?

Can't pas around non-anonymous callables directly? method(:function_name)?


[self-delete] - seems I was thinking about something different than OP.


Why does the type matter? Ruby apps extensively use duck typing, they only care that #call is implemented irregardless of the actual type. Even blasdel is duck typing by implementing __call__ in his own classes.

> In most situations if you saw 4 classes in the system doing virtually the same thing, but you need to use a specific one depending on context

You don't need to use a specific one. You never directly instantiate a Method/Proc/etc. Instead of you just focus on the goal you want to achieve (e.g. passing an object's method, or maybe some kind of callable object that you received via an argument) and then Ruby gives you an object of some type that works.

> You just expect to call .get_address(), but suddenly find out that one of them handles only .get_customer_address().

This does not apply to Ruby. Method and Proc (actually I think there are only Method and Proc, not 4 classes as the grandparent suggested) both respond to #call. They are not two distinct types that behave completely different, they behave the same from the point of view of the caller and just have different names. Given a function that expects a Method or a Proc, you can pass either a Method or a Proc and it always works.


Don't delete comments. Just edit them and note that you were wrong, or reply to the comment that corrected you.


There is no special block or lambda type in Ruby, AFAIK there is just proc and method. The difference is simple, a method is a proc that will be evaluated in the context of its object.

I don't know why you're having a problem passing around foo = lambda {|x,y,z| x+y+z }.

I admit that having to explicitly call a proc using .call or .[] is less pretty than the python equivalent, but it's hardly a big problem.

Having used both Python and Ruby extensively I've concluded that both languages are very nice and usable. They both have their warts and shortcomings, but after Python added closures I really haven't had many "darn, I hate [Ruby|Python] because of shortcoming X".

Edit: As noted elsewhere, there IS in fact a difference between Proc.new and lambda/proc, Though I've personally never used Proc.new.


You didn't address the point of the article: Ruby is optimized for the common case. Yes, I too like the idea of how pure Python's approach is, but look at your code; you are mostly writing methods. They should be primitive. Ruby gets a little messy, but it does it for you, so you can write cleaner code.

As for those four types, methods are one of the central pillars of Ruby, they are more than functions and they deserve their own type. Blocks are not a type, that's a common confusion. Lambdas are just what you'd expect, and more powerful than Python lambdas. Procs are there to support block syntax, again for convenience in the common use case, because if you used lambdas for blocks you wouldn't be able to return out of a method while inside a block such as a loop.


It's probably just because it's late here, and I'm over tired, but I'm having a real hard time understanding your complaint. Are you able to provide some examples please?


tl;dr: The problem is that if () were to call blocks, then this

    def foo
      proc {}
    end
    
    foo()
...would be ambiguous. Is it a call of foo returning a block, or a call of foo returning a block and calling that block?

To "fix" this you'd have to require parens when calling all functions, since you have no way of knowing whether the function returns a block or not. The other option is only allowing parens to call blocks at certain times and not at others - certainly confusing!


I'm not sure why people say that. It seems obvious. What optional '()' needs to achieve is:

    def a
      42
    end

    a => 42
    a() => 42
So the grammar could use something like:

    call = expression '(' ... ')'
    call_or_variable = expression
it cannot be reversed really, because then it would match `call_or_variable` '(' ')' - which would make `a()` fail to run, because 42 is not callable (or you'd have to use backtracking and guessing... evil).

It cannot be the case of "no way of knowing", because at parsing time in dynamic language you don't know what the result of a function is - you only look at grammar. So in the simplest possible way, if the '()' is optional, then... I'm not sure how to call it, but let's say - '()' binds stronger than 'possible application':

    foo => call foo
    foo() => call foo
    foo()() => call proc


foo()() doesn't seem better than foo.call


If we were to make () call blocks, then we would have to make ()'s in function calls mandatory, so that we would get something like foo()() to call the function _and_ the block.


Io presents an interesting alternative. There are only Blocks (functions with assignable scope). Blocks have an activatable slot. If you set it to true, the block is activated when an object receives a message that looks up the slot with the block. If false, it returns the block.

    sayHello := block(“hello” println)
    sayHello //returns the block
    sayHello setIsActivatable(true)
    sayHello //prints “hello”
    getSlot(“sayHello”) //returns the block
http://www.iolanguage.com/scm/git/checkout/Io/docs/IoGuide.h...


Whoa. So much for the principle of least surprise!


In practice, Io does follow the principle of least surprise. By default, blocks aren't activatable and methods are.


I'm talking about a higher meta-level: to me, it's surprising that the language semantics of what it means to simply type an identifier of a function change based upon the runtime state of the program. This seems crazy to me, it's like A New Kind of Side Effect I'd never seen before.


This is called late binding, and it allows you more flexibility and productivity if you're willing to accept fewer constraints.

Its actually quite powerful and allows some things that make Io a joy to work with. Memoization for instance:

    foo := method(foo = performSomeBigCalculation)


Io rocks :)


Hmm, I was unaware that Ruby worked that way. I'm not one to say that this is "bad" or "good" -- since I haven't used Ruby much -- but it seems unnecessarily confusing, even if it is consistent.




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

Search: