TIL: [함수형 자바스크립트 프로그래밍] 책을 읽고

함수형 자바스크립트 프로그래밍

함수형이라는 것은 뭐지?? 파라미터로 함수 값을 받을 수 있고, 리턴 값으로 함수를 뱉을 수 있는 것을 말하는 것 인듯!

그렇다면 이런 짓을 왜 할까?

이는 로직 간에 독립성을 증가시켜준다. 유연함이 있다.

우선 절차지향적으로 작성된 코드를 함수형으로 변경하면서 이의 참된 실용성을 알아보자

var users = [
    {id: 1, name: "ID", age: 32},
    {id: 2, name: "HJ", age: 27},
    {id: 3, name: "GT", age: 24},
    {id: 4, name: "DV", age: 22},
    {id: 5, name: "FB", age: 29},
    {id: 6, name: "DE", age: 31},
    {id: 7, name: "HI", age: 28},
]

// var temp_users = [];
// for (var i = 0, len - users.length; i < len; i++) {
//     if (users[i].age < 30) temp_users.push(users[i]);
// }

function filter(list, predicate) {
    var new_list = [];
    for (var i = 0, len = list.length; i < len; i++) {
        if (predicate(list[i])) new_list.push(list[i]);
    }
    return new_list;
}

// filter 사용
var users_under_30 = filter(users, function(user) {return user.age < 30});

filter 함수는 인자로 list와 predicate를 받는다. predicate 함수가 참일 때만 new_list.push를 실행한다.

filter는 predicate 함수가 무슨 일을 하는지 전혀 모른다. 오직 predicate의 결과에만 의존한다. 마지막에는 new_list를 리턴한다.

이전 값의 상태를 변경하지 않고 새로운 값을 만드는 식으로 값을 다루는 것은 함수형 프로그래밍의 매우 중요한 관점 중 하나이다.

filter라는 함수는 항상 동일하게 동작하는 함수다. 이는 한 가지 로직을 가졌다는 이야기이다. new_list는 이 함수에서 최초로 만들어져 외부의 상황이나 상태에도 관계가 없다.

new_list가 완성될 때까지는 외부에서 어떠한 접근도 할 수 없기 때문에 filter의 결과도 달라질 수 없다. new_list가 완성되고 나면 new_list를 리턴해버리고 filter 함수도 종료된다. new_list가 외부로 전달되고 나면 new_list와 filter와의 연관성도 사라진다.

또한 filter의 if는 predicate의 결과에만 의존한다.

함수형 프로그래밍에서는 항상 동일하게 동작하는 함수를 만들고 보조 함수를 조합하는 식으로 로직을 완성한다. 내부에서 관리되고 있는 상태를 두지 않고 오직 넘겨진 인자에만 의존하도록 한다. 동일한 인자가 들어오면 항상 동일한 값을 리턴하도록 하고, 보조 함수 역시 인자이고 보조 함수 역시 상태를 변경하지 않으면 보조 함수를 받은 함수는 항상 동일한 결과를 만드는 함수가 된다.

객체지향과 함수지향은 별반 차이가 없다. 단지 객체를 확장하느냐 아니면 객체를 다루는 함수를 늘리느냐 차이이고, 추상화의 단위가 클래스이냐 함수이냐의 차이이다.

// 기존 코드
var ages = [];
for (var i = 0, len=users_under_30.length; i<len; i++) {
    ages.push(users_under_30[i].age);
}

// 바꾼 코드
function map(list, iteratee) {
    var new_list = [];
    for (var i = 0, len=list.length; i<len; i++) {
        new_list.push(iteratee(list[i]));
    }
    return new_list;
}

new_list에 어떤 것을 push 할지에 대해 iteratee 함수에게 위임하였다. 이를 filter 함수와 같이쓰면, filter 함수의 결과가 배열이므로 map의 첫 번째 인자인 list로 바로 활용할 수 있다. 즉, 함수의 리턴값을 바로 다른 함수의 인자로 사용하면 변수 할당을 줄일 수 있다.

var ages = map(
    filter(users, function(user) {return user.age < 30}),
    function(user) {return user.age;});

// 변수 할당 없애기
function log_length(value) {
    console.log(value.length);
    return value;
}

console.log(log_length(
    map(
        filter(users, function(user) {return user.age < 30}),
        function(user) {return user.age;}
    )
));

filter 함수는 predicate를 통해 값을 필터링하여 map에게 전달하고 map은 받은 iteratee를 통해 새로운 값들을 만들어 log_length에 전달한다.

고차 함수

고차 함수란, 함수를 인자로 받거나 함수를 리턴하는 함수를 말한다. 보통 고차 함수는 함수를 인자로 받아 필요한 때에 실행하거나 클로저를 만들어 리턴한다.

javascript를 쓸 때 underscore.js라는 라이브러리에 _.identity라는 아이가 있다. 이 것은 다른 고차 함수와 조합하는 식으로 많은 유용한 함수들을 만들 수 있다. _.some, _.every와도 같이 다른 함수들과 활용하여 유용하게 쓰인다.