Files
softwaredesign/wiki/concepts/additive-programming.md
minsung f868de1ce7 feat: wiki/concepts/ 핵심 개념 페이지 8개 생성
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>
2026-04-30 14:55:04 +09:00

83 lines
5.3 KiB
Markdown

---
title: 가산적 프로그래밍
tags: [concept, SDF]
sources: [SDF-ch1-flexibility, SDF-ch2-dsl, SDF-ch3-generic-procedures, SDF-ch4-pattern-matching, SDF-ch5-evaluation, SDF-ch6-layering, SDF-ch7-propagation]
updated: 2026-04-30
---
# 가산적 프로그래밍
## 한 줄 정의
동작하는 코드를 수정하지 않고 새 코드를 *추가*하는 것만으로 새 기능을 구현하거나 기존 기능을 새 요구사항에 맞추는 프로그래밍 스타일.
## 핵심 내용
### 왜 "수정"이 아닌 "추가"인가
소프트웨어 수명주기에서 가장 비싼 비용은 재작성과 리팩토링이다. 요구사항이 변할 때마다 기존 코드를 건드리면 다음과 같은 문제가 발생한다:
- 회귀 버그: 잘 동작하던 부분이 수정 여파로 깨짐
- 예상치 못한 결합(coupling): 수정이 다른 곳에 영향
- 테스트 재작성 부담
가산적 접근은 변경 대신 레이어를 쌓거나(Ch6), 핸들러를 추가하거나(Ch3), 규칙을 추가하거나(Ch4), 전파기를 배선도에 더하는(Ch7) 식으로 기능을 확장한다. 기존 코드는 원래대로 남아 있다.
### 가산성을 가능하게 하는 전제 조건
> "In order for additive programming to be possible, it is necessary to minimize the assumptions about how a program works and how it will be used."
세 가지 전제 조건:
1. **가정의 최소화**: 컴포넌트가 자기 역할 외의 것을 가정하지 않아야 새 컨텍스트에서도 재사용 가능
2. **just-in-time 결정**: 실행 환경에 기반해 결정을 늦춤. 미리 굳힌 가정은 나중에 변경할 수 없음
3. **관심사의 명확한 분리**: 의도치 않은 상호작용을 줄여야 변경이 국소적으로 머뭄
### SDF 전체에 걸친 구현 패턴
각 챕터는 가산성을 다른 차원에서 구현한다:
| 챕터 | 가산성 메커니즘 | 추가 단위 |
|------|----------------|---------|
| Ch2 | 컴비네이터 | 새 컴비네이터/프리미티브 부품 |
| Ch3 | 제네릭 프로시저 | 새 타입 핸들러 |
| Ch4 | 규칙 시스템 | 새 패턴 규칙 |
| Ch5 | 제네릭 인터프리터 | 새 표현식 타입 핸들러 |
| Ch6 | 레이어드 데이터 | 새 메타데이터 레이어 |
| Ch7 | 전파 네트워크 | 새 전파기 노드 |
### 정확성 요구와의 긴장
"모든 것을 증명 가능하게 만들라"는 규율은 역설적으로 시스템을 취약하게 만든다. 일반적 메커니즘의 일반적 속성을 증명하기는 특수 메커니즘의 특수 속성을 증명하기보다 훨씬 어렵기 때문에, 증명 요구는 부품을 가능한 한 특수화하도록 유도한다. 특수화된 부품의 조합은 변화의 여지가 없다.
Postel의 법칙이 여기 적용된다: "Be conservative in what you do, be liberal in what you accept from others." 컴포넌트가 넓은 입력을 수용할수록 재사용성이 높아지고, 출력은 하위 컴포넌트에 최소한의 가정을 강요한다.
## SDF에서의 등장
- [[SDF-ch1-flexibility]]: 개념의 철학적·생물학적 기반을 제시. 퇴화성, 탐색적 행동, 스마트 파츠 원칙으로 동기 부여
- [[SDF-ch2-dsl]]: 컴비네이터와 래퍼를 통한 첫 번째 구체적 구현. 믹스앤매치 부품 체계
- [[SDF-ch3-generic-procedures]]: 핸들러를 추가하는 방식으로 제네릭 프로시저를 확장
- [[SDF-ch4-pattern-matching]]: `attach-rule!`로 나중에 패턴 규칙을 동적으로 추가
- [[SDF-ch5-evaluation]]: `g:eval`을 제네릭 프로시저로 구현하여 새 표현식 타입을 핸들러 추가만으로 도입
- [[SDF-ch6-layering]]: 기존 코드를 전혀 수정하지 않고 단위·의존성 레이어를 추가
- [[SDF-ch7-propagation]]: 배선도에 새 전파기를 추가하는 것이 곧 기능 확장
## 실천 시 주의점
**컴비네이터의 한계**: Ch3에서 지적하듯, "다이아몬드 구조"(컴비네이터로 완성된 아름다운 폐쇄 시스템)는 이후 확장이 매우 어렵다. 가산성과 폐쇄성(closure)은 상충한다. 컴비네이터가 가산적인 것은 새 부품을 *조합*하는 한에서이고, 컴비네이터 자체를 수정해야 할 때는 그렇지 않다.
**순서 의존성**: 제네릭 프로시저에서 핸들러를 추가할 때, 적용 가능성이 겹치는 규칙들의 우선순위가 추가 순서에 따라 달라질 수 있다. 이 의존성은 암묵적이어서 디버깅이 어렵다.
**비용**: 가산성은 공간(여러 레이어를 유지), 계산 시간(디스패치 오버헤드), 프로그래머 시간(복잡한 인프라 이해) 비용을 수반한다. 수명이 짧거나 변경이 없는 코드에서는 과잉 투자다.
**진짜 가산성인지 확인**: "추가만으로" 확장이 이루어지는지 아니면 기존 코드를 수정해야 하는지를 의식적으로 확인해야 한다. 많은 경우 '수정하는 곳'이 숨어있다.
## 관련 개념
- [[generic-procedures]] — 가산적 확장의 핵심 메커니즘: 핸들러 추가
- [[combinators]] — 가산적 조합의 기반 도구
- [[layered-data]] — 수정 없는 메타데이터 추가의 구현
- [[propagation]] — 배선도에 노드를 더하는 가산적 계산 모델
- [[degeneracy]] — 가산성의 생물학적 대응물: 여러 메커니즘의 공존
- [[domain-specific-language]] — 가산적 확장이 자연스러운 도메인 언어 구축