함수형 사고 (Functional Thinking) – 닐 포드

1.
객체지향의 틀안에서 느꼈던 갈증을 해결해 준는 책. 이 책만 읽는 다면 함수형 프로그래밍이 마침내 찾아낸 유토피아 같겠지만 모든 것에는 장단점이 있는법. 모든 문제를 하나의 언어로 해결 할 필요가 없는 것처럼 하나의 패러다임 만으로 문제를 해결할 필요도 없는 것이겠지.

2.
객체지향은 매번 새로운 자료구조(상태)를 만들어내지만 이는 각 상태에 대한 별도의 조작함수를 매번 제공해야 하므로 복잡도가 높아진다. 함수형은 리스트, 맵과 같은 보편적인 자료구조를 기반으로 이를 조작할 수 있는 다양한 함수를 만드는 방식으로 접근하여 재사용이 쉽다.

3.
객체지향에서 상속으로 추상화된 한계로 인해 특정기능을 다룰 수 없게되는 경우가 얼마나 많던가, 섣부른 추상화로 인해 다시 구조를 바꾸겠다고 리펙토링을 하며 새로이 만들어낸 추상화는 또다른 한계를 결정짓곤 한다. (조엘이 말하는 허술한 추상화의 법칙) 구성(Composition)을 기반으로 한 함수형 접근 방식에서 이 문제 해결의 실마리를 보는 것 같다.

4.
함수형 접근방법이 해결한 3가지 갈증

a. 오류 처리: 예외도 맘에 들지 않았고, 리턴값처리도 맘에 들지 않았다. Either나 optional이 그 대안이 될 것
b. TDD에 쓰기 유용하다는 점: TDD에서는 다른 객체와의 연관성을 끊고 테스트 하기 위해 DI와 Mock을 사용한다. 이게 여간 번거로운 것이 아니다. Functional에서는 애초에 주입할 state가 없다. 가급적 파급효과가 없는 함수들을 작성한다.
c. 도메인 특화 언어 사용의 편리함: 간단한 배치 스크립트를 대체하려던 파이썬 스크립트 조차도 루프가들어가고 자료형이 나오면 한눈에 들어오지 않는다. 연산자 오버로딩, 상속을 사용하지 않는 다형성(이를 통한 디스패치)들을 통해 구현이 간단해진다.

책 속으로…

폴드나 리듀스의 경우 컬렉션에 매번 새로운 함수(같은 이항 함수이지만 첫 매개변수가 달라진다)를 적용하게 된다. 모든 요소에 같은 일항 함수를 적용하는 map과는 구별된다 -50

명령형 언어는 상태로 프로그래밍 모델을 만든다. 그 좋은 예가 매개변수를 주고 받는 것이다. 클로저는 코드와 문맥을 한 구조로 캡슐화해서 행위의 모델을 만들 수 있게 해준다. 이렇게 만들어진 클로저는 마치 전통적인 자료구조처럼 주고받을 수도 있고, 적절한 시간과 장소에서 실행할 수도 있다. – 66

객체지향에서는 this의 문맥이 멤버함수 호출에 숨겨져 있다고 볼수 있다. 마찬가지로 커링/부분 적용 역시 이를 구현하는 것이다. – 75

배열을 head와 tail로 접근하자
배열 순회를 재귀로 처리하기 쉬워진다
-76

꼬리호출 최적화: 재귀 호출이 함수에서 마지막 단계이면, 런타임이 스택을 증가시키지 않고 스택에 놓여 있는 결과를 교체할 수 있다. – 80

100개의 함수를 하나의 자료구조에 적용하는 것이 10개의 함수를 10개의 자료구조에 적용하는 것 보다 낫다 – 111

도메인 특화 언어(DSL)을 구축해라: 대부분의 개발자들은 복잡한 비즈니스 문제를 자바와 같은 언어로 번역하는 것이 그들의 할 일이라는 착각속에서 일을 한다. 자바가 언어로서 유연하지 못하기 때문에, 아이디어를 기존의 고정된 구조에 맞게 주물러야 하기 때문이다. 그런 개발자가 유연한 언어를 접하면 문제를 언어에 맞게 구부리는 대신 언어를 문제에 어울리게 구부릴 수 있다는 것을 깨닫게 된다. – 114

코드 블럭을 직접 다룰 수 있게 되면 Strategy pattern을 구현하는데 필요한 보일러플레이트코드(인터페이스를 만들고 구현클래스를 만드는 등의)를 만들지 않아도 된다. 그냥 두가지 다른 알고리즘 코드를 리스트로 담아두고 돌려 쓸수 있다. -154

함수형 프로그래밍을 한다는 것은 상속보다는 구성을 선호하여 커플링을 최소화 하는 것 -168

함수형 언어는 불편형 자료에 연산을 적용하지만 자바8은 기존의 가변형 자료를 활용하여 연산해야 하기 때문에 가변 리듀스작업을 하는 메서드가 포함되었다. Collect() -173

디츨러의 법칙: 모든 엑세스 플젝트는 사용자가 원하는 80%의 기능은 빠르게 만들고 쉽지만, 다음 10%는 가능하지만 어렵고, 마지막 10%는 장착된 추상화 밑으로 더 파고들 수 없기 때문에 불가능하다. 하지만 사용자는 항상 100%를 원하기 때문에, 이 프로젝트들은 결국 실패한다.(조엘이 말하는 허술한 추상화의 법칙) – 193

구성과 문맥: 문맥위주의 시스템은 추상화를 통해 토대를 제공함으로서 초기 사용시 어려움을 줄여준다. 하지만 장착된 추상화가 제공하지 않는 기능은 사용할 수 없다. 구성 위주의 시스템은 초기 사용이 그리 용이하지는 않지만 좀 더 세밀한 부분까지 제어할 수 있으므로 결국은 더 유용하게 된다. 제대로 설계된 구성 위주의 시스템은 캡슐화된 모듈 내부에서 좁은 의미의 국지적 문맥을 제공한다.(일례로 메이븐의 문맥위주의 시스템이지만 Rake는 구성위주의 시스템이다. 몇몇 헬퍼들을 제공할 뿐 사용자가 원하는대로 작성해서 구성한다. ) – 194

구성위주의 시스템은 범용언어 위에 DSL을 구축한다. – 196

언어의 종류를 나누는 두 축. 강한 타이핑 vs 약한 타이핑, 동적타이핑 vs 정적타이핑. 강한 타입 변수는 자신의 타입을 알고있기 때문에 리플렉션이나 인스턴스 검사가 가능하며, 이런 성질이 유지된다. 약한 타이핑 언어는 이런 의식이 덜하다. 예를 들어 C는 정적 약한 타이핑 언어이다. C의 변수는 여러 방법으로 해석이 가능한 비트의 컬렉션에 불과하다. – 196

함수형에서 런타임에게 일을 떠넘기는 방법

1.반복문 대신에 추상화된 고차 함수 사용. Map, reduce, filter
2.클로저: 상태의 관리를 런타임에게 넘기고 개발자는 행위만 다룬다.
3.커링과 부분 적용
- Currying: 여러 인수의 함수를 한개 인수의 함수들의 체인으로 변환. 리턴값은 새로운 커링
- Partial application: 여러 인수의 함수에서 생략될 값을 미리 정함. 리턴값은 동일한 함수
4. 재귀: 언어가 상태를 관리하게 한다.
- 언어가 메서드 호출 시마다 리턴 값을 스택에 쌓아가면서 관리한다.
- 배열 순회를 재귀로 처리하면 움직이는 부분의 관리를 런타임에게 맡길 수 있게 된다.
5.스트림과 작업 재정렬: 스트림에서 map, filter가 호출된 순서로 실행되지 않을 수 있다.
- 스트림은 원본데이터(입력된 배열)를 목적지(collect함수)까지 값이 흐르게한다. 이 과정에서 map과 filter는 collect에서 호출되기 전까지는 지연평가된다.
- map이 먼저 실행될지 filter가 먼저 실행될지를 런타임이 알아서 최적화할 수 있다.

 

함수형 사고 (Functional Thinking) – 닐 포드”의 1개의 생각

  1. 폴리글랏프로그래밍을 읽다보니 이 닐포드라는 사람이 함수형프로그래밍에서 꽤나 유명한 사람이었다.

Brent 에 답글 남기기 응답 취소

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중

This site uses Akismet to reduce spam. Learn how your comment data is processed.