함수 기본

이 챕터는 Elm 기본 문법 중에서도 중요한 다음 것들을 다룹니다: 함수, 함수 시그내쳐, 부분 적용, 파이프 연산자

함수

Elm 은 두 종류의 함수를 지원합니다:

  • 익명 함수
  • 이름이 있는 함수

익명 함수

익명 함수란 이름에서 보듯이, 이름이 없는 함수입니다:

\x -> x + 1

\x y -> x + y

백슬래시와 화살표 사이에 함수의 인자들이, 화살표 오른편에는 함수의 내용이 들어갑니다.

이름이 있는 함수

Elm 의 이름이 있는 함수는 이렇게 생겼습니다:

add1 : Int -> Int
add1 x =
  x + 1
  • 첫 줄은 함수 시그내쳐입니다. Elm 에서는 필수는 아니지만, 함수의 목적을 명확히 하기 때문에 가급적 사용합니다.
  • 이하는 함수의 구현입니다. 구현은 시그내쳐의 아래에 따라와야 합니다.

이 시그내쳐는 이렇게 말하고 있습니다: 인자로 정수 (Int) 를 받고 결과로 또다른 정수를 리턴함.

호출은 이렇게 합니다:

add1 3

Elm 에서 우리는 (괄호 대신) 공백 으로 함수 호출을 표현합니다.

다음은 또 다른 이름 있는 함수입니다:

add : Int -> Int -> Int
add x y =
  x + y

이 함수는 두 인자를 받고 (둘 다 Int) 다른 Int 를 리턴합니다. 이렇게 호출할 수 있습니다:

add 2 3

인자가 없는 경우

Elm 에서 인자가 없는 함수는 상수입니다:

name =
  "Sam"

함수가 적용되는 원리

위에서 본 것처럼 두 인자를 받는 함수는 이런 식입니다:

divide : Float -> Float -> Float
divide x y =
    x / y

우리는 이 시그내쳐를 보고 두 실수를 인자로 받아 다른 실수를 리턴하는 함수라고 알 수 있습니다:

divide 5 2 == 2.5

하지만, 완전히 맞는 말은 아닙니다. Elm 에서는 사실 모든 함수는 하나의 인자만을 받고, 다른 함수를 리턴하기도 합니다. 위 함수를 사용해서 설명해 보겠습니다.

-- 이렇게 호출하면:

divide 5 2

-- 이와 같습니다:

((divide 5) 2)

-- First `divide 5` is evaluated.
-- 앞의 `divide 5` 가 처리됩니다.
-- 인자 `5` 가 `divide` 에 적용되고, 중간 함수가 리턴됩니다.

divide 5 -- -> 중간 함수

-- 이 중간 함수를 `divide5` 라고 부릅시다.
-- 이 중간 함수의 시그내쳐와 구현을 볼 수 있다면 다음과 같을 겁니다:

divide5 : Float -> Float
divide5 y =
  5 / y

-- 이는 `5` 가 이미 적용되어 있는 함수라고 볼 수 있습니다.

-- 그리고 다음 인자 `2` 가 적용됩니다.

divide5 2

-- 그러면 최종 결과가 리턴됩니다.

우리가 괄호를 쓸 필요가 없는 이유는 함수 호출이 왼쪽 결합 형태이기 때문입니다.

괄호로 묶기

다른 함수 호출의 결과를 함수에 전달할 때는 괄호로 호출을 묶어줄 필요가 있습니다:

add 1 (divide 12 3)

여기서는 divide 12 3add 의 두번째 인자로 전달되고 있습니다.

다른 많은 언어에서는 이렇게 쓰겠지만요:

add(1, divide(12, 3))

부분 적용

위에서 보았듯이 모든 함수는 하나의 인자만을 받고 다른 함수나 결과를 리턴합니다. 그 말은 add 같은 함수를 호출할 때도 하나의 인자만 사용할 수 있다는 것이죠. (예: add 2부분 적용된 함수 를 리턴함) 이 리턴된 함수는 Int -> Int 의 시그내쳐를 가지고 있습니다.

add 2 는 첫 인자에 2 가 적용된 다른 함수를 리턴합니다. 두번째 인자를 리턴된 함수에 적용하면 2 + 두번째 인자 가 됩니다:

add2 = add 2
add2 3 -- result 5

부분 적용은 여러분의 코드를 더 읽기 쉽게 해 주고, 함수간 상태를 전달하는 데 있어 Elm 에서 매우 유용합니다.

파이프 연산자

위에서 보았듯이 함수 호출은 중첩 가능합니다:

add 1 (multiply 2 3)

이건 단순한 예지만, 더 복잡해지면 어떨 지 봅시다:

sum (filter (isOver 100) (map getCost records))

이런 코드는 안쪽에서부터 계산되는 형태라 읽기 불편합니다. 파이프 연산자는 이런 경우에 더 읽기 쉬운 방식으로 표현 할 수 있게 해 줍니다:

3
    |> multiply 2
    |> add 1

이는 앞에서 설명한 부분 적용과 밀접한 관계가 있습니다. 예시에서 3 은 부분 적용된 함수 multiply 2 에 전달됩니다. 그 결과는 다시 또다른 부분 적용 함수 add 1 에 전달됩니다.

파이프 연산자를 사용해서 위 복잡한 예시를 이렇게 쓸 수 있습니다:

records
    |> map getCost
    |> filter (isOver 100)
    |> sum

results matching ""

    No results matching ""