This is the formal specification of method and call arguments.
A method definition consists of:
For example:
def foo( # These are positional arguments: x, y, z = 1, # This is the splat argument: *args, # These are the named arguments: a, b, c = 2, # This is the double splat argument: **options ) end
Each one of them is optional, so a method can do without the double splat, without the splat, without keyword arguments and without positional arguments.
A method call also has some parts:
foo( # These are positional arguments 1, 2, # These are named arguments a: 1, b: 2 )
Additionally, a call argument can have a splat (*
) or double splat (**
). A splat expands a Tuple into positional arguments, while a double splat expands a NamedTuple into named arguments. Multiple argument splats and double splats are allowed.
When invoking a method, the algorithm to match call arguments to method arguments is:
When a splat method argument has no name, it means no more positional arguments can be passed, and next arguments must be passed as named arguments. For example:
# Only one positional argument allowed, y must be passed as a named argument def foo(x, *, y) end foo 1 # Error, missing argument: y foo 1, 2 # Error: wrong number of arguments (given 2, expected 1) foo 1, y: 10 # OK
But even if a splat method argument has a name, arguments that follow it must be passed as named arguments:
# One or more positional argument allowed, y must be passed as a named argument def foo(x, *args, y) end foo 1 # Error, missing argument: y foo 1, 2 # Error: missing argument; y foo 1, 2, 3 # Error: missing argument: y foo 1, y: 10 # OK foo 1, 2, 3, y: 4 # OK
There's also the possibility of making a method only receive named arguments (and list them), by placing the star at the beginning:
# A method with two required named arguments: x and y def foo(*, x, y) end foo # Error: missing arguments: x, y foo x: 1 # Error: missing argument: y foo x: 1, y: 2 # OK
Arguments past the star can also have default values. It means: they must be passed as named arguments, but they aren't required (so: optional named arguments):
# A method with two required named arguments: x and y def foo(*, x, y = 2) end foo # Error: missing argument: x foo x: 1 # OK, y is 2 foo x: 1, y: 3 # OK, y is 3
Because arguments (without a default value) after the splat method argument must be passed by name, two methods with different required named arguments overload:
def foo(*, x) puts "Passed with x: #{x}" end def foo(*, y) puts "Passed with y: #{y}" end foo x: 1 # => Passed with x: 1 foo y: 2 # => Passed with y: 2
Positional arguments can always be matched by name:
def foo(x, *, y) end foo 1, y: 2 # OK foo y: 2, x: 3 # OK
An external name can be specified for a method argument. The external name is the one used when passing an argument as a named argument, and the internal name is the one used inside the method definition:
def foo(external_name internal_name) # here we use internal_name end foo external_name: 1
This covers two uses cases.
The first use case is using keywords as named arguments:
def plan(begin begin_time, end end_time) puts "Planning between #{begin_time} and #{end_time}" end plan begin: Time.now, end: 2.days.from_now
The second use case is making a method argument more readable inside a method body:
def increment(value, by) # OK, but reads odd value + by end def increment(value, by amount) # Better value + amount end
To the extent possible under law, the persons who contributed to this workhave waived
all copyright and related or neighboring rights to this workby associating CC0 with it.
https://crystal-lang.org/docs/syntax_and_semantics/default_values_named_arguments_splats_tuples_and_overloading.html