• 함수형 사고 (닐 포드 지음, 김재완 옮김)

  • 자바스크립트로 알아보는 함수형 프로그래밍 (ES5) (인프런, 유인동)


  • 새로운 프로그래밍 패러다임의 문제점은 새로운 언어를 배우는 것이 아님. 어려운 점은 바로 다른 방식으로 사고하는 법을 배우는 것.

  • 함수형 코드를 작성하기 위해서는 문제에 접근하는 방식의 전환이 필요하다.

  • 함수형 프로그래밍 같이 다른 패터다임을 익힐때 어려운 점은

    1. 새로운 빌딩블록을 배우고,

    2. 풀고자 하는 문제에서 그것이 해법이 될 수 있다는 점을 인지하는 것

  • 대부분의 개발자들은 복잡한 비지니스 문제를 자바와 같은 어어로 번역하는 것이 그들의 할 일이라는 착각 속에서 일을 한다.

    자바가 언어로서 유연하지 못하기 때문에, 아이디어를 기존의 고정된 구조에 맞게 주물러야 하기 때문이다.

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

  • 고차함수들로만 코딩하면 좋은점은 코드 분리가 쉽다. (구조적 리팩토링이 쉽다.)

Note
문법적 설탕syntactic sugar

같은 의미의 코드를 간결하게 작성할 수 있게 해주는 언어 문법의 일종. 간결 표기법이라고도 함.

  • 자바가 메모리 관리 작업을 쉽게 해줬다면, 함수형 프로그래밍 언어는 다른 빌딩블록들을 고수준 추상적 개념으로 대체해준다.

  • OOP의 세계에서는 고유한 자료구조를 작성하는 것을 관장하나. 그 자료구조에 특정 통작을 메서드의 형태로 부착해서 말이다. 함수형 프로그래밍 언어는 같은 방식으로 재사용을 달설하여 하지 않고 최적화된 동작으로 몇몇 자료구조(list, set, map)를 이용하는 방식의 재사용을 선호한다.

  • 함수형 개발자는 적은 수의 자료구조와 그것들을 잘 이해하기 위한 최적화된 방법을 만들기를 선호한다. 객체지향형 개발자는 항상 새로운 자료구조와 그것에 부착된 메서드를 만든다.

  • 자바를 사용할 때는 null 때문에 문제가 많이 발생한다. null은 제대로 된 리턴 값인가, 아니면 값이 없다는 뜻인가? 스칼라를 포함한 많은 함수형 언어는 Option 클래스를 사용하여 이런 모호함을 피한다.

  • 다른 함수형 언어들과 마찬가리고 스칼라는 null을 리턴한 것을 피하기 위해 관례적으로 Option을 사용한다.

함수형 프로그래밍

"함수형 프로그래밍은 애플리케이션, 함수의 구성요소, 더 나아가서 언어 자체를 함수처럼 여기도록 만들고, 이러한 함수 개념을 가장 우선순위에 놓는다."

— 클로저 프로그래밍의 즐거움
마이클 포거스
/* 데이터(객체) 기준 */
dock.moveLeft();
duck.moveRight();
dog.moveLeft();
dog.moveRight();

/* 함수 기준 */
moveLeft(dog);
moveRight(duck);
moveLeft({ x: 5, y: 2 });
moveRight(dog);

"함수형 사고방식은 문제의 해결 방법을 동사(함수)들로 구성(조합)하는 것"

— 클로저 프로그래밍의 즐거움
마이클 포거스
  • 함수형프로그래밍은 값을 변경해나가면서 상태를 바꿈

  • 함수형 프로그래밍은 부수 효과side effect를 미워하고 조합성을 강조하는 프로그래밍 패러다임

    • 부수 효과를 미워한다. → 순수 함수를 만든다. → 오류를 줄이고 안정성을 높힘

    • 조합성을 강조한다 → 모듈화 수준을 높힌다. → 생산성을 높힌다.

  • 좋아지는 하드웨어 성능, 컴파일러

순수 함수

  • 순수 함수pure function는 부수 효과가 없는 함수. 수학적 함수.

    function add(a, b) {
        return a + b;
    }
  • 들어온 인자가 같으면 항상 동일한 결과를 리턴.

    순수 함수 X, 동일한 결과를 주었을 때 상황에 따라 다른 결과를 리턴하는 함수.
    var c = 10; // 이것이 상수라면 순수함수, 아니라면 순수함수라고 볼 수 없음
    function add2(a, b) {
        return a + b + c;
    }
    
    console.log(add2(10, 2));
    c = 20;
    console.log(add2(10, 2));
  • 함수가 받은 인자외에 다른 어떠한 상태에 영향을 끼치지 않은 함수.

    순수 함수 X, 부수효과를 일으키는 함수. 외부의 상태를 변경, 들어온 인자의 상태를 변경.
    var c = 10
    function add3(a, b) {
        c = b; // 부수효과
        return a + b;
    }
    
    console.log(c) // 10
    console.log(add3(20, 30)) // 50. 결과는 동일하지만...
    console.log(c) // 30. 외부 상태가 변경됨
    console.log(add3(20, 30)) // 50
    순수 함수 X, 외부 상태를 변경하는 함수.
    val obj1 = { val: 10 };
    function add3(obj, b) {
        obj.val += b;
    }
    console.log(obj1.val); // 10
    add4(obj1, 20)
    console.log(obj1.val); // 30
  • 순수 함수는 평가 시점이 중요하지 않음.

순수 함수
val obj1 = { val: 10 };
function add5(obj, b) {
    return { val: obj.val + b }
}
console.log(obj1.val)
val obj2 = add5(obj1, 20)
console.log(obj1.val);
console.log(obj2.val);

일급 함수

  • first class function. 함수를 값으로 다룰 수 있다는 것

  • JS에서는 함수가 일급함수

  • 언제 평가해도 상관 없는 순수 함수와 일급 함수를 통해 함수의 조합성을 높혀나가는 것.

function add(a, b) { return a + b; };

val f1 = function(a) { return a * a; };
console.log(f1);

val f2 = add

function f3(f) {
    return f();
}
console.log(f3(function() { return 10; })); // 10
console.log(f3(function() { return 20; })); // 20

고차 함수

  • filter: 주어진 조건에 맞는 컬렉션의 부분집합 구하기

  • map: 컬렉션을 그 자리에서 변형하기

  • recude, fold: 컬렉션의 요소를 하나씩 다른 함수로 처리하기

Note
캐터모피즘catamorphism

카테고리 이론의 개념으로 목록을 접어서 다른 형태로 만드는 연산을 총칭한다.

Note
플래트닝falttening

평탄화. 중첩을 펼치는 연산.

클로저

  • 모든 함수형 언어는 클로저를 포함한다.

  • 클로저closure란 그 내부에서 참조되는 모든 인수에 대한 뭊시적 바인딩을 지닌 함수를 칭한다.

    • 이 함수는 자신이 참조하는 것들의 문맥context를 포함한다.

  • 클로저란 단어의 어원리 문맥을 포괄함enclosing context이란 점에서 이 작업의 내용을 추측할 수 있을 것이다.

  • 클로저는 지연 실행deferred execution의 좋은 예다.

function add_maker(a) {
    return function(b) { // (1)
        return a + b;
    }
}
  1. a 를 기억하는 클로저. 또한, 순수함수

커링과 부분 적용

  • 커링과 부분 적용은 20세기 수학자인 해스컬 커리Haskell Curry 등의 작업을 통해 수학에서 유래한 언어 기술이다.

  • 커링이나 부분 적용은 함수나 메서드의 인수의 개수를 조작할 수 있게 해준다.

  • 주로 인수 일부에 기본값을 주는 방법을 사용한다.
    이를 인수 고정이라고도 부른다.

Note
커링currying

다인수multi-argument 함수를 일인수single-argument 함수들의 체인으로 바꿔주는 방법. 이것은 변형 과정이나 변형된 함수를 실행하는 것을 지징하는 것은 아님.

Note
부분 적용partial application

주어진 다인수 함수를 생랼될 인수의 값을 미리 정해서 더 적은 수의 인수를 받는 하나의 함수로 변형하는 방법. 이 방법은 이름이 의미하듯이 몇몇 인수에 값을 미리 적용하고 나머지 인수만 받는 함수를 리던한다.

  • 커링은 체인의 다음 함수를 리턴.
    부분 적용은 주어진 값을 인수에 바인딩시켜서 인수가 더 적은 하나의 함수를 만듬.

  • process(x,y,z) 의 완전히 커링된 버전은 process(x)(y)(z).

    첫 인수만 커링을 하면 process(x) 의 리턴 값은 인수가 하나인((y)) 또 하나의 함수다. 이 함수의 리턴 값은 또 하나의 일인수 함수다.

  • 부분 적용을 사용하며 변환하면 인수 숫자가 적은 함수가 남는다.

    process(x,y,z) 의 인수 하나를 부분 적용하면 인수 두 개짜리인 process(y,z) 가 된다.

메모이제이션

  • 메모아이즈된 함수의 결과가 매개변수 이외의 어떤 것에라도 의존하면 기대하는 결과를 항상 얻을 수는 없다.

  • 메모아이즈된 함수는 부수효과가 없어야 한다.

Note
메모이제이션memorization
  • 함수의 연산 결과를 저장해두었다가, 같은 입력이 주어지면 연산을 하지 않고 리턴하는 최적화 기법.

  • 영국의 인공지능 연구학자인 도널드 미치Donald Michie가 연속해서 사용되는 연산 값을 함수 레벨에서 캐시하는 것을 지징하는 것으로 처음 사용.

  • 글자 그대로 해석하면 '메모리에 넣기’라는 의미.

  • 라틴어 memorandum(기억되어야 할 것)에서 파생.

  • 동일한 계산을 반복할 때, 이전 계산 값을 메모리에 저장함(캐싱?)으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술. 동적 계획법의 핵심.

게으름

  • 표현의 평가를 가능한 최대로 늦추는 기법인 게으른 평가는 함수형 프로그래밍 언어에서 많이 볼 수 있는 기능이다.

  • 엄격한지strict 혹은 관대한지nonstrict(게으른지)

  • 게으름의 이점

    1. 무한수열을 만들 수 있음. 값을 평가하지 않아도 되기 때문.

    2. 저장시 크기가 줄어듬. 컬렉션 전부를 유지하지 않고 순차적으로 다음 값을 유도할 수 있으니.

    3. 런타임이 좀 더 효율적은 코드를 만들 수 있음

Note
추상 구문 트리abstract syntax tree, AST

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

— 앨런 펄리스(Alan Perlis)

연산자 오버로딩

  • 함수형 언어의 공통적인 기능은 연산자 오버로딩이다.

  • 스칼라에서 연산자는 특별한 이름을 가진 메서드에 불과하다.

  • 새로운 언어를 만들지 말고, 연산자 오버로딩을 통해 문제 도메인을 향하여 언어를 구부리자.

함수형 자료구조

  • 대부분의 함수형 언어들은 예외 패터다임을 지원하지 않기 때문에 개발자는 다른 방법으로 오류 조건을 표현해야 한다.

  • 예외는 많은 함수형 언어가 준수하는 전제 몇 가지를 깨뜨린다.

    • 함수형 언어는 부수효과가 없는 순수함수를 선호한다. 예외를 발생시키는 것은 예외적인 프로그램 흐름을 야기하는 부수효과다.

    • 함수형 언어들은 주로 값을 처리하기 때문에 프로그램의 흐름을 막기보다는 오류를 나타내는 리턴 값에 반응하는 것을 선호한다.

      Note

      '이펙티브 코틀린 - 아이템 7’에서 실패는 나타내는 sealed 클래스(일반적으로 Failuer)를 사용하라는 얘기가 있는데, 함수형 언어에서 예외를 던지는 것보다 '오류를 나타내는 리턴 값’이 이와 비슷한 의미지 않을까?

Either 클래스

  • 함수형 언어에서 다른 두 값을 리턴해야하는 경우가 종종 있는데 그런 행동을 모델링하는 자료구조.

  • Either 는 왼쪽 또는 오른쪽 값 중 하나만 가질 수 있게 설계됨.

  • 이런 자료구조를 분리합집합disjoint union이라고 함

  • 자바에서 Optional ?
    코틀린에서 Pair 는 아닌 것 같음. 부분집합? 구현체? 라고도 볼 수 있어 보임.

  • 함수형의 보편적인 관례에 따라 Either 클래스의 왼쪽이 예외, 오른쪽이 결과 값.

  • 여러 프레임워크에 Either 와 유사한 Option 이란 클래스가 있다.
    OptionEither 의 간단한 부분집합이라고 볼 수 있음.
    Either 는 어떤 값이든 저장할 수 있는 반면, Option 은 주로 성공과 실패의 값을 저장하는데 쓰임.

모나드

디자인패턴

  • 함수형 언어계의 어떤 이들은 디자인 패턴이 개념 자체에 결함이 있기 때문에 함수형 프로그래밍에서는 필요가 없다고 주장한다. 패턴의 좁은 정의만 볼 때에는 일리가 있는 말이다. 아지만 그런 주장은 패턴의 사용보다는 의미론에 국한된 것이다.

  • 디자인 패턴의 개념은 아직도 건재하다. 하지만 다른 패러다임에서 패턴들은 다른 형태로 나타난다.

자바

Note
SAM, single abstract method

단일 추상 메서드. Java에서 @FunctionalInterface, Kotlin에서 fun interface (ref)

  • Runnable, Callable 같이 메서드를 하나만 가지는 인터페이스를 단일 추상 메서드라 부름

  • 하나의 함수형 인터페이스는 하나의 SAM을 포함하며, 여러 개의 디폴트 메서드도 함께 포함할 수 있다.

  • 함수형 인터페이스는 기존의 SAM 인터페이스가 전통적인 익명 클래스 인트턴스를 람다 블록으로 대체할 수 있게 해준다.

  • 자바 8에서는 인터페이스에 디폴트 메서드를 지정할 수 있다.
    (디폴트 메서드는 순수 함수가 아닐까?)

  • 자바 8읠 디폴트 메서드는 자바에 믹신을 제공한다. 이 덕분에 Arrays, Collections 같은 클래스들을 JDK에서 없앨 수 있게 된다. 이것들은 적당한 자리를 찾기 못한 정적 메서드의 모임에 불과했기 때문이다.

Note
mixin

다른 클래스에서 사용될 메서드를 정의하지만, 그 클래스의 상속 체계에 포함되지 않은 클래스를 지칭한다. 코드의 재사용성을 권장하고 다중상속의 모호함을 해결해준다.

믹신은 플레이어버스 언어에서 시작되었는데, 아이스크림 가게에서 영감을 받았다고 한다. 평범한 맛의 아이스크림에 손님이 원하는 '믹스-인'(사탕 조각, 땅콩 등)을 가미해서 판매했다.

  • 스트림은 여러모로 컬렉션과 비슷하지만 다음과 같은 차이점이 있다.

    • 스트림은 값을 저장하지 않으며, 종결 작업을 통해 입력에서 종착점까지 흐르는 파이프라인처럼 사용된다.

    • 스트림은 상태를 유지하지 않는 함수형으로 설계되었다.

    • 스트림 작업은 최대한 게으르게 한다.

    • 무한한 스트림이 가능하다.

    • Iterator 인스턴스처럼 스트림은 사용과 동시에 소멸되고, 재사용 전에 다시 생성해야 한다.

함수형 인프라스트럭처

아키텍처

  • 함수형 아키텍처는 불변성이 그 중심에 있고, 이를 최대한 사용하려 시도한다.

  • 불편 클래스는 자바에서 주로 걱정해야 하는 것들을 많이 없애준다.

  • 테스트의 진정한 목적은 변이mutation를 확인하는 것이고, 변이가 많을수록 테스트가 많이 필요하게 된다.

  • 불편 클래스들은 생성 시에만 변화가 있기 때문에 테스트가 간단한다.

  • 불변 객체는 변경을 허용하지 않으므로 컬렉션의 키로도 안성맞춤이다.

  • 불변 객체는 가동적으로 스레드 안전하기 때문에 동기화의 문제가 없다.

CQRS

  • 그레그 영(Greg Young)이 개념을 도입했고, 마틴 파울러가 영향력 있게 그 개념을 기술했다.

  • 전통적인 어플리케이션 아키텍처에서는 모델 부분이 비지니스 규칙이나 검증을 담당한다.

    • 읽기/쓰기의 홉합된 의미를 모델 전역에서 관리해야하고 이것이 시스템을 복잡하게 한다.

  • CQRS는 읽기와 명령 부분을 분리함으로써 아키텍처의 일부를 단순화한다.

    • 쿼리 모델

      • 개발자가 불변성을 가정할 수 있기 때문에 쿼리 쪽의 논리는 훨씬 단순하다.

    • 커맨드 모델

  • CQRS를 사용하면 트랜잭션형보다는 최종 일관성 모델로 전환해야 할 것이다.

Note
최종 일관성(eventual consistency)

최종 일관성 분산 컴퓨팅 모델은 모델을 업데이트하는 데 정해진 시간 제한을 요구하지 않는다. 대신 새로운 업데이트가 없는 한, 최종적으로 모델이 일관성을 갖추기만 하면 된다.

트랜잭션 모델이 ACID에 의존하는 반면, 최종 일관성 모델은 BASE를 중요하게 여긴다.

  • ACID: 원자성atomic, 일관성consistent, 고립성isolated, 지속성durable

  • BASE: 기존 가용성basically available, 부드러운 상태soft state, 최종 일관성eventual consistency

데이터베이스

  • 관계형 데이터베이스에서 파괴적 업데이트를 하는 의도는 무엇일까?

    • 다시 말해, 업데이트할 때마다, 예쩐 값은 없어지고 새 값으로 대체된다는 뜻

  • 데이터가 증가하는 것을 억제하고 저장 장소를 극대화하기 위해서다.

  • 하지만 이제 세상이 바뀌었다. 컴퓨팅 자원은 이제 가격이 낮다.

폴리글랏

  • 함수형 프로그래밍 패러다임은 문제와 그것을 푸는 데 사용되는 도구에 관한 사고의 틀이라고 할 수 있다.

  • 많은 현대 언어들은 폴리패러다임(혹은 멀티패러다임)이다.

  • 이들을 객체지향, 함수형, 절차형 등 몇 가지 서로 다른 프로그래밍 패러다임을 지원한다.

  • 멀티패러다임 언어는 개발자가 각 패러다임을 적재적소에 사용할 수 있다는 엄청난 이점이 있다.

  • 객체지향 언어에서는 코드 재사용이 구조 중심이지만, 함수형 언어에서는 구성과 고계함수 중심이다.