mostly-adequate-guide-ko
  • README
  • Chapter 01: 그래서 우린 대체 뭘 할건가요?
    • 소개하기
    • 간단하게 만나보기
  • Chapter 02: 일급 함수 (First Class Functions)
    • 훑어보기
    • 왜 일급을 선호하나요?
  • Chapter 03: 순수 함수를 통해 진정한 행복을 만나보세요
    • 순수해지기
    • Side Effects May Include...
    • 8th Grade Math
    • The Case for Purity
    • In Summary
  • Chapter 04: Currying
    • Can't Live If Livin' Is without You
    • More Than a Pun / Special Sauce
    • In Summary
    • Exercises
  • Chapter 05: Coding by Composing
    • Functional Husbandry
    • Pointfree
    • Debugging
    • Category Theory
    • In Summary
    • Exercises
  • Chapter 06: Example Application
    • Declarative Coding
    • A Flickr of Functional Programming
    • A Principled Refactor
    • In Summary
  • Chapter 07: Hindley-Milner and Me
    • What's Your Type?
    • Tales from the Cryptic
    • Narrowing the Possibility
    • Free as in Theorem
    • Constraints
    • In Summary
  • Chapter 08: Tupperware
    • The Mighty Container
    • My First Functor
    • Schrödinger's Maybe
    • Use Cases
    • Releasing the Value
    • Pure Error Handling
    • Old McDonald Had Effects...
    • Asynchronous Tasks
    • A Spot of Theory
    • In Summary
    • Exercises
  • Chapter 09: Monadic Onions
    • Pointy Functor Factory
    • Mixing Metaphors
    • My Chain Hits My Chest
    • Power Trip
    • Theory
    • In Summary
    • Exercises
  • Chapter 10: Applicative Functors
    • Applying Applicatives
    • Ships in Bottles
    • Coordination Motivation
    • Bro, Do You Even Lift?
    • Operators
    • Free Can Openers
    • Laws
    • In Summary
    • Exercises
  • Chapter 11: Transform Again, Naturally
    • Curse This Nest
    • A Situational Comedy
    • All Natural
    • Principled Type Conversions
    • Feature Envy
    • Isomorphic JavaScript
    • A Broader Definition
    • One Nesting Solution
    • In Summary
    • Exercises
  • Chapter 12: Traversing the Stone
    • Types n' Types
    • Type Feng Shui
    • Effect Assortment
    • Waltz of the Types
    • No Law and Order
    • In Summary
    • Exercises
  • Chapter 13: Monoids bring it all together
    • Wild combination
    • Abstracting addition
    • All my favourite functors are semigroups.
    • Monoids for nothing
    • Folding down the house
    • Not quite a monoid
    • Grand unifying theory
    • Group theory or Category theory?
    • In summary
    • Exercises
  • Appendix A: Essential Functions Support
    • always
    • compose
    • curry
    • either
    • identity
    • inspect
    • left
    • liftA2
    • liftA3
    • maybe
    • nothing
    • reject
  • Appendix B: Algebraic Structures Support
    • Compose
    • Either
    • Identity
    • IO
    • List
    • Map
    • Maybe
    • Task
  • Appendix C: Pointfree Utilities
    • add
    • append
    • chain
    • concat
    • eq
    • filter
    • flip
    • forEach
    • head
    • intercalate
    • join
    • last
    • map
    • match
    • prop
    • reduce
    • replace
    • reverse
    • safeHead
    • safeLast
    • safeProp
    • sequence
    • sortBy
    • split
    • take
    • toLowerCase
    • toString
    • toUpperCase
    • traverse
    • unsafePerformIO
Powered by GitBook
On this page
  • 훑어보기
  • 왜 일급을 선호하나요?

Chapter 02: 일급 함수 (First Class Functions)

훑어보기

우리가 함수를 "일급"이라고 부를 때는, 함수가 다른 것들과 같다는 걸 강조하고자 함입니다... 여기에서 '다른 것'은 일반 클래스라고 보면 됩니다. 우리는 함수를 여느 데이터 타입처럼 다룰 수 있어요. 전혀 특별할 게 없습니다. 이것들은 배열에 담길 수도 있고, 함수 인자로 전달될 수도, 변수에 할당될 수도 있습니다.

이건 JavaScript 기초지식이지만 이렇게 언급하는 이유는, Github에 올라오는 코드들만 잠깐 살펴봐도 사람들이 이 개념에 대해서 단체로 무시하거나 집단 망각을 한것처럼 보이기 때문입니다. 가상의 예를 들어볼까요? 그럽시다.

const hi = name => `Hi ${name}`;
const greeting = name => hi(name);

여기에서 hi를 에워싸고 있는 함수 래퍼 greeting은 완전히 불필요합니다. 왜그럴까요? 왜냐하면 JavaScript에서 함수들은 callable 이기 때문입니다. 만약 hi의 끝에 ()가 붙어있다면 실행되고 실행결과를 반환할거에요. 그렇지 않은 경우에는 단순히 변수에 저장되어있던 함수를 반환할 겁니다. 분명히 하기 위해서 예제로 보여드릴게요:

hi; // name => `Hi ${name}`
hi("jonas"); // "Hi jonas"

greeting이 그저 hi를 완전히 동일한 인자를 갖고 호출하는 것에 지나지 않기 때문에, 우리는 이렇게 단순화시킬 수 있습니다:

const greeting = hi;
greeting("times"); // "Hi times"

다시 말해서, hi는 이미 하나의 인자만을 기대하는 함수인데 왜 단순히 hi를 완전히 동일한 인자로 호출하는 또다른 함수를 만들어야 할까요? 이건 정말 말도 안되는 일이에요. 마치 무더운 7월의 끝자락에 바람을 막기위해 두꺼운 파카를 껴입고 아이스크림을 먹는 것과 같습니다.

이건 불쾌하게 장황할 뿐 아니라, 함수를 둘러싼 함수는 평가를 지연시킬 뿐입니다.(왜 그런지는 나중에 살펴보겠습니다)

계속 나아가기 전에 이 개념을 단단히 이해하는것이 정말 중요하기때문에, 어느 npm 패키지에서 발굴해낸 몇가지 재미있는 예제를 통해 탐구해보겠습니다:

// 흠..
const getServerStuff = callback => ajaxCall(json => callback(json));

// 똑똑
const getServerStuff = ajaxCall;

세상이 이렇게 ajax 코드로 오염되어있습니다. 여기에서 왜 둘이 동등한지를 보여줄게요:

// 이 라인이
ajaxCall(json => callback(json));

// 이 라인과 동일합니다
ajaxCall(callback);

// 그러니 일단 getServerStuff 를 한번 리팩토링 하고 
const getServerStuff = callback => ajaxCall(callback);

// 한번 더 리팩토링 합니다
const getServerStuff = ajaxCall; // <-- 보세요, ()가 없죠

자 제군들, 이런일이 벌어진거였어요. 제가 왜 이렇게 끈질긴지를 이해시켜드리기 위해 한 가지 더 살펴볼게요.

const BlogController = {
  index(posts) { return Views.index(posts); },
  show(post) { return Views.show(post); },
  create(attrs) { return Db.create(attrs); },
  update(post, attrs) { return Db.update(post, attrs); },
  destroy(post) { return Db.destroy(post); },
};

이 웃기는 컨트롤러는 굉장히 부풀려져있어요. 우리는 이렇게 재작성할 수 있습니다:

const BlogController = {
  index: Views.index,
  show: Views.show,
  create: Db.create,
  update: Db.update,
  destroy: Db.destroy,
};

...또는 View 와 DB을 묶어주는 역할외에는 하는 것이 없으므로 아예 폐기하는 것도 방법입니다.

왜 일급을 선호하나요?

좋아요, 일급 함수를 선호하는 이유에 대해서 파고들어봅시다. 우리가 getServerStuff 와 BlogController 예제에서 보았듯이, 아무런 이점도 없이 단순히 관리해야할 불필요한 코드의 양만을 늘려주는 참조 레이어를 새로 추가하는 일은 정말 쉬웠죠.

게다가 만약 감싸진 내부 함수를 수정해야할 일이 생긴다면, 우리는 바깥 함수까지 수정해야만 할겁니다.

httpGet('/post/2', json => renderPost(json));

만약 우리가 httpGet이 발생가능한 err를 전달할 수 있게 변경하려면, 코드베이스를 돌아다니며 "glue" 부분을 수정해줘야합니다.

// httpGet을 호출하는 위치를 전부 찾아내어 명시적으로 err를 전달하도록 수정하기
httpGet('/post/2', (json, err) => renderPost(json, err));

만약 우리가 일급 함수로 작성했었더라면, 훨씬 적은 변경으로도 수정이 가능했을겁니다:

// renderPost는 얼마나 많은 수의 인자를 받던지간에 httpGet에서 호출됩니다 
httpGet('/post/2', renderPost);

불필요한 함수를 제거하는 것 외에도, 우리는 참조할 인자들에 이름을 지어줘야 합니다. 네이밍은 여러분도 잘 아시듯이 꽤 중요한 주제죠. 특히 코드베이스가 작성된지 시간이 지날수록, 요구사항이 변경될수록 인자들에 잘못된 이름이 적용되어있을 가능성이 높아집니다.

같은 의미임에도 불구하고 서로 다른 여러 이름들이 사용되는건 프로젝트에 혼란을 낳는 흔한 요인이죠. 이건 일반적인 코드에서도 마찬가지로 적용됩니다. 예를 들어, 이 두 함수들은 완전히 동일한 일을 하지만 하나는 굉장히 범용적이고 재사용하기 좋아보이게 느껴집니다:

// 현재 blog에 특정적인 코드
const validArticles = articles =>
  articles.filter(article => article !== null && article !== undefined),

// 향후 코드에서도 활용될 수 있는 코드
const compact = xs => xs.filter(x => x !== null && x !== undefined);

구체적인 이름을 사용하면, 특정한 데이터에 우리를 가두어버리게 됩니다 (여기에서는 articles가 되겠네요). 이런 일은 꽤 빈번히 발생하며 사람들이 불필요하게 코드를 재작성하게 만드는 요인 중 하나입니다.

한가지 알아두어야 할것이 있는데, 객체 지향 코드를 작성할 때와 마찬가지로 여러분은 this가 늘 당신의 숨통을 끊어버리려 기회를 노리고 있다는 사실을 기억해야합니다. 만약 this를 사용하는 함수를 일급으로서 호출하면, 우리는 추상화의 분노에 당해버릴겁니다.

const fs = require('fs');

// 무섭군요
fs.readFile('freaky_friday.txt', Db.save);

// 조금 덜 무섭군요
fs.readFile('freaky_friday.txt', Db.save.bind(Db));

스스로에게 bind 시켜준 이상, Db는 prototype의 쓰레기 코드에 자유롭게 접근할 수 있습니다. 저는 더러운 기저귀마냥 this 사용하기를 꺼려합니다. 함수형 코드를 작성할 때에는 전혀 사용할 필요가 없어요. 어쨌거나, 외부 라이브러리를 마주하는 경우에는 우리가 정말 험난한 세상에서 살아나가고 있다는 걸 조용히 인정해야할지도 모릅니다.

누군가는 this가 성능 최적화를 위해 꼭 필요하다고 말할지도 몰라요. 만약 당신이 그런 마이크로-최적화를 필요로 한다면, 이 책은 당신에게 맞지 않으니 조용히 덮어주세요.

여기까지 되었으면, 이제 우린 다음으로 넘어갈 준비가 되었습니다.

PreviousChapter 01: 그래서 우린 대체 뭘 할건가요?NextChapter 03: 순수 함수를 통해 진정한 행복을 만나보세요

Last updated 2 years ago

Chapter 03: Pure Happiness with Pure Functions