ruby picture

RCR 227: Make blocks default to zero arguments

Submitted by eeklund (Thu Mar 04 13:03:36 UTC 2004)

Abstract

Change blocks with no parameter list to accept zero args instead of 0..infinity args.

Problem

def block_arity(&block)
   block.arity
end

Present behaviour:

block_arity { }           => -1
block_arity { || }        => 0
block_arity { |*unused| } => -1

In other words, a block with nothing specified now accepts any number of arguments, silently discarding these.

This makes for risky code: People generally do not specify argument lists for blocks they do not expect to take any arguments, and this result in arguments being discarded in an error situation. It also result in it being hard to implement meta-programming around these blocks (it "looks weird").

For instance: I discovered this while attempting to use meta-programming to remove duplication in my initialize-methods. I'd discovered that a large fraction of my initializers looked like this:

def initialize(a, b, c)
    @a = a
    @b = b
    @c = c
    ... calculate some things based on a, b, and c ...
end

Thus, I wanted to add a meta-method to get rid of the duplication in that code, resulting in


    
   simple_initializer(:a, :b, :c) do
        ... calculate some things based on @a, @b, and @c ...
   end

    

I wanted to keep the flexibility to add an argument d by doing


    
   simple_initializer(:a. :b, :c) do |d|
   end

    

Due to blocks defaulting to taking |*unused| instead of ||, this requires extra syntax for the normal case - and it means that if that syntax is forgotten and a mistake is made somewhere else (and number of parameter errors is a type of error I've noticed crop up reasonably often), the code silently does the wrong thing.

Proposal

Change behaviour to

block_arity { }           => 0
block_arity { || }        => 0
block_arity { |*unused| } => -1

(with associated behaviour when arguments are passed).

Analysis

Why does the problem demand a language change?

The present behaviour is risky (errors lead to widespread failure far from the point of call and potentially much later), and it surprised at least me.

Pros of the change:

Cons of the change:

Implementation

I'll provide a patch if people are interested (not written yet.)
ruby picture
Comments Current voting

Just realized that you might want to add some words on the scope of this change:

>> def foo(&b); b.call 1,2,3 end
=> nil
>> foo { || }
=> nil
>> foo(&lambda{||})
ArgumentError: wrong number of arguments (3 for 0)
        from (irb):1
        from (irb):3:in `call'
        from (irb):1:in `foo'
        from (irb):3
so that it's clear that "plain blocks/Procs" ({ } or Proc.new) can/will still be called with 'yield semantics' regardless of their arity.
          

Also, the following sentence could lead the reader to believe the change in #arity would protect him from all such errors when using blocks, which it wouldn't:

"People generally do not specify argument lists for blocks they do not expect to take any arguments, and this result in arguments being discarded in an error situation."

That is, you might want to state that the change would only affect ("fix") lambdas and direct calls to Proc#arity, not the general block case.

-- Mauricio Fernández (batsman)


What exactly is your proposal? Is it just changing Proc#arity to return zero for Proc.new{}? Or something else?

--matz.


No - change blocks with no arguments specified to be the same as blocks with zero arguments specified for the Proc.new case.

--eek


OK, tell me _all_ changes you want, and what you want to accomplish by those changes. Proc usually discards unused arguments anyway. Note that I'm not against your proposal. I want to make things clear before making any change.

-- matz.


Strongly opposed 0
Opposed 0
Neutral 4
In favor 9
Strongly advocate 3
ruby picture
If you have registered at RCRchive, you may now sign in below. If you have not registered, you may sign up for a username and password. Registering enables you to submit new RCRs, and vote and leave comments on existing RCRs.
Your username:
Your password:

ruby picture

Powered by .