Function

Function

함수와 함께 작업하기 위한 함수들의 모음이에요. **익명 함수(anonymous function)**는 보통 fn을 사용해 만들어요.

iex> add = fn a, b -> a + b end
iex> add.(1, 2)
3

익명 함수는 여러 개의 절(clause)을 가질 수도 있어요. 모든 절은 같은 개수의 인자를 기대해야 해요.

iex> negate = fn
...>   true -> false
...>   false -> true
...> end
iex> negate.(false)
true

출처: Function

본문

캡처 연산자 (The capture operator)

캡처 연산자 &/1를 쓰면 공개된 모듈 함수를 캡처해서 익명 함수인 것처럼 넘겨다닐 수 있어요.

iex> add = &Kernel.+/2
iex> add.(1, 2)
3

iex> length = &String.length/1
iex> length.("hello")
5

현재 모듈 안의 정의를 캡처할 때는 모듈 접두사를 생략할 수 있어요. 예를 들어 &my_fun/2처럼요. 그런 경우 캡처된 함수는 공개 함수(def)일 수도 있고 비공개 함수(defp)일 수도 있어요.

캡처 연산자는 또한 최소 하나의 인자를 기대하는 익명 함수를 만드는 데도 쓸 수 있어요.

iex> add = &(&1 + &2)
iex> add.(1, 2)
3

이런 경우 캡처 연산자를 쓰는 건 fn을 쓰는 것과 다를 바 없어요.

내부 함수와 외부 함수

&String.length/1처럼 모듈 안에 정의된 지점을 가리키는 함수를 **외부 함수(external function)**라고 불러요. 그 외의 모든 함수는 내부(로컬) 함수로, 항상 그것을 정의한 파일이나 모듈에 묶여 있어요.

이 모듈의 함수들 외에도 Kernel에는 동적 개수의 인자로 함수를 호출하는 apply/2가 있어요. 그리고 어떤 값이 함수인지(is_function/1), 특정 arity의 함수인지(is_function/2)를 확인하는 함수도 있어요.

정보 타입

information/0 타입은 함수에 대한 정보를 조회할 때 쓸 수 있는 아이템들을 나타내요.

@type information() ::
  :arity
  | :env
  | :index
  | :module
  | :name
  | :new_index
  | :new_uniq
  | :pid
  | :type
  | :uniq

함수들

capture/3 — 주어진 함수를 캡처해요. 컴파일러가 인라인해요.

iex> Function.capture(String, :length, 1)
&String.length/1

identity/1 — 입력 값을 그대로 돌려줘요. 변환 함수에 익명 함수로 넘겨줄 수 있어요.

iex> Function.identity("Hello world!")
"Hello world!"

iex> ~c"abcdaabccc" |> Enum.sort() |> Enum.chunk_by(&Function.identity/1)
[~c"aaa", ~c"bb", ~c"cccc", ~c"d"]

iex> Enum.group_by(~c"abracadabra", &Function.identity/1)
%{97 => ~c"aaaaa", 98 => ~c"bb", 99 => ~c"c", 100 => ~c"d", 114 => ~c"rr"}

iex> Enum.map([1, 2, 3, 4], &Function.identity/1)
[1, 2, 3, 4]

info/1 — 함수에 대한 정보를 담은 키워드 리스트를 돌려줘요. 모든 종류의 함수(로컬/외부)에서 돌려주는 키는 다음과 같아요.

  • :type:local(익명 함수) 또는 :external(이름 있는 함수)이에요.
  • :module — 익명 함수라면 함수가 정의된 모듈, 이름 있는 함수라면 함수가 가리키는 모듈을 나타내는 아톰이에요.
  • :arity — (정수) 함수가 호출될 인자의 개수예요.
  • :name — (아톰) 함수의 이름이에요.
  • :env — 환경(자유 변수)의 리스트예요. 이름 있는 함수의 경우 항상 빈 리스트예요.

fun이 익명 함수일 때(즉 타입이 :local일 때) 다음 추가 키들이 돌아와요.

  • :pid — 함수를 처음 만든 프로세스의 PID예요.
  • :index — (정수) 모듈 함수 테이블로의 인덱스예요.
  • :new_index — (정수) 모듈 함수 테이블로의 인덱스예요.
  • :new_uniq — (바이너리) 이 함수에 대한 고유 값이에요. 전체 모듈의 컴파일된 코드에서 계산돼요.
  • :uniq — (정수) 이 함수에 대한 고유 값이에요. 전체 모듈의 컴파일된 코드에서 계산돼요.

참고로 이 함수는 디버깅 목적으로만 사용해야 해요. 컴파일러가 인라인해요.

iex> fun = fn x -> x end
iex> info = Function.info(fun)
iex> Keyword.get(info, :arity)
1
iex> Keyword.get(info, :type)
:local

iex> fun = &String.length/1
iex> info = Function.info(fun)
iex> Keyword.get(info, :type)
:external
iex> Keyword.get(info, :name)
:length

info/2 — 함수에 대한 특정 정보를 돌려줘요. 반환 정보는 {info, value} 모양의 두 원소 튜플이에요. 어떤 함수든 :module, :name, :arity, :env, :type 아톰 중 하나를 물어볼 수 있어요. 익명 함수라면 :index, :new_index, :new_uniq, :uniq, :pid에 대한 정보도 있어요. 이름 있는 함수의 경우 이 항목들의 값은 항상 :undefined 아톰이에요. 각 반환 값에 대한 자세한 내용은 info/1을 보세요. 컴파일러가 인라인해요.

iex> f = fn x -> x end
iex> Function.info(f, :arity)
{:arity, 1}
iex> Function.info(f, :type)
{:type, :local}

iex> fun = &String.length/1
iex> Function.info(fun, :name)
{:name, :length}
iex> Function.info(fun, :pid)
{:pid, :undefined}

더 알아보기

  • 캡처 연산자와 관련된 자세한 내용은 Kernel.SpecialForms.&/1 문서를 참고해 보세요.
  • 동적 호출을 하려면 Kernel.apply/2를, 함수 여부 확인은 is_function/1, is_function/2를 보면 돼요.
  • 익명 함수의 다양한 쓰임새는 Enum 모듈의 고차 함수 예시들에서 익힐 수 있어요.