Method arguments

    A method definition consists of:

    • required and optional positional parameters
    • an optional splat parameter, whose name can be empty
    • required and optional named parameters
    • an optional double splat parameter

    For example:

    Each one of them is optional, so a method can do without the double splat, without the splat, without named parameters and without positional parameters.

    A method call also has some parts:

    1. # These are positional arguments
    2. 1, 2,
    3. # These are named arguments
    4. a: 1, b: 2
    5. )

    When invoking a method, the algorithm to match call arguments to method parameters is:

    • First positional call arguments are matched with positional method parameters. The number of these must be at least the number of positional parameters without a default value. If there’s a splat parameter with a name (the case without a name is explained below), more positional arguments are allowed and they are captured as a tuple. Positional arguments never match past the splat parameter.
    • Then named arguments are matched, by name, with any parameter in the method (it can be before or after the splat parameter). If a parameter was already filled by a positional argument then it’s an error.
    • Extra named arguments are placed in the double splat method parameter, as a NamedTuple, if it exists, otherwise it’s an error.

    When a splat parameter has no name, it means no more positional arguments can be passed, and any following parameters must be passed as named arguments. For example:

    1. # Only one positional argument allowed, y must be passed as a named argument
    2. end
    3. foo 1 # Error, missing argument: y
    4. foo 1, 2 # Error: wrong number of arguments (given 2, expected 1)
    5. foo 1, y: 10 # OK

    But even if a splat parameter has a name, parameters that follow it must be passed as named arguments:

    There’s also the possibility of making a method only receive named arguments (and list them), by placing the star at the beginning:

    1. # A method with two required named parameters: x and y
    2. end
    3. foo # Error: missing arguments: x, y
    4. foo x: 1 # Error: missing argument: y
    5. foo x: 1, y: 2 # OK
    1. # x is a required named parameter, y is an optional named parameter
    2. def foo(*, x, y = 2)
    3. end
    4. foo # Error: missing argument: x
    5. foo x: 1 # OK, y is 2

    Because parameters (without a default value) after the splat parameter must be passed by name, two methods with different required named parameters overload:

    Positional parameters can always be matched by name:

    1. def foo(x, *, y)
    2. end
    3. foo y: 2, x: 3 # OK

    An external name can be specified for a method parameter. The external name is the one used when passing an argument as a named argument, and the internal name is the one used to refer to the parameter inside the method definition:

    1. def foo(external_name internal_name)
    2. # here we use internal_name
    3. end
    4. foo external_name: 1

    This covers two uses cases.

    The second use case is making a method parameter more readable inside a method body:

    1. def increment(value, by)
    2. # OK, but reads odd
    3. value + by
    4. end
    5. def increment(value, by amount)
    6. # Better
    7. value + amount