Submitted by eeklund (Thu Mar 04 13:03:36 UTC 2004)
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.
block_arity { } => 0 block_arity { || } => 0 block_arity { |*unused| } => -1
(with associated behaviour when arguments are passed).
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:
Comments | Current voting | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|
RCRchive copyright © David Alan Black, 2003-2005.
Powered by .
Just realized that you might want to add some words on the scope of this change:
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.