메서드 인자

메서드 인자 (Method arguments)

이 페이지는 메서드의 매개변수와 호출 인자를 공식적으로 다루는 명세예요. 메서드 정의가 어떤 구성 요소로 이루어지는지, 호출 인자가 어떻게 매개변수에 매칭되는지, 스플랫(splat)·이중 스플랫(double splat)·외부 이름(external name)이 어떻게 동작하는지 자세히 살펴볼게요.

출처: Crystal 공식 문서 — Method arguments

본문

이것은 메서드 매개변수와 호출 인자의 공식 명세예요.

메서드 정의의 구성 요소 (Components of a method definition)

메서드 정의는 다음으로 이루어져요:

  • 필수 및 선택적 위치 매개변수(positional parameter)
  • 이름이 비어 있을 수 있는 선택적 스플랫 매개변수
  • 필수 및 선택적 이름 매개변수(named parameter)
  • 선택적 이중 스플랫 매개변수

예를 들어:

def foo(
  # These are positional parameters:
  x, y, z = 1,
  # This is the splat parameter:
  *args,
  # These are the named parameters:
  a, b, c = 2,
  # This is the double splat parameter:
  **options,
)
end

그것들 각각은 선택 사항이라, 메서드는 이중 스플랫 없이, 스플랫 없이, 이름 매개변수 없이, 위치 매개변수 없이도 존재할 수 있어요.

메서드 호출의 구성 요소 (Components of a method call)

메서드 호출도 여러 부분으로 이루어져요:

foo(
  # These are positional arguments
  1, 2,
  # These are named arguments
  a: 1, b: 2
)

추가로 호출 인자는 스플랫(*)이나 이중 스플랫(**)을 가질 수 있어요. 스플랫은 Tuple을 위치 인자로 확장하고, 이중 스플랫은 NamedTuple을 이름 인자로 확장해요. 여러 개의 인자 스플랫과 이중 스플랫도 허용돼요.

호출 인자가 메서드 매개변수에 매칭되는 방법 (How call arguments are matched to method parameters)

메서드를 호출할 때 호출 인자를 메서드 매개변수에 매칭하는 알고리즘은 다음과 같아요:

  • 먼저 위치 호출 인자를 위치 메서드 매개변수와 매칭해요. 그 개수는 기본값이 없는 위치 매개변수의 수 이상이어야 해요. 이름이 있는 스플랫 매개변수가 있다면 (이름이 없는 경우는 아래에서 설명할게요) 더 많은 위치 인자가 허용되고 그것들은 튜플로 캡처돼요. 위치 인자는 스플랫 매개변수를 지나서 매칭되지 않아요.
  • 그다음 이름 인자를 메서드의 아무 매개변수와 (스플랫 매개변수 앞이든 뒤든) 이름으로 매칭해요. 그 매개변수가 이미 위치 인자로 채워져 있다면 오류예요.
  • 여분의 이름 인자는 이중 스플랫 메서드 매개변수에 NamedTuple로 담겨요. 그 매개변수가 없다면 오류예요.

스플랫 매개변수가 이름을 갖지 않으면, 더 이상 위치 인자를 넘길 수 없다는 뜻이고, 뒤따르는 매개변수들은 모두 이름 인자로 넘겨야 해요. 예를 들어:

# 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

하지만 스플랫 매개변수에 이름이 있어도, 그 뒤에 오는 매개변수는 이름 인자로 넘겨야 해요:

# 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

별(star)을 맨 앞에 놓아서 메서드가 이름 인자만 받도록 (그것들만 나열하도록) 만들 수도 있어요:

# A method with two required named parameters: 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

별 뒤의 매개변수도 기본값을 가질 수 있어요. 즉, 이름 인자로는 넘겨야 하지만 필수는 아니라는 뜻이에요 (그래서: 선택적 이름 매개변수):

# x is a required named parameter, y is an optional named parameter
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

스플랫 매개변수 뒤의 (기본값이 없는) 매개변수는 이름으로 넘겨야 하기 때문에, 서로 다른 필수 이름 매개변수를 가진 두 메서드는 오버로드돼요:

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

위치 매개변수는 항상 이름으로도 매칭할 수 있어요:

def foo(x, *, y)
end

foo 1, y: 2    # OK
foo y: 2, x: 3 # OK

외부 이름 (External names)

메서드 매개변수에 외부 이름을 지정할 수 있어요. 외부 이름은 이름 인자로 인자를 넘길 때 쓰는 이름이고, 내부 이름은 메서드 정의 안에서 그 매개변수를 가리킬 때 쓰는 이름이에요:

def foo(external_name internal_name)
  # here we use internal_name
end

foo external_name: 1

이것은 두 가지 용도를 다룹니다.

첫 번째 용도는 키워드를 이름 매개변수로 쓰는 거예요:

def plan(begin begin_time, end end_time)
  puts "Planning between #{begin_time} and #{end_time}"
end

plan begin: Time.local, end: 2.days.from_now

두 번째 용도는 메서드 본문 안에서 메서드 매개변수를 더 읽기 좋게 만드는 거예요:

def increment(value, by)
  # OK, but reads odd
  value + by
end

def increment(value, by amount)
  # Better
  value + amount
end

더 알아보기