JANGUN


Functional Thinking
함수형 사고


지음 : 닐 포드
김재완 옮김



목차

요약
Chapter 1 왜
Chapter 2 전환
Chapter 3 양도하라
Chapter 4 열심히 보다는 현명하게
Chapter 5 진화하라
Chapter 6 전진하라
Chapter 7 실용적 사고
Chapter 8 폴리글랏과 폴리패러다임


요약

객체지향 프로그래밍은 움직이는 부분을 캡슐화하여 코드 이해를 돕고, 함수형 프로그래밍은 움직이는 부분을 최소화하여 코드 이해를 돕는다. – 마이클 페더스

100개의 함수를 하나의 자료구조에 적용하는 것이 10개의 함수를 10개의 자료구조에 적용하는 것보다 낫다. – 앨런 펄리스

함수형 프로그래밍은 값을 추상화의 단위로 바꾸는 기능을 하며 결국 바뀐 값들로 소프트웨어 시스템이 만들어진다.

함수형 프로그래밍 스타일을 고수하려면 수많은 예전 습관을 버리고, OOP로 향하려 하는 습성을 물리쳐야 한다. - 존 카맥

실행할 수 있게 한 다음, 올바로 동작하게 하고, 그 다음 빠르게 실행되도록 만들어라 - 켄트 벡의 테스트 주도 개발 (TDD)


- 값 대신 함수를 사용, 함수를 일급 요소로 취급, 함수가 객체의 필드값이 될 수 있다
- 프로그램을 여러 구성 요소로 분해하고, 추상화된 함수를 이용해서 본래의 기능을 수행하도록 재조립하는 것
- 문제를 (함수라 불리는) 부분으로 나누고 (문제를 ‘동사’나 함수의 그룹으로 나눈다.) 여러 함수를 붙이거나 조립해서 고수준 동작을 만든다.
- 함수 – 추상화의 단위, 동작의 단위
- 점진적으로 함수를 정의하고 저수준 함수에서 만들어진 여러 기능을 사용하는 것
- 레고 블록으로 다양한 물건을 만들듯이 여러 함수를 연결해서 더 풍부한 기능의 함수를 조립하는 다양한 방법
- 프로그램에서 발생하는 상태 변화를 최소화하거나 아예 없애는 것은 복잡성을 줄일 수 있는 방법 중 하나다. (불변성)
- 실용적인 함수형 프로그래밍은 어떤 시스템에서 상태 변화를 완전히 제거하는 것이 아니라 변이가 발생하는 지역을 가능한 최소화하는 것을 목표로 한다.
- 연쇄적으로 함수를 호출하는 함수 체인 (중간 결과를 점점 바꿔가면서 결국에는 최종 솔루션을 만든다)
- 한쪽으로 데이터를 넣으면 반대편으로 완전히 새로운 데이터가 나올 수 있도록 함수들이 파이프라인을 이루고 있을 때
- 새로운 기능을 추가할 때는 새로운 함수가 지역화되고 비파괴적인 데이터 전이 과정에서 어떻게 동작할 것인지를 파악하는 것이 핵심이다.

순수 함수 : 부수 효과가 없는 함수, 오직 인자만을 이용해서 계산 (지역변수?), 외적 요소에 영향을 받는 데이터에 의존하지 않는다, 바디 외부의 상태를 변화시킬 수 없는 구조다
불변성 : 불변성 객체는 생성시에 자신의 값을 채운 이후로는 절대 바뀌지 않는다, 불변성 객체에 행하는 모든 동작의 결과로 새로운 객체가 반환된다.
일급 함수 : 함수를 여느 자료구조처럼 사용할 수 있게 해주는 함수, 함수를 데이터처럼 취급할 수 있는 함수
고계 함수 : 함수를 인자로 받는다. 결과로 함수를 반환한다.

공통된 빌딩 블록
- 필터 : 컬렉션에 블리언 조건을 명시하여 조건을 만족시키는 요소로 이루어진 컬렉션의 부분집합을 리턴
- 맵 : 함수와 컬렉션을 받아서 이 함수를 각 요소에 적용한 후 컬렉션을 리턴
- 폴드/리듀스 : 목록 조작 개념의 특별한 변형. (인덱스를 증가시켜) 컬렉션의 각 값을 순서대로 내부 함수의 인자로 제공

양도하라
- 클로저 : 내부에서 참조되는 모든 인수에 대한 묵시적 바인딩을 지닌 함수, 지연 실행, 상태 대신 문맥
- 커링 : 다인수 함수를 일인수 함수들의 체인으로 바꿔주는 방법, 각각의 논리적 인자에 대응하는 새로운 함수를 반환하는 함수
- 부분적용 : 다인수 함수를 생략될 인수의 값을 미리 정해서 더 적은 수의 인수를 받는 하나의 함수로 변형하는 방법
- 재귀 : 같은 메서드를 반복해서 호출하여 컬렉션을 각 단계마다 줄여가면서 반복 처리, 종료 조건

열심히보다는 현명하게
- 메모이제이션 : 프로그래밍 언어에 내장되어 반복되는 함수의 리턴 값을 자동으로 캐싱해주는 기능. 움직이는 부분을 최소화
- 캐싱 : 매개변수를 사용하여 연산을 할 때마다 그 값을 매개변수를 키 값으로 하는 캐시에 저장한다. 후에 이 함수가 같은 매개변수로 호출되면 다시 연산하는 대신에 캐시의 값을 리턴, 메모리 사용 증대
- 게으름 : 요소들을 한꺼번에 미리 연산하는 것이 아니라, 필요에 따라 하나씩 전달. 효율 증대, 무한 컬렉션

흐름 기반 프로그래밍
- 데이터 값을 입력받아서 비파괴적으로 변형한 다음에 새로운 데이터를 반환하는 식으로 호출 체인이 일어난다



Chapter 1. 왜

어려운 점은 바로 다른 방식으로 사고하는 법을 배우는 것이다.
1.1 패러다임 전환
1.2 언어 트랜드에 발맞추기
1.3 언어/런타임에 제어를 양도하기
malloc에 시달리기에는 인생은 너무 짧다.

1.4 간결함
객체지향 프로그래밍은 움직이는 부분을 캡슐화하여 코드 이해를 돕고,
함수형 프로그래밍은 움직이는 부분을 최소화하여 코드 이해를 돕는다. – 마이클 페더스

객체지향 명령형 프로그래밍 언어에서, 재사용의 단위는 클래스와 그 클래스들이 주고받는 통신 메시지이고, 이는 클래스 다이어그램으로 포착할 수 있다. OOP의 세계에서는 고유한 자료구조를 작성하는 것을 권장한다. 그 자료구조에 특정 동작을 메서드의 형태로 부착해서 말이다.
함수형 프로그래밍 언어는 같은 방식으로 재사용을 달성하려 하지 않고, 최적화된 동작으로 몇몇 자료구조(list, set, map)를 이용하는 방식의 재사용을 선호한다. 개발자가 이런 방법을 잘 사용하려면, 특정 용도로 정의된 방법에 자료구조와 고계함수를 함께 넣어야 한다.

함수형 개발자는 적은 수의 자료구조와 그것들을 잘 이해하기 위한 최적화된 방법을 만들기를 선호한다.
객체지향형 개발자는 항상 새로운 자료구조와 그것에 부착된 메서드를 만든다. 클래스와 통신 메시지를 만드는 것이 지배적인 객체지향 패러다임이다. 모든 자료구조를 캡슐화하면 메서드 수준의 재사용보다는 큰 프레임워크 스타일의 재사용을 선호하게 된다.
함수형 프로그래밍 구조는 세부적인 단계에서 쉽게 코드를 재사용할 수 있게 한다.



Chapter 2. 전환

2.1 일반적인 예제
함수형 프로그래밍은 복잡한 최적화는 런타임에게 맡기고 개발자가 좀 더 추상화된 수준에서 코드를 작성할 수 있게 함으로써, 알고리즘 측면에서 가비지 컬렉션과 동일한 역할을 수행할 것이다.
명령형 프로그래밍이란 상태를 변형하는 일련의 명령들로 구성된 프로그래밍 방식을 말한다. 전형적인 for 루프가 명령형 프로그래밍의 훌륭한 예이다. 초기 상태를 설정하고, 되풀이할 때마다 일련의 명령을 실행한다.

함수형 프로그래밍은 프로그램을 수학 공식을 모델링하는 표현과 변형으로 기술하며, 불변 상태를 지양한다. 함수형 프로그래밍 언어는 명령형 언어와는 다르게 문제를 분류한다.
필터, 변형, 변환 등의 논리적 분류도 저 수준의 변형을 구현하는 함수들이었다. 개발자는 고계 함수에 매개변수로 주어지는 함수를 이용하여 저 수준의 작업을 커스터마이즈할 수 있다. (filter, reduce, map)
- 예를 들어 map(function)에서 map은 고계 함수이고 function은 매개변수로 주어지는 함수에 해당한다. function을 사용하여 각 요소에 변형을 적용한다는 뜻이다.

(예제) '회사 프로세스'에 대한 의사코드
ListOfEmps
-> filter(x.length > 1)
-> transform( x.capitalize )
-> convert( x+ ',' + y )

함수형 사고로의 전환은, 어떤 경우에 세부적인 구현에 뛰어들지 않고 고수준 추상 개념을 적용할 지를 배우는 것이다. 그렇다면 고수준의 추상적 사고로 얻는 이점은 무엇일까?
- 첫째로, 문제의 공통점을 고려하여 다른 방식으로 분류하기를 권장한다는 것이다.
- 둘째로, 런타임이 최적화를 잘할 수 있도록 해준다는 것이다. 어떤 경우에는, 결과가 변하지 않는 한, 작업 순서를 바꾸면 더 능률적이 된다.
- 셋째로, 개발자가 엔진 세부사항에 깊이 파묻힐 경우 불가능한 해답을 가능하게 한다.

2.2 사례 연구: 자연수의 분류
멤버 변수를 없애고 필요한 값들을 매개변수로 넘기고, static method로 처리한다.
물리학에서 에너지는 사용을 위해 저장되는 위치에너지와 에너지 사용에 해당하는 운동에너지로 분류된다. Java 버전 8 이전의 자바 같은 언어에서는 모든 컬렉션이 운동에너지와 같다. 컬렉션은 중간 상태 없이 값을 바로 준다. 함수형 언어에서 스트림은 나중에 사용하기 위해 저장해두는 위치에너지와 같다. 스트림은 자료의 원천(range() 메서드)과 필터 작업과 같은 스트림에 주어진 조건들을 지닌다. 스트림은 개발자가 종료 작업을 통해 값을 요구할 때까지는 위치에너지를 운동에너지로 변환하지 않는다. 스트림은 매개변수로 주고받을 수 있으며, 운동에너지가 되기 전까지는 다른 조건을 덧붙일 수도 있다. 이것을 게으른 평가라고 한다.
고수준의 추상화는 마찰을 없앤다. 한 언어로 아이디어를 표현하는 방식이 곧 문법이다. (foldLeft(), / foldRight() )

2.3 공통된 빌딩블록
- 필터 : 주어진 조건에 맞는 컬렉션의 부분집합을 구하려면 filter를 사용하라.
- 맵 : 컬렉션의 각 요소에 같은 함수를 적용하여 새로운 컬렉션을 만든다. 컬렉션을 그 자리에서 변형하려면 map 함수를 사용하라. 각 함수가 한 줄로 축약될 수 있다면, 함수의 정의를 몇 번의 할당으로 짧게 표현할 수 있다.
- 폴드/리듀스 (catamorphism) : 목록 조작 개념의 특별한 변형. 컬렉션의 요소를 하나씩 다른 함수로 처리할 때는 reduce나 fold를 사용하라.
폴드 연산은 전체 목록의 각 요소를 다음 요소와 결합하여 하나의 결과를 구하는 연산을 뜻한다.
왼쪽 폴드는 초기 값을 시작으로 모든 요소들을 하나씩 왼쪽으로 결합해서 최종 결과를 구한다. 교환법칙이 성립안되는 연산을 고려하여, 왼쪽/오른쪽 폴드를 사용한다

함수형 프로그래밍 같이 다른 패러다임을 익힐 때 어려운 점은 새로운 빌딩블록을 배우고, 풀고자 하는 문제에서 그것이 해법이 될 수 있다는 점을 인지하는 것이다.
함수형 프로그래밍에서는 추상 개념이 많지 않은 대신, 그 각 개념이 범용성을 띤다. (구체성은 고계 함수에 매개변수로 주어지는 함수를 통해 덧붙여진다.)
함수형 프로그래밍은 매개변수와 구성에 크게 의존하므로 ‘움직이는 부분’ 사이의 상호작용에 대한 규칙이 많지 않고, 따라서 개발자가의 작업을 쉽게 해준다.

2.4 골치 아프게 비슷비슷한 이름들 ~ 함수형 프로그래밍 언어들(스칼라, 그루비, 클로저)
- 필터 함수로 컬렉션에 블리언 조건을 명시할 수 있다. 이 함수는 조건을 만족시키는 요소로 이루어진 컬렉션의 부분집합을 리턴한다.
- 맵 함수는 함수와 컬렉션을 받아서 이 함수를 각 요소에 적용한 후 컬렉션을 리턴한다. 리턴된 컬렉션은 개개의 값은 변했지만 필터의 경우와는 달리 원래 컬렉션과 크기는 같다.
- 폴드/리듀스는 주어지는 함수 또는 연산자로 주로 두 개의 매개변수를 받아서 하나의 결과를 리턴하여 목록을 처리한다.
- 리듀스와 같은 고수준의 추상 개념을 어떤 경우에 사용하는가를 터득하는 것이 함수형 프로그래밍을 마스터하는 방법 중의 하나이다.



Chapter 3. 양도하라

가비지 컬렉션 같은 저수준 세부사항의 조작을 런타임에 양도함으로써 찾아야 할 수 많은 오류를 방지해주는 능력이야말로 함수형 사고의 가치라고 하겠다.

3.1 반복 처리에서 고계 함수로
반복 처리 대신에 map과 같은 함수를 사용하여 제어를 포기하는 법을 배워야 한다.
여기서 무엇이 양도되는 지는 확실하다. 고계 함수 내에서 어떤 연산을 할 것인지를 표현하기만 하면, 언어가 그것을 능률적으로 처리할 것이다. (게다가 par라는 변형자를 덧붙이기만 하면 분산처리까지 해준다.)
그렇다고 개발자가 저수준 추상 단계에서 코드가 어떻게 동작하는지 이해하는 것까지 전부 떠넘겨도 된다는 것은 아니다.
많은 경우에 개발자는 stream 같은 추상 개념을 사용할 때 거기에 함축된 의미를 반드시 알아야 한다. -> 사용하는 추상화 단계보다 한 단계 아래를 이해하라.
일례로 많은 개발자들은 자바8의 Stream API를 사용할 때조차 그 안에서 작동하는 포크/조인 라이브러리의 세부사항을 이해해야 좋은 성능을 낼수 있다는 사실에 놀라곤 한다. 하지만 일단 이해만 하면 간단하게 사용할 수 있다.

3.2 클로저
클로저(closusre)란 그 내부에서 참조되는 모든 인수에 대한 묵시적 바인딩을 지닌 함수를 칭한다. 다시 말하면 이 함수(또는 메서드)는 자신이 참조하는 것들의 문맥을 포함한다.
- 클로저는 함수형 언어나 프레임워크에서 코드 블록을 다양한 상황에서 실행하게 해주는 메커니즘으로 많이 쓰인다.
- 클로저를 map()과 같은 고계 함수에 자료 변형 코드 블록으로 전달하는 것이 대표적인 예이다.

클로저란 단어의 어원이 문맥을 포괄함이란 것이다. - 언어로 하여금 상태를 관리하게 하라.
- 클로저는 지연 실행의 좋은 예이다. 클로저 블록에 코드를 바인딩함으로써 그 블록의 실행을 나중으로 연기할 수 있다. 실행 문맥을 클로저 내에 포장하면 적절한 때까지 기다렸다가 실행할 수 있게 된다.
- 명령형 언어는 상태로 프로그래밍 모델을 만든다. 그 좋은 예가 매개변수를 주고받는 것이다.
- 클로저는 코드와 문맥을 한 구조로 캡슐화해서 행위의 모델을 만들 수 있게 해준다. 이렇게 만들어진 클로저는 마치 전통적인 자료구조처럼 주고받을 수 있고, 적절한 시간과 장소에서 실행할 수도 있다.
- 상태 대신 문맥을 잡아라

3.3 커링과 부분 적용
- 커링이나 부분 적용은 함수나 메서드의 인수의 개수를 조작할 수 있게 해준다. 주로 인수 일부에 기본값을 주는 방법을 사용한다. 이를 인수 고정이라고도 부른다.
- 커링(currying)은 다인수 함수를 일인수 함수들의 체인으로 바꿔주는 방법이다.
이것은 그 변형 과정이지 변형된 함수를 실행하는 것을 지칭하는 것은 아니다. 함수의 호출자가 몇 개의 인수를 고정할 지를 결정하며 적은 수의 인수를 가지는 함수를 유도해낸다.
- 부분 적용(partial application)은 주어진 다인수 함수를 생략될 인수의 값을 미리 정해서 더 적은 수의 인수를 받는 하나의 함수로 변형하는 방법이다.
이 방법은 이름이 의미하듯이 몇몇 인수에 값을 미리 적용하고 나머지 인수만 받는 함수를 리턴한다.

커링이나 부분 적용 모두 몇몇 인수의 값만 주면 인수가 몇 개 빠져도 호출할 수 있는 함수를 리턴해준다.
다만 커링은 체인의 다음 함수를 리턴하는 반면에, 부분 적용은 주어진 값을 인수에 바인딩시켜서 인수가 더 적은 하나의 함수를 만들어 준다.
예를 들면, process(x, y, z)의 완전히 커링된 버전은 process(x)(y)(z)이다.

재귀는 같은 메서드를 반복해서 호출하여 컬렉션을 각 단계마다 줄여가면서 반복 처리하는 것을 말한다.
- 이때 조심해야 할 것은 항상 종료 조건을 보장해야 한다는 점이다. 재귀는 프로그래밍 언어의 중요한 흐름을 조명해준다.
- '움직이는 부분'의 관리를 런타임에 양도하는 것이다.
- 재귀는 상태 관리를 런타임에 양도할 수 있게 해준다.
- 커링과 재귀를 결합한 필터 방법이 함수형 언어에서 사용하기에 적격이다.

3.4 스트림과 작업 재정렬
Stream은 여러모로 컬렉션과 흡사하지만 바탕 값(backing value)이 없다. 대신 원천에서 목적지까지 값들이 흐르게끔 한다.
- map()과 filter()는 게으른 함수이다. 즉 이들은 실행을 가능하면 미룬다. 이들은 목적지에서 요구하지 않으면 결과를 내려고 시도하지도 않는다.
- 영리한 런타임은 게으른 작업들을 재정렬할 수 있다. 런타임은 필터를 맵작업 전에 실행하여 게으른 작업을 효율적으로 재정렬할 수도 있다. 단, filter() 같은 함수에 주어진 람다 블록에 부수효과가 없어야 한다.
- 런타임에 최적화를 맡기는 것이 양도의 중요한 예이다. 시시콜콜한 세부사항은 버리고 문제 도메인의 구현에 집중하게 되는 것이다.



Chapter 4. 열심히 보다는 현명하게

4.1 메모이제이션
메모이제이션이란 단어는 영국의 인공지능 연구학자인 도널드 미치가 연속해서 사용되는 연산 값을 함수 레벨에서 캐시하는 것을 지칭하는 것으로 처음 사용하였다.
메모이제이션은 시간이 많이 걸리는 연산을 반복적으로 사용해야 할 때 유용하다. 보편적인 해결방법은 내장 캐시를 설정하는 것이다.
- 주어진 매개변수를 사용하여 연산을 할 때마다 그 값을 매개변수를 키 값으로 하는 캐시에 저장한다.
- 후에 이 함수가 같은 매개변수로 호출되면 다시 연산하는 대신에 캐시의 값을 리턴한다.
- 함수 캐싱은 전형적인 컴퓨터과학의 트레이드오프이다. 이 방법은 좋은 성능을 위해서 메모리를 더 많이 사용한다.

캐싱 방법이 제대로 작동하려면 함수가 순수해야 한다.
- 순수함수란 부수효과가 없는 함수를 말한다.
- 불변 클래스 필드를 참고하지도 않고, 리턴 값 외에는 아무 값도 쓰지 않아야 하며, 주어진 매개변수에만 의존해야 한다.
- java.lang.Math의 모든 메서드가 순수함수의 좋은 예이다. 물론 주어진 매개변수에 대해 항상 값은 값을 리턴하는 함수에 한해서만 캐시된 값을 재사용할 수 있다.

함수형 프로그래밍은 런타임에 재사용 가능한 메커니즘을 만들어서 움직이는 부분을 최소화하는 데 주력한다.
- 메모이제이션은 프로그래밍 언어에 내장되어 반복되는 함수의 리턴 값을 자동으로 캐싱해주는 기능이다.

함수를 메모아이즈하는 것은 메타함수를 적용하는 것이라고 할 수 있다.
언어 설계자들은 반드시 규칙을 지킬 필요가 없기 때문에 그들이 만든 메커니즘이 항상 더 효율적이다.

4.2 게으름
게으른 컬렉션은 그 요소들을 한꺼번에 미리 연산하는 것이 아니라, 필요에 따라 하나씩 전달해준다. 이렇게 하면 몇 가지 이점이 있다.
- 우선 시간이 많이 걸리는 연산을 반드시 필요할 때까지 미룰 수 있게 된다.
- 둘째로, 요청이 계속되는 한 요소를 계속 전달하는 무한 컬렉션을 만들 수 있다.
- 셋째로, 맵이나 필터 같은 함수형 개념을 게으르게 사용하면 효율이 높은 코드를 만들 수 있다.

자바는 게으른 컬렉션을 네이티브로 지원하지 않지만, Iterator를 사용하면 흉내를 낼 수는 있다.

게으른 목록은 몇가지 이점이 있다.
- 첫째, 무한수열을 만들 수 있다. 필요할 때까지 값을 평가하지 않아도 되기 때문에 게으른 컬렉션을 사용하면 무한수열을 모델링할 수 있다.
- 둘째는 저장 시 크기가 줄어든다. 컬렉션 전부를 유지하지 않고 순차적으로 다음 값을 유도할 수 있으니 저장소와 실행 속도를 맞바꿀 수 있다.
- 셋째 이점은 런타임이 좀 더 효율적인 코드를 만들 수 있다는 점이다.



Chapter 5. 진화하라

객체지향 언어는 주로 수많은 자료구조와 거기에 딸린 수많은 연산을 포함한다.
반면에 함수형 언어에는 적은 수의 자료구조와 많은 연산들이 있기 마련이다.

객체지향 언어는 클래스에 종속된 메서드를 만드는 것을 권장하여 반복되는 패턴을 재사용하려 한다.
함수형 언어는 자료구조에 대해 공통된 변형 연산을 적용하고, 특정 경우에 맞춰서 주어진 함수를 사용하여 작업을 커스터마이즈함으로써 재사용을 장려한다.

5.1 적은 수의 자료구조, 많은 연산자
100개의 함수를 하나의 자료구조에 적용하는 것이 10개의 함수를 10개의 자료구조에 적용하는 것보다 낫다. – 앨런 펄리스

객체지향적인 명령형 프로그래밍 언어에서 재사용의 단위는 클래스와 그것들이 주고받는 메시지들이다. 이것들은 클래스 도표로 표시되곤 한다.
– GoF의 디자인 패턴에서는 각 패턴마다 적어도 하나씩의 클래스 도표를 제공한다.
OOP세상에서는 특정한 메서드가 장착된 특정한 자료구조를 개발자가 만들기를 권장한다.

함수형 프로그래밍 언어에서는 몇몇 주요 자료구조(list, set, map)와 거기에 따른 최적화된 연산들을 선호한다.
이런 기계장치에 자료구조와 함수를 끼워 넣어서 특정한 목적에 맞게 커스터마이즈하는 것이다.
함수 수준에서 캡슐화하면 커스텀 클래스 구조를 만드는 것보다 좀 더 세밀하고 기본적인 수준에서 재사용이 가능해진다.

5.2 문제를 향하여 언어를 구부리기
대부분의 개발자들은 복잡한 비즈니스 문제를 자바와 같은 언어로 번역하는 것이 그들의 할 일이라는 착각 속에서 일을 한다.
- 자바나 자바 계열 언어들에는 키워드가 있다. 키워드들은 문법의 기반을 이룬다. 개발자들은 언어 내에서 키워드를 만들 수 없고, 이 키워드들은 개발자들이 사용할 수 없는 것으로서 특별한 의미를 갖는다.
- 자바에서는 함수나 클래스를 만들 수는 있지만 기초적인 빌딩 블록을 만드는 것은 불가능하다
- 자바가 언어로서 유연하지 못하기 때문에, 아이디어를 기존의 고정된 구조에 맞게 주물러야 하기 때문이다.
- 개발자는 문제를 프로그래밍 언어로 번역해야 한다.
- 그런 개발자가 유연한 언어를 접하면 문제를 언어에 맞게 구부리는(바꾸는) 대신 언어를 문제에 어울리게 구부릴 수 있다는 것을 깨닫게 된다.
- 클로저 같은 리스프 계열의 언어에서는 개발자가 언어를 문제 맞게 변형할 수 있다. 즉 언어 설계자와 그 언어를 사용하는 개발자가 만들 수 있는 것들의 경계가 불분명해진다.

문제를 프로그램에 맞추지 말고, 프로그램을 문제에 맞게끔 조정해가라

5.3 디스패치 다시 생각하기
디스패치란 넓은 의미로 언어가 작동 방식을 동적으로 선택하는 것을 말한다.

5.4 연산자 오버로딩
함수형 언어의 공통적인 기능은 연산자 오버로딩이다. 이것은 연산자를 새로 정의하여 새로운 자료형에 적용하고 새로운 행동을 하게 하는 기능이다.

5.5 함수형 자료구조
함수형 언어는 부수효과가 없는 순수함수를 선호한다.
함수형 프로그램이 선호하는 또 하나의 특성은 참조 투명성이다.
- 호출하는 입장에서는 단순한 값 하나를 사용하든, 하나의 값을 리턴하는 함수를 사용하든 다를 바가 없어야 한다.

함수형 언어에서는 다른 두 값을 리턴해야 하는 경우가 종종 있는데, 그런 행동을 모델링하는 자료구조 Either 클래스이다.
- Either는 왼쪽 또는 오른쪽 값 중 하나만 가질 수 있게 설계되었다. 이런 자료구조를 분리합집합(disjoint union)이라고 한다.
- Option 클래스는 적당한 값이 존재하지 않을 경우를 의미하는 none, 성공적인 리턴을 의미하는 some을 사용하여 예외 조건을 더 쉽게 표현한다.



Chapter 6. 전진하라

6.1 함수형 언어의 디자인 패턴
함수형 프로그래밍에서는 전통적인 디자인 패턴들이 다음과 같은 세 가지로 나타난다.
* 패턴이 언어에 흡수된다.
* 패턴 해법이 함수형 패러다임에도 존재하지만, 구체적인 구현 방식은 다르다
* 해법이 있는 다른 언어나 패러다임에 없는 기능으로 구현된다.

6.2 함수 수준의 재사용
구성(composition) (주어진 매개변수와 일급 함수들의 형태로 이루어진다)은 함수형 프로그래밍 라이브러리에서 재사용의 방식으로 자주 사용된다.
함수형 언어들은 객체지향 언어들보다 더 큰 단위로 재사용을 한다. 그러기 위해서는 매개변수로 커스터마이즈되는 공통된 작업들을 추출해낸다.

함수형 프로그래머들도 코드를 재사용하고 싶어 하지만 그들은 다른 빌딩블록을 사용한다.
- 함수형 프로그래밍은 구조물들 간에 잘 알려진 관계(커플링)를 만들기보다는, 큰 단위의 재사용 메커니즘을 추출하려 한다.
- 이런 노력은 객체 간의 관계(모피즘)를 규정하는 수학의 한 분야인 카테고리 이론에 근거를 둔다.

함수형 접근 방법은 목록을 중심으로 재사용 메커니즘을 구축하며, 이때 사용되는 것이 상황에 따라 달라지고 이동 가능한(portable) 코드다.
- 템플릿 메서드 : 일급함수를 사용하면 불필요한 구조물들을 없앨 수 있기 때문에 템플릿 메서드 디자인 패턴을 구현하기가 쉬워진다. 템플릿 메서드는 하나의 알고리즘의 뼈대만 정의하고, 세부절차는 하위 클래스가 주어진 알고리즘의 구조를 바꾸지 않고 정의하게끔 한다.
- 전략 패턴 : 전략 패턴은 각각 캡슐화되어 서로 교환 가능한 알고리즘 군을 정의한다. 이것은 클라이언트에 상관없이 알고리즘을 바꿔서 사용할 수 있게 해주는 패턴이다. 일급 함수를 사용하면 전략을 만들고 조작하기가 쉽다.
- 플라이웨이트 디자인 패턴과 메모이제이션 : 플라이웨이트 패턴은 많은 수의 조밀한 객체의 참조들을 공유하는 최적화 기법이다. 참조들을 객체 풀에 생성하여 특정 뷰를 위해 사용한다. 플라이웨이트는 같은 자료형의 모든 객체를 대표하는 하나의 객체, 즉 표준 객체라는 아이디어를 사용한다. 즉, 소비자 상품의 표준 상품은 같은 종류의 모든 상품을 대표한다. 애플리케이션 내에서 각 사용자를 위해 상품 목록을 모두 생성하기 보다는, 표준 상품들의 목록을 하나 만들고 각 사용자는 원하는 상품의 참조를 가지는 식이다
- 팩토리와 커링 : 커링은 함수의 팩토리처럼 사용된다. 함수형 프로그래밍 언어에서 보편적인 기능은 함수를 여느 자료구조처럼 사용할 수 있게 해주는 일급 함수들이다. 이 기능 덕분에, 주어진 조건에 따라 다른 함수들을 리턴하는 함수를 만들 수 있다. 이것이 사실상 팩토리의 본질이다. 두 개의 수를 더하는 일반적인 함수가 있다면, 커링을 팩토리로 사용하여 매개변수에 1을 더하는 증가를 만들 수 있다.

6.3 구조형 재사용과 함수형 재사용
객체지향 프로그래밍은 움직이는 부분을 캡슐화하여 코드 이해를 돕고, 함수형 프로그래밍은 움직이는 부분을 최소화하여 코드 이해를 돕는다.
- 객체지향의 한 가지 목적은 캡슐화와 상태 조작을 쉽게 하는 것이다. 그래서 객체지향형 추상화는 문제 해결을 위해 주로 상태를 이용한다.
- 함수형 프로그래밍은 구조물들을 연결하기보다는 부분들로 구성하여 움직이는 부분을 최소화하려고 노력한다. 이 미묘한 개념의 차이는 쉽게 느끼기 어렵다.

중복된 코드를 해결하는 방법은 하나의 Factors 클래스로 리팩토링하는 것이다.



Chapter 7. 실용적 사고

7.1 자바 8
자바8 언어 설계자들은, 언어에 고계함수를 그냥 덧붙이지 않고, 교모하게 기존의 인터페이스들이 함수형 기능을 사용할 수 있도록 만들었다.
- Runnable이나 Callable 같이 메서드를 하나만 가지는 인터페이스를 단일 추상 메서드(SAM) 인터페이스라고 부른다.
- SAM은 이동 가능한 코드를 운송하는 메커니즘으로 주로 사용된다.
- 자바8에서는 람다 블록으로 이동 가능한 코드를 잘 구현할 수 있다.
- 함수형 인터페이스라는 영리한 메커니즘은 람다와 SAM이 유용하게 상호작용할 수 있게 해준다.

자바 8에서는 인터페이스에 디폴트 메서드를 지정할 수 있다.
- 디폴트 메서드를 덧붙일 수 있는 기능은 흔히 사용되는 믹신과 유사하고, 자바 언어에 더해진 좋은 기능이다.
- 믹신(mixin) 개념 : 다른 클래스에서 사용될 메서드를 정의하지만, 그 클래스의 상속 체계에 포함되지 않은 클래스를 지칭한다. 언어마다 구현이나 사용법은 다르지만, 공통적으로 코드의 재사용을 권장하고 다중상속의 모호함을 해결해준다.

자바 8의 Optional은 오류로서의 null과 리턴값으로서의 null을 혼용하는 것을 방지한다.

자바 8의 스트림은 많은 함수형 기능을 가능하게 한다. 스트림은 여러모로 컬렉션과 비스하지만 다음과 같은 중요한 차이점이 있다.
- 스트림 작업은 중간 작업 또는 종결 작업이다. 중간 작업은 새 스트림을 리턴하고 항상 게으르다. 종결작업은 스트림을 순회하여 값이나 부수효과를 낳는다.
* 스트림은 값을 저장하지 않으며, 종결 작업을 통해 입력에서 종착점까지 흐르는 파이프라인처럼 사용된다.
* 스트림은 상태를 유지하지 않는 함수형으로 설계되었다. 스트림 작업은 최대한 게으르게 한다.
* 무한한 스트림이 가능하다.
* Iterator 인스턴스처럼 스트림은 사용과 동시에 소멸되고, 재사용 전에 다시 생성해야 한다.

7.2 함수형 인프라스트럭처
함수형 아키텍처는 불변성이 그 중심에 있고, 이를 최대한 사용하려 시도한다. 함수형 프로그래머처럼 사고하려면 불변성을 받아들이는 것이 중요하다.
불변 상태와 테스트는 직접적인 상호 관계가 있다.
* 모든 필드는 final로 선언한다.
* 클래스를 final로 선언해서 오버라이드를 방지하라
* 인수가 없는 생성자를 제공하지 말라
* 적어도 하나의 생성자를 제공하라
* 생성자 외에는 변이 메서드를 제공하지 말라



Chapter 8. 폴리글랏과 폴리패러다임

함수형 프로그래밍 패러다임은 문제와 그것을 푸는 데 사용되는 도구에 관한 사고의 틀이라고 할 수 있다. 많은 현대 언어들은 폴리패러다임(멀티패러다임)이다.
이들은 객체지향, 메타프로그램, 함수형, 절차형 등 몇 가지 서로 다른 프로그래밍 패러다임을 지원한다.
8.1 함수형과 메타프로그램의 결합
8.2 메타프로그래밍을 통한 자료형의 매핑
8.3 멀티패러다임 언어의 결과

8.4 문맥 대 구성
함수형 사고는 프로젝트에 사용하는 언어에만 적용되는 것이 아니고, 도구 디자인에도 영향을 준다.
구성 위주의 시스템은 서로 연결할 수 있는 세밀한 부분들로 이루어진다.
문맥 위주의 시스템은 토대, 곧바로 사용할 수 있는 방식, 토대를 이용한 문맥의 이해를 제공한다. 따라서 문맥 위주의 시스템은 초기 사용 시 어려움을 준다.

8.5 함수형 피라미드