왜 하스켈을 배워야 하는가? | Kwang Yul Seo

 
하스켈은 배우기 어려운 언어로 정평이 나있습니다. 순수(pure) 함수지연 연산(lazy evaluation), 하스켈 타입 시스템(System F와 type class 등등), 병렬 처리 등은 그저 시작에 불과합니다.
하스켈을 제대로 쓰기 위해서는 카테고리 이론에서 빌려온 Functor, Applicative Functor, Monad 같은 개념들을 익혀야 합니다. IO뿐만 아니라 대부분의 하스켈 코드가 이런 개념들을 활용해서 작성되어 있기 때문에 하스켈을 제대로 쓰려면 중요 개념들을 제대로 이해하고 있어야 합니다.
Monad 몰라도 IO 코드는 충분히 작성할 수 있고 MaybeEither, List 타입들을 사용할 수 있습니다만, Monad를 모를 거면 굳이 왜 하스켈을 배워야 할까요? 이미 Clojure, F#, Scala 등 대중적인 함수 언어들이 나와 있고, Java 8이나 C#, Swift, ES6에도 함수 언어의 요소가 상당히 반영되어 있는 상황에서 굳이 더 어렵기만 한 하스켈을 배워야 할 이유가 없어 보입니다.
우리가 사용하는 대중적인 프로그래밍 언어들은 어차피 모두 튜링 컴플리트(Turing complete)하기 때문에 계산가능성(computability)에 있어서는 차이가 없습니다. 한 언어가 계산할 수 있는 것을 다른 언어가 계산하지 못하지 않는다는 뜻입니다. 하스켈이 강력하다고 하지만 본질적으로 C++이나 Java가 할 수 없는 일을 할 수 있지는 않습니다.
왜 하스켈을 배워야 하는지 설명하기에 앞서 프로그래밍의 본질이 무엇인지 질문을 던져볼 필요가 있습니다. 프로그래밍은 기본적으로 컴퓨터에 일을 시키는 것입니다. “메모리 x에 있는 값을 레지스터에 로드해서 1을 더하고 다시 메모리 x에 저장해라”도 일종의 프로그램입니다.
하지만 이게 끝은 아닙니다. 우리가 푸는 문제는 메모리에서 값을 꺼내 1을 더한 다음 다시 메모리에 저장하는 수준이 아니라 현실 세계에서 발생하는 훨씬 더 크고 복잡한 문제이기 때문입니다. 그래서 프로그래밍의 본질은 한 번에 풀 수 없는 크고 복잡한 문제를 작은 문제들로 나누어서 해결하고 그렇게 나온 결과물들을 조합하여 다른 문제를 해결하는 것을 말합니다.
여기서 좋은 프로그램의 가장 중요한 특성으로 조합성(composability)이 등장합니다. 우리는 계속해서 크고 복잡한 문제를 풀어야 하고, 또한 비슷하지만 조금은 다른 문제들을 풀어야 합니다. 앞서 만들어 놓은 산출물을 쉽게 조합하여 새로운 문제를 해결할 수 있다면 프로그래머의 생산성은 비약적으로 늘 수 있기 때문입니다.
프로그래밍의 패러다임 변화는 조합성을 끌어올리려는 노력의 연속입니다. goto 문을 사용하던 어셈블리에서 서브루틴, 블록 구조, for/while 루프를 강조하는 구조적 프로그래밍(structured programming)이 나온 이유는 풀어야 하는 문제의 복잡도가 증가하여 프로그램을 조합할 더 좋은 방법이 필요했기 때문입니다.
구조적 프로그래밍이 많은 문제점을 해결했음에도 불구하고 80-90년대 우리가 풀어야 할 문제의 복잡도가 또 다시 가파르게 상승하면서 새롭게 등장한 패러다임이 객체지향 프로그래밍(object-oriented programming)입니다. 클래스나 객체의 개념, 캡슐화(encapsulation), 정보 은닉(information hiding) 등이 나온 이유도 더 복잡해진 문제를 풀기 위해 더 좋은 조합 방법이 필요했기 때문입니다.
21세기에 들어와 함수 언어가 주목 받고 있는 이유는 우리가 풀어야 할 문제가 또 다시 더욱 복잡해지면서 클래스나 객체가 제공했던 수준 이상의 조합성이 필요해졌기 때문입니다. 함수 언어의 중요한 특징인 고차 함수(higher order function), 다형 함수(polymorphic function) 등이 궁극적으로 해결하고자 하는 문제도 결국 조합의 문제입니다.
하스켈을 배워야 하는 이유는 Functor, Bifunctor, Profunctor, Applicative functor, Monoid, Monad, Arrow, Lense, F-algebra, Adjunction 같은 수학적 개념들이 또 다시 복잡해지고 있는 문제를 풀 수 있는 새로운 조합 방법의 단서를 제공하기 때문입니다. 그리고 하스켈은 이런 측면에서 다른 함수 언어와도 비교하기 힘들 정도로 발전해 있습니다.
물론 하스켈을 통해 배우지 않아도 우리는 이미 많은 개념들을 알고 있고 실제로 사용하고 있습니다. ES6의 Promise, C#의 널 전파 연산자(null propagation operator), Python의 리스트 컴프리헨션(list comprehension) 등은 전혀 다른 기능처럼 보이지만, 내부적으로 Monad라는 같은 구조를 가지고 있습니다. 하스켈을 공부하면 이렇게 서로 상이해 보이는 개념들에 존재하는 공통 구조를 쉽게 발견할 수 있습니다.
정리하면, 프로그래밍의 본질은 조합을 얼마나 쉽게 할 수 있느냐에 있습니다. 그리고 하스켈은 프로그램을 조합에 사용할 수 있는 새로운 개념과 도구들을 제공합니다. 하스켈을 배워야 하는 이유는 이런 도구들을 습득하여 앞으로 닥칠 더 크고 복잡한 문제를 해결하는 프로그램을 작성하는 능력을 배양하는 데에 있습니다.
마지막으로 하스켈을 배워야 하는 또 다른 이유는 그 자체로 재미있기 때문입니다. 어렵기 때문에 재미가 없는 것이 아니라 퍼즐을 푸는 것처럼 새로운 것을 알아가고 깨닫는 재미가 있습니다. 특히, 이미 오랜 세월 개발을 해서 매너리즘에 빠지신 분들은 하스켈을 통해서 다시 프로그래밍의 재미를 느껴보시길 권합니다.