Named argument passing, and method_args extension

Quick question - what are the current thoughts about named argument passing
in future versions of Ruby? I've seen it requested in the past but not much
in the way of concrete proposals.

Those awfully nice Merb people have just come up with a clever idea, already
implemented as a small C extension. This lets you reflect on the names of
arguments in a method definition.

  require 'method_args'
  class X
    def hello(foo, bar)
      nil
    end
  end

  x = X.new
  meth = x.method(:hello)
  meth.args? # => true
  meth.args # => ['foo', 'bar'] <<< cool!

This has been released as a Gem, see
http://rubyforge.org/pipermail/merb-devel/2007-September/000239.html

The idea is that instead of writing

  def show
    id = params[:id]
    page = params[:page]
    ...
  end

you should be able to write

  def show(id, page=nil)
    ...
  end

with Method#args allowing the desired elements of the params hash to be
picked out automatically and passed in the correct order.

Effectively what you end up is something like a splat call, but with the
arguments in named order as defined by the method definition.

Taking this one step further, has it been considered that splat might be
applied to a hash in the same way? e.g.

   def func(a, b, c)
     [a, b, c]
   end

   func( *{:c=>3, :b=>2, :a=>1} ) # => [1,2,3] :slight_smile:

I had a look at http://eigenclass.org/hiki.rb?Changes+in+Ruby+1.9#l4 but
couldn't see anything along these lines.

In 1.8, splat _does_ work on a hash, but only by flattening it to an array
first, which isn't very useful as the order is arbitary.

irb(main):003:0> def f(*a); p a; end
=> nil
irb(main):004:0> f(*[1,2,3])
[1, 2, 3]
=> nil
irb(main):005:0> f(*{:a=>1,:b=>2})
[[:b, 2], [:a, 1]]
=> nil

Regards,

Brian.

P.S. By symmetry, I'd suggest that any 'unused' elements could be available
as a hash too:

  def func(a, b, c=3, *d)
    p a, b, c, d
  end

  func( *{:a=>1, :b=>2, :e=>5, :f=>6} )
  # 1
  # 2
  # 3
  # {:e=>5, :f=>6}

However this does mean that the receiver of *d would have to be able to cope
with receiving either an Array or a Hash.

Method#args has a home now at http://method-args.rubyforge.org/
I released a new version too. It can be installed directly from the command line

% sudo gem install method_args

There are several new methods for more refined sorcery:

% irb -r rubygems

require 'method_args'
class X; def foo(hello, world = 2, *blah); end; end
method = X.new.method :foo

=> #<Method: X#foo>

method.required_args

=> [:hello]

method.optional_args

=> [:world]

method.splat_arg

=> :blah

method.args

=> [:hello, :world, :blah]

ry

What IS in 1.9 is a new literal syntax for hash arguments:

$ irb1.9
irb(main):001:0> def test(arg)
irb(main):002:1> p arg
irb(main):003:1> end
=> nil
irb(main):004:0> test(a: "hello", b: 3)
{:b=>3, :a=>"hello"}
=> nil

as an alternative to:

irb(main):005:0> test(:a => "hello", :b => 3)
{:b=>3, :a=>"hello"}
=> nil

This allows all the current code which uses options hashs as a form of
named parameters to support a new "nicer" caller syntax without
change.

···

On 9/6/07, Brian Candler <B.Candler@pobox.com> wrote:

Quick question - what are the current thoughts about named argument passing
in future versions of Ruby? I've seen it requested in the past but not much
in the way of concrete proposals.

Those awfully nice Merb people have just come up with a clever idea, already
implemented as a small C extension. This lets you reflect on the names of
arguments in a method definition.

I had a look at http://eigenclass.org/hiki.rb?Changes+in+Ruby+1.9#l4 but
couldn't see anything along these lines.

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Hello,

Quick question - what are the current thoughts about named argument passing
in future versions of Ruby?

[...]
Taking this one step further, has it been considered that splat might be
applied to a hash in the same way? e.g.

   def func(a, b, c)
     [a, b, c]
   end

   func( *{:c=>3, :b=>2, :a=>1} ) # => [1,2,3] :slight_smile:

Wow, I really like this idea a lot.

I think this could be taken even a bit further,
by allowing the splat outside method headers:

  h = {:c=>3, :b=>2, :a=>1}
  a, b, c = *h
  p [a, b, c] # => [1,2,3]

The most important reason why I like this,
is because it would give us not only named
arguments, but named return values too,
leading to a nice symmetry:

def get_data
  ...
  {:name => name, :zip => zip}
  # the old way would be: [name, zip]
end

# and then we would simply write:
name, zip = get_data

# or just as well:
zip, name = get_data # same effect, more robust

Yes, hashes are already used to return multiple values today,
but I think the clumsy notation (
  d = get_data
  name = d[:name]
  zip = d[:zip]
) prevents many programmers from adopting this practice.

To sum up:
Yes. I would really like to see something like this in Ruby 3.0

P.S. By symmetry, I'd suggest that any 'unused' elements could be available
as a hash too:

  def func(a, b, c=3, *d)
    p a, b, c, d
  end

  func( *{:a=>1, :b=>2, :e=>5, :f=>6} )
  # 1
  # 2
  # 3
  # {:e=>5, :f=>6}

However this does mean that the receiver of *d would have to be able to
cope with receiving either an Array or a Hash.

OK, I would call that the problem of "reverse splatting".

I.e.: If we allow splat for both arrays and hashes,
we will be able to determine the type of the splat from the object
if the operator is on the right-hand side.

But we won't have any information, if it is on the left-hand side:
  *x = a, b, c
This could be interpreted as both
  x = [a, b, c]
or
  x = {:a=>a, :b=>b, :c=>c}

A simple way to deal with that, would be to declare the old interpretation
(array) as the only one for a LHS-splat.

And the semantics of Brian's example could be preserved in this approach,
because it not only has a LHS-splat, but also a RHS-splat with it.

Essentially it boils down to:
  *d = *{:e=>5, :f=>6}

And we just have to assume an internal "splat reduction", giving:
  d = {:e=>5, :f=>6}

However, it might be nice to have a way to force reverse hash splatting,
for example by a new prefix operator **
  **h = a, b, c
  # => h = {:a=>a, :b=>b, :c=>c}

... but also inviting cans of trouble, like
  **h = a, b, 42 # result??
  # perhaps
  # => h = {:a=>a, b=>b, 2=>42} (position used as key)

Yes, at this point I really wish that there be a unified Hash-Array
data structure, as they have in Oz or Lua.

Bye for now

Regards
Sven

···

On Do, 2007-September-06 10:17:11, Brian Candler wrote: