42

Ruby 2.0 supports keyword arguments. I was wondering, what are the 'rules' for mixing regular with keyword arguments? Something like this would not work:

def some_method(a: 'first', b: 'second', c)
  [a, b, c]
end

but this will:

def some_method(c, a: 'first', b: 'second')
  [a, b, c]
end

So why does putting a positional argument before the keyword arguments (and not after) work?

Is there some reference on the web on this (mixing keyword and positional arguments)? I haven't found any.

Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
daremkd
  • 8,244
  • 6
  • 40
  • 66

3 Answers3

64

The order is as follows:

  • required arguments
  • arguments with default values (arg=default_value notation)
  • optional arguments (*args notation, sometimes called "splat parameter")
  • required arguments, again
  • keyword arguments
    • optional (arg:default_value notation, since 2.0.0)
    • intermixed with required (arg: notation, since 2.1.0)
  • arbitrary keyword arguments (**args notation, since 2.0.0)
  • block argument (&blk notation)

For example:

def test(a, b=0, *c, d, e:1, f:, **g, &blk)
  puts "a = #{a}"
  puts "b = #{b}"
  puts "c = #{c}"
  puts "d = #{d}"
  puts "e = #{e}"
  puts "f = #{f}"
  puts "g = #{g}"
  puts "blk = #{blk}"
end

test(1, 2, 3, 4, 5, e:6, f:7, foo:'bar') { puts 'foo' }
# a = 1
# b = 2
# c = [3, 4]
# d = 5
# e = 6
# f = 7
# g = {:foo=>"bar"}
# blk = #<Proc:0x007fb818ba3808@(irb):24>

More detailed information is available from the official Ruby Syntax Documentation.

Patrick Oscity
  • 53,604
  • 17
  • 144
  • 168
  • Forgot about "arbitrary keyword arguments" with the `**` notation, so I added these, too. – Patrick Oscity Dec 17 '13 at 12:32
  • You can also have mandatory parameters after the splat. And in Ruby 2.1, you can also have mandatory keyword parameters, which can be freely intermixed with optional keyword parameters. See http://stackoverflow.com/a/20634180/2988 – Jörg W Mittag Dec 17 '13 at 12:35
  • @JörgWMittag thanks! I've also noticed that skimming through the docs. Added it to the list. – Patrick Oscity Dec 17 '13 at 13:04
  • 2
    Thanks, this was more helpful to me than the accepted answer. I know that says more about me than it does about the accepted answer, but there you go :) – sixty4bit Mar 10 '15 at 19:09
19

A pseudo-regex for parameter lists in Ruby (this applies equally to methods, blocks and lambda literals) is something like this:

mand* opt* splat? mand* (mand_kw | opt_kw)* ksplat? block?

Here's an example:

def foo(m1, m2, o1=:o1, o2=:o2, *splat, m3, m4, 
          ok1: :ok1, mk1:, mk2:, ok2: :ok2, **ksplat, &blk)
  Hash[local_variables.map {|var| [var, eval(var.to_s)] }]
end

method(:foo).arity
# => -5

method(:foo).parameters
# => [[:req, :m1], [:req, :m2], [:opt, :o1], [:opt, :o2], [:rest, :splat], 
#     [:req, :m3], [:req, :m4], [:keyreq, :mk1], [:keyreq, :mk2], 
#     [:key, :ok1], [:key, :ok2], [:keyrest, :ksplat], [:block, :blk]]

foo(1, 2, 3, 4)
# ArgumentError: missing keywords: mk1, mk2

foo(1, 2, 3, mk1: 4, mk2: 5)
# ArgumentError: wrong number of arguments (3 for 4+)

foo(1, 2, 3, 4, mk1: 5, mk2: 6)
# => { m1: 1, m2: 2, o1: :o1, o2: :o2, splat: [], m3: 3, m4: 4, 
#      ok1: :ok1, mk1: 5, mk2: 6, ok2: :ok2, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, mk1: 6, mk2: 7)
# => { m1: 1, m2: 2, o1: 3, o2: :o2, splat: [], m3: 4, m4: 5, 
#      ok1: :ok1, mk1: 6, mk2: 7, ok2: :ok2, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, mk1: 7, mk2: 8)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [], m3: 5, m4: 6, 
#      ok1: :ok1, mk1: 7, mk2: 8, ok2: :ok2, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, mk1: 8, mk2: 9)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5], m3: 6, m4: 7, 
#      ok1: :ok1, mk1: 8, mk2: 9, ok2: :ok2, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, 8, mk1: 9, mk2: 10)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, 
#      ok1: :ok1, mk1: 9, mk2: 10, ok2: :ok2, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, 8, ok1: 9, mk1: 10, mk2: 11)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, 
#      ok1: 9, mk1: 10, mk2: 11, ok2: :ok2, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, 8, ok1: 9, mk1: 10, mk2: 11, ok2: 12)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, 
#      ok1: 9, mk1: 10, mk2: 11, ok2: 12, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, 8, ok1: 9, mk1: 10, mk2: 11, ok2: 12, k3: 13)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, 
#      ok1: 9, mk1: 10, mk2: 11, ok2: 12, ksplat: {k3: 13}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, 8, 
      ok1: 9, mk1: 10, mk2: 11, ok2: 12, k3: 13, k4: 14)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, 
#      ok1: 9, mk1: 10, mk2: 11, ok2: 12, ksplat: {k3: 13, k4: 14}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, 8, 
      ok1: 9, ok2: 10, mk1: 11, mk2: 12, k3: 13, k4: 14) do 15 end
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, 
#      ok1: 9, mk1: 10, mk2: 11, ok2: 12, ksplat: {k3: 13, k4: 14}, 
#      blk: #<Proc:0xdeadbeefc00l42@(irb):15> }

[Note: mandatory keyword arguments will be introduced in Ruby 2.1, all the rest already works.]

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 8
    If you're a newb like me and this answer makes no sense, make sure you scroll down to @PatrickOscity's answer. – sixty4bit Mar 10 '15 at 19:06
  • wow, this is why I dislike ruby so much. What kind of introduction to a language is this? – heeroyuy84 Aug 18 '21 at 13:10
  • 1
    @heeroyuy84: What makes you think this is an introduction to Ruby? This is not an introduction to Ruby, this is an answer on a question&answer website. – Jörg W Mittag Aug 18 '21 at 18:05
2
  1. Arguments with defaults and splat argument must be grouped together;
  2. Splat argument must appear after positional arguments with default values but before keyword arguments;
  3. Keyword arguments must appear after positional arguments and before double splat argument;
  4. Double splat argument must appear last but before block argument.

    def foo(a, b=1, c=2, *d, e, f: 1, g: 2, **kwargs, &block)

Viorel
  • 1,420
  • 1
  • 17
  • 27