Project

General

Profile

Actions

Misc #19971

open

Confusing arity of a Proc with implicit rest parameter

Added by andrykonchin (Andrew Konchin) 6 months ago. Updated 5 months ago.

Status:
Open
Assignee:
-
[ruby-core:115146]

Description

I've noticed that such proc proc { |a,| } has arity 1:

proc { |a,| }.arity # => 1

that means only one required parameter, but the proc behaves similar to a proc with explicit rest parameter (proc { |a, *| }) that has arity -2:

proc { |a, *| }.arity # => -2

that means one required parameter and rest parameter.

So I am wondering whether it's intentional behaviour and what the rational is behind it.

Updated by shan (Shannon Skipper) 6 months ago

Docs say it's intended, since #arity "returns -n-1, where n is the number of mandatory arguments, with the exception for blocks that are not lambdas and have only a finite number of optional arguments; in this latter case, returns n." A Proc in this case isn't a lambda, so it returns n rather than -n-1. Procs have loose arity, like blocks, unlike lambdas.

Updated by zverok (Victor Shepelev) 6 months ago

@shan (Shannon Skipper) I don't think this documentation definition covers the case described by OP.

proc { |a,| } is not the same as proc { |a| }, and is semantically equivalent of proc { |a,*| }. I think it is reasonable to expect its arity—and, by the way, parameters, would behave as in latter, not as in former.

I think this would be reasonable (and might be useful for metaprogramming code that checks the signature of the passed callable object):

proc { |a| a }.call([1, 2, 3]) #=> [1, 2, 3]
proc { |a| }.arity #=> 1
proc { |a| }.parameters #=> [[:opt, :a]]

proc { |a, *| a }.call([1, 2, 3]) #=> 1
proc { |a, *| }.arity #=> -2
proc { |a, *| }.parameters #=> [[:opt, :a], [:rest, :*]]

proc { |a,| a }.call([1, 2, 3]) #=> 1, like the latter
proc { |a,| }.arity #=> 1, should be -2
proc { |a,| }.parameters #=> [[:opt, :a]], should be [[:opt, :a], [:rest, :*]]

Though, experimenting a bit more with it, there is a funny edge case:

define_method(:m1, &proc { |a| })
define_method(:m2, &proc { |a, *| })
define_method(:m3, &proc { |a,| })

p method(:m1)  #<Method: Object#m1(a)>
p method(:m2)  #<Method: Object#m2(a, *)>
p method(:m3)  #<Method: Object#m3(a)> --- like 1, not like 2

...which is probably related to reported arity/parameters.

Updated by shan (Shannon Skipper) 6 months ago

Ah, I see. Thanks for clarifying! FWIW, lambdas don't ack the implicit rest either.

Actions #4

Updated by andrykonchin (Andrew Konchin) 5 months ago

  • Description updated (diff)
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0