함수 리터럴

자바는 람다 함수 문법으로, 함수를 이름 없이도 만들어 쓸 수 있습니다.

익명 함수

자바

Stream.of(1, 2, 3).map(x -> x + 1).forEach(System.out::println);
// => 2, 3, 4

자바 8부터 람다 함수 문법이 추가되었습니다. 위 예제에서 x -> x + 1로, x를 인수로 받아서, 1을 더한 값을 반환하는 함수를 그 자리에서 만들었으며, 이 함수에는 따로 이름을 붙이지 않았기 때문에, 익명 함수라고도 부릅니다.

스칼라

List(1, 2, 3).map(x => x + 1).foreach(println) // => 2, 3, 4

스칼라에서도 같은 방법으로 익명 함수를 만듭니다. -> 기호 대신 => 기호를 쓰는 점이 다릅니다.

스칼라 익명 함수 파라미터 줄임 표현

스칼라에서는 익명 함수를 줄여 표현하는 방법이 있습니다.

List(1, 2, 3).map(x -> x + 1)

위 스칼라 구문은 아래와 똑같습니다.

List(1, 2, 3).map(_ + 1)

밑줄(_)이 첫 번째 파라미터를 의미하는 익명 함수 표현식입니다.

익명 함수를 값으로 다루기

다른 메서드에 람다 함수를 넘길 때 쓰는 경우가 가장 흔합니다만, 평범한 값으로도 함수를 다룰 수 있습니다. 평범한 값으로 다룬다는 뜻은, 어떤 변수에 담는다거나, 다른 메서드에 파라미터로 전달한다거나, 아니면 메서드의 반환 값으로도 사용할 수 있다는 의미입니다. 함수를 마치 문자열 다루듯이 평범한 값으로 취급할 수 있다는 뜻이죠.

자바

Function<Integer, Integer> add1 = x -> x + 1;
Stream.of(1, 2, 3).map(add1).forEach(System.out::println);

익명 함수 i -> i + 1add1이라는 변수에 담았습니다. 그 타입은 Function<Integer, Integer>이고, 이는 Integer 하나를 받아서 Integer를 반환하는 함수 인터페이스를 뜻합니다. 그다음 map메서드에 이 add1라는 변수를 그대로 전달했습니다.

스칼라

val add1 = (x: Int) => x + 1
List(1, 2, 3).map(add1) // =>  List(2, 3, 4)

스칼라에서도 똑같은 방식으로 사용합니다. 타입을 구체적으로 명시하지 않고, 타입 추론으로 add1 함수를 만들었습니다.

함수 합성하기

자바

Function<Integer, Integer> add1 = x -> x + 1;
Function<Integer, Integer> square = x -> x * x;
Function<Integer, Integer> add1Square = square.compose(add1);

Stream.of(1, 2, 3).map(add1).map(square).forEach(System.out::println);
Stream.of(1, 2, 3).map(add1Square).forEach(System.out::println);
// => 4, 9, 16

1을 더하는 함수 add1과, 제곱을 하는 square 함수를 연이어 map 메서드에 전달하면, 최종적으로는 \( (x + 1)^2 \) 값을 구할 수 있습니다. 수학시간에 배운 합성 함수 \( g \circ f \)를 만드는 compose 메서드를 활용해서, 새로운 익명 함수를 만들어서 한 번에 처리해도 됩니다.

스칼라

val add1 = (x: Int) => x + 1
val square = (x: Int) => x * x
val add1Square = square.compose(add1)

List(1, 2, 3).map(add1).map(square) // => List(4, 9, 16)
List(1, 2, 3).map(add1Square)       // => List(4, 9, 16)

스칼라에서도 마찬가지로 함수를 합성해서 쓸 수 있습니다.