선언형 프로그래밍이 무슨 말일까요?
보통 이 용어를 설명하는 글들은, 반복 작업으로 for
구문을 쓰는 명령형 프로그래밍 모양과, 리스트에 filter
등을 적용하는 모양을 비교하는 예시를 듭니다. 하지만, 처음 이런 설명을 들으면, 반복하는 작업 구현의 위치를 옮겼을 뿐 그리 달라 보이지 않기도 합니다.
How
보다는 What
에 집중하는 스타일이라는 추상적인 설명도 많이 접합니다. 그냥 DSL(Domain Specific Language)을 만드는 방식으로 모델링하는 걸 말하는가 싶기도 합니다. 저는 이런 설명들로는 명확한 느낌이 오진 않았습니다. 하스켈을 잠시동안 물고 늘어진 저에게, 선언형 프로그래밍을 설명하라면 다음을 (컴비네이터 스타일)예시로 들겠습니다.
조합
을 통해 로직을 표현합니다. 문제들을 잘 쪼개서, 부분 문제들을 어떻게How
동작해서 실제 해결하는지는 기본 블록
과 도구
들에 구현되어 있어, 최종 조합
에는 무엇을What
을 하는지만 남아 있는 명세서처럼 보이게 됩니다.
단순히 일부 기능을 모듈화(프로시져화)해서 빼내고, 이름만 남겨두는 걸 의미한다기 보다, 모듈화 한 것들을, 한 가지 방식이 아닌, 여러가지 방식으로 유연하게 “조합”할 수 있게 설계하는 걸 의미합니다. 마치 자연수 같은 대상들을 정하고, 그 것들을 연산하는 더하기, 빼기들을 준비해서, 최종식은 이 것들을 조합해서 그 때 그 때 필요한 걸 표현하는 것처럼 말입니다.
문제를 해결하기 위해
a를 먼저 이렇게how
해결하고, 그 다음 b를 이렇게how
해결하고… 이렇게 시퀀스로 떠올릴 수도 있고,
문제를 이루는 기본 요소들을 먼저 정의하고, 이들이 어떤 동작들을 필요로 하는지 정의하고, 최종 로직은 이들 정의들을 잘 조합해서 해결하면, 여기엔 How
보다는 What
만 들어 있게 됩니다. 이 걸 “선언Declaration형”이라 부릅니다.
눈에 금방 들어오는 예시로, 기본 블럭
이 되는 Parser들을 정의하고, 이들의 실행 흐름을 제어할 수 있는(변형, 결합하며 접착할 수 있는 다양한) 도구
Combinator들을 만들어, 복잡한 파싱을 이들 조합
으로 해결하는 것을 들 수 있습니다.
리스트와 필터를 예시로 삼는 이유는, 리스트가 하나의 스트림으로 여러 필터의 조합을 통과해 원하는 결과에 도달하는 모양을, 실제 어떻게How
동작하는 가는 필터 구현에 들어있게 되고, 최종 문제를 기술하는 문장에는 이 필터들을 조합해서, 이런What
문제를 해결한다는 말만 보이게 됩니다.
도구
들에는 최대한 범용적인 기능만 남겨 놓고, 지금 접근 중인 문제에만 특별히 필요한 기능들은 기본 블럭
과 조합
에서만 드러나게 하면, 모나드니, Arrow니 하는 이름을 붙인 패턴들이 됩니다. (다른 말로 구조안에 뭐가 들어 있든 상관하지 않고, 구조만을 위한 컴비네이션을 만드는 걸 말합니다. )보통 이런 패턴들은 Int
나 Char
같은 Primitive 타입보다는 이런 Primitive 타입들을 담을 수 있는 구조
를 대상으로 조합
, 도구
를 준비해 놓습니다. 아주 인포멀하게 얘기하면, SomeWrapper a
에서 a
에는 무관하고, SomeWrapper
만으로 컴비네이션을 만듭니다.
제가 기존 설명들에 추가하고 싶은 핵심 키워드는 조합
입니다.
쯤 되겠습니다.
선언형의 한 예시인 컴비네이터 스타일에 대한 얘기지만, 선언형이란 용어에 접근하기에 괜찮아 보입니다. 역시나 금방 와닿는 설명도 아니고, 포멀한 설명도 아니긴 합니다만, FRP 프레임워크를 보다, 또 한 번 컴비네이션 스타일의 우아함을 느껴, 이게 바로 선언형이구나란 생각이 들어 글을 남깁니다.