SDF 챕터들에서 추출한 핵심 개념: - additive-programming: 가산적 프로그래밍 (전체 관통 테마) - generic-procedures: 제네릭 프로시저 (Ch3, Ch5, Ch6) - combinators: 컴비네이터 (Ch2~Ch5) - partial-information: 부분 정보 (Ch1, Ch4, Ch6, Ch7) - degeneracy: 퇴화성 (Ch1, Ch7) - layered-data: 레이어드 데이터 + 의존성 추적 (Ch2, Ch3, Ch6, Ch7) - propagation: 전파 모델 (Ch1, Ch5, Ch6, Ch7) - domain-specific-language: DSL (Ch2~Ch5) wiki/index.md Concepts 섹션 등록, wiki/log.md 기록 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
112 lines
6.1 KiB
Markdown
112 lines
6.1 KiB
Markdown
---
|
|
title: 컴비네이터
|
|
tags: [concept, SDF]
|
|
sources: [SDF-ch2-dsl, SDF-ch3-generic-procedures, SDF-ch4-pattern-matching, SDF-ch5-evaluation]
|
|
updated: 2026-04-30
|
|
---
|
|
|
|
# 컴비네이터
|
|
|
|
## 한 줄 정의
|
|
|
|
원시 부품(primitives)과 조합 수단(combinators)으로 이루어진 언어 체계에서, 조합의 인터페이스 명세가 원시 부품의 인터페이스 명세와 동일하도록 설계된 부품 조합기.
|
|
|
|
## 핵심 내용
|
|
|
|
### 컴비네이터 시스템의 정의와 핵심 성질
|
|
|
|
SDF의 정의:
|
|
|
|
> "A *system of combinators* is a set of primitive parts and a set of means of combining parts such that the interface specifications of the combinations are the same as those of the primitives."
|
|
|
|
이 정의에서 핵심은 **인터페이스 동일성**이다. 조합의 결과가 원시 부품과 동일한 인터페이스를 가지므로, 재귀적 조합이 무제한으로 가능하다. 어떤 조합도 법적으로 유효한(legally correct) 프로그램을 만들어낸다.
|
|
|
|
네 가지 핵심 성질:
|
|
1. **믹스앤매치**: 임의의 부품 조합이 가능
|
|
2. **법적 정확성**: 모든 조합이 타입 오류 없이 유효
|
|
3. **컨텍스트 독립성**: 부품의 동작이 조합 맥락에 영향받지 않음
|
|
4. **비간섭성**: 새 부품·컴비네이터 추가가 기존 프로그램에 영향 없음
|
|
|
|
### 기본 함수 컴비네이터
|
|
|
|
```scheme
|
|
;; compose: g 다음 f (f ∘ g)
|
|
(define (compose f g)
|
|
(define (the-composition . args)
|
|
(call-with-values (lambda () (apply g args)) f))
|
|
(restrict-arity the-composition (get-arity g)))
|
|
|
|
;; parallel-combine: f와 g를 같은 인수에 적용, 결과를 h에 전달
|
|
(define (parallel-combine h f g)
|
|
(define (the-combination . args)
|
|
(h (apply f args) (apply g args)))
|
|
the-combination)
|
|
|
|
;; spread-combine: 인수를 두 그룹으로 나눠 f와 g에 각각 적용
|
|
(define (spread-combine h f g)
|
|
(compose h (spread-apply f g)))
|
|
```
|
|
|
|
**아리티(arity) 관리**: 컴비네이터가 올바르게 작동하려면 각 함수의 인수 개수를 추적해야 한다. `restrict-arity`와 `get-arity`로 함수에 아리티를 메타데이터로 첨부한다(해시 테이블). 이는 레이어링 아이디어의 초기 형태이기도 하다.
|
|
|
|
### 컴비네이터로 DSL 만들기: 정규표현식 사례
|
|
|
|
SDF Ch2는 정규 표현식을 나쁜 설계의 예시로 들고, 컴비네이터 기반 대안을 제시한다:
|
|
|
|
```scheme
|
|
;; 나쁜 예: 정규표현식 문자열
|
|
"^[a-z].*[0-9]$" ; 컨텍스트에 따라 의미가 달라지고 조합이 어렵다
|
|
|
|
;; 좋은 예: 컴비네이터 기반 DSL
|
|
(r:seq (r:bol)
|
|
(r:char-from "abcdefghijklmnopqrstuvwxyz")
|
|
(r:repeat 0 #f (r:dot))
|
|
(r:char-from "0123456789")
|
|
(r:eol))
|
|
```
|
|
|
|
컴비네이터 기반 DSL은 각 부품이 독립적이고, 조합이 안전하며, 의미가 맥락에 무관하다.
|
|
|
|
### 컴비네이터가 채택되는 영역들
|
|
|
|
SDF 전체에서 컴비네이터 패턴이 반복 등장한다:
|
|
|
|
- **Ch2**: 함수 컴비네이터(`compose`, `parallel-combine`), 정규표현식 컴비네이터
|
|
- **Ch3**: 산술 패키지 컴비네이터(`extend-arithmetic`, `add-arithmetics`)
|
|
- **Ch4**: 매처 컴비네이터 — 패턴이 매처들의 컴비네이터 조합으로 컴파일됨
|
|
- **Ch5**: 실행 프로시저들이 컴비네이터 시스템을 이룸 (분석/실행 분리 단계)
|
|
|
|
### 컴비네이터의 한계: 다이아몬드와 진흙
|
|
|
|
> "Systems built by combinators result in beautiful diamond-like systems. This is sometimes the right idea... but it is very hard to add to a diamond."
|
|
|
|
컴비네이터 시스템이 완성된 이후 확장하려면, 기존 컴비네이터 구조 *밖*에서 새 부품을 만들어 합치는 방법이 필요하다. 이것이 Ch3에서 제네릭 프로시저로 이행하는 이유다.
|
|
|
|
컴비네이터의 폐쇄성(closure)이 장점이자 단점이다. 조합이 원시 부품과 같은 타입이라는 성질이 무한 조합을 가능하게 하지만, 그 타입 체계 바깥으로 나가기 어렵게 만든다.
|
|
|
|
아키텍트 관점에서: 컴비네이터는 **설계 공간이 안정적이고 미리 알려진 도메인**에 적합하다. 요구사항이 변동적이거나 타입이 동적으로 추가되어야 하면 제네릭 프로시저가 낫다.
|
|
|
|
## SDF에서의 등장
|
|
|
|
- [[SDF-ch2-dsl]]: 핵심 챕터. 함수 컴비네이터(`compose`, `parallel-combine`, `spread-combine`) 구현, 아리티 관리, 정규표현식 DSL, 단위 래퍼, 체커 도메인 모델
|
|
- [[SDF-ch3-generic-procedures]]: 산술 패키지를 컴비네이터로 결합하는 시도와 그 한계 분석. 컴비네이터에서 제네릭 프로시저로의 이행 동기 설명
|
|
- [[SDF-ch4-pattern-matching]]: 매처들이 컴비네이터 방식으로 구성됨. 패턴 컴파일 = 매처 컴비네이터 조합
|
|
- [[SDF-ch5-evaluation]]: 실행 프로시저(execution procedures)들이 CPS 컴비네이터 시스템을 구성함
|
|
|
|
## 실천 시 주의점
|
|
|
|
**아리티 추적의 부담**: 함수가 몇 개의 인수를 받는지를 런타임에 추적해야 컴비네이터가 올바르게 작동한다. 언어가 이를 지원하지 않으면 별도의 메타데이터 관리가 필요하다.
|
|
|
|
**에러 메시지 품질**: 잘못된 조합에서의 에러가 어느 부품에서 발생했는지 추적하기 어렵다. 조합이 깊어질수록 스택 트레이스가 의미없어진다.
|
|
|
|
**컴비네이터 수의 폭발**: 모든 유용한 조합을 컴비네이터로 제공하려다 보면 컴비네이터 수가 폭발적으로 늘어날 수 있다. 최소한의 직교 컴비네이터 집합을 설계하는 것이 중요하다.
|
|
|
|
**맥락 독립성 보장**: 컴비네이터의 핵심 성질인 컨텍스트 독립성을 실제로 보장하기 위해서는 부품들이 부수 효과(side effect)나 전역 상태에 의존하지 않아야 한다.
|
|
|
|
## 관련 개념
|
|
|
|
- [[additive-programming]] — 컴비네이터는 가산적 프로그래밍의 기반 도구
|
|
- [[generic-procedures]] — 컴비네이터의 한계를 넘어서는 동적 확장 메커니즘
|
|
- [[domain-specific-language]] — 컴비네이터 시스템이 DSL의 구조적 기반
|
|
- [[layered-data]] — 아리티 첨부에서 시작된 레이어링 아이디어의 확장
|