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>
This commit is contained in:
82
wiki/concepts/additive-programming.md
Normal file
82
wiki/concepts/additive-programming.md
Normal file
@@ -0,0 +1,82 @@
|
||||
---
|
||||
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]] — 가산적 확장이 자연스러운 도메인 언어 구축
|
||||
111
wiki/concepts/combinators.md
Normal file
111
wiki/concepts/combinators.md
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
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]] — 아리티 첨부에서 시작된 레이어링 아이디어의 확장
|
||||
93
wiki/concepts/degeneracy.md
Normal file
93
wiki/concepts/degeneracy.md
Normal file
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: 퇴화성
|
||||
tags: [concept, SDF]
|
||||
sources: [SDF-ch1-flexibility, SDF-ch7-propagation]
|
||||
updated: 2026-04-30
|
||||
---
|
||||
|
||||
# 퇴화성
|
||||
|
||||
## 한 줄 정의
|
||||
|
||||
동일한 목적을 달성하는 *서로 다른* 복수의 메커니즘을 보유함으로써, 하나가 실패해도 다른 방법이 작동하고, 여러 메커니즘의 부분 정보를 결합하면 단일 메커니즘보다 더 정확한 결과를 얻는 설계 원칙.
|
||||
|
||||
## 핵심 내용
|
||||
|
||||
### 퇴화성 vs 중복성
|
||||
|
||||
이 두 개념의 구분이 SDF의 핵심 통찰 중 하나다:
|
||||
|
||||
- **중복성(redundancy)**: *동일한* 기능의 여분 용량. 신장이 두 개인 것처럼. 하나가 고장 나면 다른 하나가 같은 일을 한다.
|
||||
- **퇴화성(degeneracy)**: *서로 다른* 메커니즘이 동일한 목적을 달성. 면역 시스템의 여러 항체 유형처럼.
|
||||
|
||||
퇴화성이 중복성보다 강력한 이유:
|
||||
|
||||
1. **서로 다른 버그**: 동일한 코드를 복사한 중복은 같은 버그를 공유한다. 서로 다른 메커니즘은 서로 다른 약점을 가지므로, 하나의 장애가 전체 실패로 이어지지 않는다.
|
||||
2. **정보 결합**: 서로 다른 방법으로 얻은 부분 정보를 결합하면 어느 단일 방법보다 더 정확한 결과를 낼 수 있다. 중복은 이 이점을 제공하지 못한다.
|
||||
3. **진화적 유연성**: 서로 다른 메커니즘은 서로 다른 맥락에서 다른 방식으로 발전할 수 있다. 중복은 항상 함께 움직여야 한다.
|
||||
|
||||
### 생물학적 유추
|
||||
|
||||
SDF의 퇴화성 개념은 생물학에서 직접 차용된다. 면역계, 유전자 조절 네트워크, 신경계는 모두 퇴화적 구조를 가진다. 인간의 위치 인식은 GPS, 관성 항법, 시각 단서, 기압계를 동시에 활용하고 일관된 위치 추정을 위해 결합한다.
|
||||
|
||||
항법 시스템 예시:
|
||||
- 여러 독립적 센서가 동일한 위치를 다른 방법으로 추정
|
||||
- 각 추정은 오차 구간을 가진 부분 정보
|
||||
- 구간의 교집합이 더 정확한 위치 추정
|
||||
|
||||
### 소프트웨어 시스템에서의 퇴화성
|
||||
|
||||
퇴화성을 소프트웨어에서 구현하는 방법들:
|
||||
|
||||
**다중 핸들러 (Ch3)**: 동일한 제네릭 프로시저에 여러 타입에 대한 핸들러를 등록하는 것은 퇴화적이다. 각 핸들러는 독립적인 구현이며, 특정 타입에 대해 어떤 핸들러가 "더 특수한" 처리를 하는지 결정될 수 있다.
|
||||
|
||||
**다중 전파기 (Ch7)**: 여러 전파기가 동일한 셀에 값을 제공할 수 있다. 셀은 이 값들을 병합하여 더 정확한 결과를 얻는다. 새 추정 방법은 새 전파기로 추가되며, 기존 구성을 바꾸지 않는다.
|
||||
|
||||
```scheme
|
||||
;; Vega까지의 거리를 두 독립 방법으로 추정
|
||||
(tell! Vega-parallax-distance interval-1 'FGWvonStruve1837)
|
||||
(tell! Vega-parallax-distance interval-2 'HipparcosSatellite)
|
||||
;; 시스템이 자동으로 두 구간의 교집합을 계산
|
||||
```
|
||||
|
||||
**레이어링 (Ch6)**: 동일한 계산에 기본 레이어, 단위 레이어, 의존성 레이어가 병렬로 실행되는 것도 퇴화적이다. 기본 계층만 실패해도 메타데이터 레이어는 독립적으로 작동한다.
|
||||
|
||||
**대안 구현 제안 (Ch6, 레이어링의 확장)**: SDF는 레이어에 "대안 구현 제안"을 담을 수 있다고 언급한다. 이는 퇴화적 구현을 시스템 내에 명시적으로 등록하는 방법이다.
|
||||
|
||||
### 탐색적 행동(Exploratory Behavior)과의 연결
|
||||
|
||||
Ch1에서 소개되는 생성-검증(generate-and-test) 메커니즘은 퇴화성의 절차적 형태다. 생성기는 여러 후보를 만들고, 검증기는 하나씩 검사한다. 여러 후보 전략이 독립적으로 발전할 수 있다.
|
||||
|
||||
백트래킹 탐색(Ch4, Ch5의 amb, Ch7의 의존성 지향 백트래킹)은 이 패턴의 계산적 구현이다. 각 선택지가 독립적인 "메커니즘"이고, 실패한 선택지는 건너뛰고 다른 선택지를 시도한다.
|
||||
|
||||
### 가산성과의 관계
|
||||
|
||||
퇴화성은 가산적 프로그래밍의 자연스러운 결과이기도 하다. 새 메커니즘을 추가하는 것만으로(기존 메커니즘을 수정하지 않고) 퇴화성을 강화할 수 있다.
|
||||
|
||||
전파 모델(Ch7)에서:
|
||||
|
||||
> "The structure is additive: new ways to contribute information can be included just by adding new parts to a network."
|
||||
|
||||
새 추정 방법 = 새 전파기 = 기존 구조에 가산적 추가 = 퇴화성 강화.
|
||||
|
||||
## SDF에서의 등장
|
||||
|
||||
- [[SDF-ch1-flexibility]]: 개념의 철학적 기반. 중복성과 퇴화성의 구분, 항법 시스템 예시, 생물학적 적응성에서의 영감
|
||||
- [[SDF-ch7-propagation]]: 계산적 구현의 완성형. "퇴화성과 다중 소스" 섹션(7절)에서 명시적으로 등장. 여러 독립 전파기가 동일 셀에 값을 제공하고 병합
|
||||
|
||||
## 실천 시 주의점
|
||||
|
||||
**퇴화성 vs 불일치**: 여러 메커니즘이 같은 목적을 달성하되 서로 *일관성 있는* 결과를 내야 한다. 메커니즘들이 상충하는 결과를 낸다면 이는 모순이지 퇴화성이 아니다. 병합 정책과 모순 처리 전략이 필요하다.
|
||||
|
||||
**퇴화성의 관리 비용**: 여러 메커니즘을 유지하는 것은 비용이다. 각 메커니즘은 독립적으로 구현되고 테스트되어야 한다. 퇴화성이 가져다주는 견고성이 이 비용을 정당화하는지 판단이 필요하다.
|
||||
|
||||
**단순 중복과 혼동**: "혹시 몰라서 같은 로직을 두 번 작성"하는 것은 퇴화성이 아니라 중복이다. 퇴화성은 독립적으로 다른 방법으로 구현해야 가치가 있다.
|
||||
|
||||
**의도적 퇴화성 설계**: 퇴화성은 우연히 생기지 않는다. 시스템이 여러 정보 소스를 수용하고 병합하는 구조를 처음부터 설계해야 한다. 단일 "정답 경로"를 가정한 설계에 나중에 퇴화성을 추가하기는 어렵다.
|
||||
|
||||
## 관련 개념
|
||||
|
||||
- [[partial-information]] — 여러 메커니즘이 제공하는 부분 정보들을 병합하는 기반
|
||||
- [[propagation]] — 퇴화적 추정을 자연스럽게 지원하는 계산 모델
|
||||
- [[additive-programming]] — 새 메커니즘을 추가하는 가산적 방식으로 퇴화성 강화
|
||||
- [[layered-data]] — 레이어가 퇴화적 메타데이터 처리를 지원
|
||||
132
wiki/concepts/domain-specific-language.md
Normal file
132
wiki/concepts/domain-specific-language.md
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
title: 도메인 특화 언어 (DSL)
|
||||
tags: [concept, SDF]
|
||||
sources: [SDF-ch2-dsl, SDF-ch3-generic-procedures, SDF-ch4-pattern-matching, SDF-ch5-evaluation]
|
||||
updated: 2026-04-30
|
||||
---
|
||||
|
||||
# 도메인 특화 언어 (DSL)
|
||||
|
||||
## 한 줄 정의
|
||||
|
||||
문제 도메인의 개념과 관계를 직접 반영하는 어휘와 구조를 가진 언어로, 해당 도메인의 문제를 그 언어로 자연스럽게 표현할 수 있게 하여 도메인 전문가와 프로그래머 사이의 간격을 줄이고 가산적 확장을 가능하게 한다.
|
||||
|
||||
## 핵심 내용
|
||||
|
||||
### DSL의 스펙트럼
|
||||
|
||||
SDF에서 DSL은 단일한 기법이 아니라 구현 깊이에 따른 스펙트럼이다:
|
||||
|
||||
**1단계: 컴비네이터 기반 임베디드 DSL (Ch2)**
|
||||
- 호스트 언어(Scheme) 함수들을 컴비네이터로 조합
|
||||
- 별도의 파서 없음. 호스트 언어의 문법을 그대로 사용
|
||||
- 예: 정규표현식 컴비네이터, 함수 컴비네이터
|
||||
|
||||
**2단계: 제네릭 프로시저 기반 도메인 언어 (Ch3)**
|
||||
- 도메인의 연산(더하기, 곱하기)이 제네릭 프로시저
|
||||
- 새 타입이 도입될 때마다 핸들러를 추가해 도메인을 확장
|
||||
- 예: 숫자 → 기호 → 함수 → 미분 객체로 확장되는 산술
|
||||
|
||||
**3단계: 패턴 기반 규칙 언어 (Ch4)**
|
||||
- 데이터 변환을 패턴+귀결 규칙으로 표현
|
||||
- `rule-simplifier`가 고정점까지 규칙들을 자동 적용
|
||||
- 예: 대수 단순화 규칙들, 체스 규칙
|
||||
|
||||
**4단계: 완전한 인터프리터 (Ch5)**
|
||||
- 새 언어를 완전히 정의: 구문, 의미, 실행 모델
|
||||
- 가장 강력하지만 가장 많은 투자가 필요
|
||||
- 예: lazy eval, amb를 포함한 Scheme 인터프리터
|
||||
|
||||
### 왜 DSL인가
|
||||
|
||||
> "One of the best ways to attack a problem is to make up a domain-specific language in which the solution is easily expressed."
|
||||
|
||||
> "Programmers should know how to escape the confines of whatever programming language they must use by making an interpreter for a language that is more appropriate for expressing the solution."
|
||||
|
||||
범용 언어로 도메인 문제를 풀면 두 가지 불편이 생긴다:
|
||||
1. **개념의 직접성 부재**: 도메인 개념을 언어 구조에 억지로 매핑해야 함
|
||||
2. **가산성 어려움**: 새 개념이 추가될 때 기존 코드의 여러 곳을 수정해야 함
|
||||
|
||||
DSL은 도메인 개념이 언어의 일급 시민이 되므로 두 문제가 모두 해결된다.
|
||||
|
||||
### 도메인 모델 분리: 체커 게임 사례
|
||||
|
||||
Ch2의 체커 게임 심판 구현은 DSL 설계의 좋은 교육 사례다:
|
||||
|
||||
**1단계 — 모놀리식**: 킹(king), 점프(jump) 같은 체커 특화 개념이 코드 전체에 분산됨. 새 규칙을 추가하려면 여러 곳을 수정해야 함.
|
||||
|
||||
**2단계 — 도메인 모델 분리**:
|
||||
- `partial-move(pmove)` 추상화: 이동 경로를 표현하는 도메인 객체
|
||||
- **룰 익스큐티브(rule executive)**: 제어 구조를 규칙에서 분리
|
||||
- **진화 규칙(evolution rules)**: pmove를 새 pmove들로 변환
|
||||
- **집합 규칙(aggregate rules)**: 완료된 pmove 집합에 작용
|
||||
- 각 체커 규칙이 단일 프로시저로 표현됨
|
||||
|
||||
이제 새 규칙(예: "룰 변형" 게임)을 추가하는 것은 새 프로시저를 등록하는 것이다. 기존 규칙 프로시저들은 그대로다.
|
||||
|
||||
도메인 모델 = 도메인 언어의 프리미티브 + 조합 수단 + 추상화 수단.
|
||||
|
||||
### 정규표현식: DSL의 나쁜 예
|
||||
|
||||
> "Regular expressions are a beautiful example of how *not* to build a system."
|
||||
|
||||
전통 정규표현식의 문제:
|
||||
- 컨텍스트 의존성: `^`, `.`, `*`이 위치에 따라 의미가 다름
|
||||
- 조합 불가능: `(abc)|(def)` 조각은 독립적으로 재사용할 수 없음
|
||||
- 가독성: 복잡한 패턴은 해독이 불가능
|
||||
|
||||
컴비네이터 기반 대안은 각 부품이 독립적이고, 조합이 안전하며, 의미가 명확하다. 성능이 약간 낮을 수 있지만, 컴파일러로 POSIX BRE로 변환하면 해결된다.
|
||||
|
||||
### 인터프리터: DSL의 궁극적 형태
|
||||
|
||||
인터프리터를 만드는 것은 새 언어를 만드는 것이다. Ch5의 인터프리터는 가산적으로 확장 가능하다:
|
||||
|
||||
- `g:eval`을 제네릭 프로시저로 구현: 새 표현식 타입은 새 핸들러 추가
|
||||
- 분석/실행 분리: 새 평가 전략(lazy, amb)은 실행 단계만 수정
|
||||
- lazy eval: 매개변수 선언으로 평가 전략 지정 → 선언적 DSL
|
||||
|
||||
```scheme
|
||||
;; 언어 확장: lazy 평가 지원 추가
|
||||
(lambda ((lazy x) y) ; x는 필요할 때만 평가
|
||||
(if (zero? y) 0 (* x y)))
|
||||
```
|
||||
|
||||
이 확장은 인터프리터의 핸들러를 추가하는 것만으로 이루어진다.
|
||||
|
||||
### 배선도 언어 (Ch7)
|
||||
|
||||
전파 모델의 배선도 언어는 또 다른 형태의 DSL이다. 제약(constraint)을 언어의 기본 단위로 삼는 선언적 언어다:
|
||||
|
||||
```scheme
|
||||
;; 곱셈 제약: 세 변수 중 둘을 알면 나머지를 계산
|
||||
(c:* a b c)
|
||||
|
||||
;; 삼각함수 제약
|
||||
(c:tan angle ratio)
|
||||
```
|
||||
|
||||
이 언어에서 "프로그램"은 제약들의 네트워크다. 제어 흐름이 없다.
|
||||
|
||||
## SDF에서의 등장
|
||||
|
||||
- [[SDF-ch2-dsl]]: 핵심 챕터. 컴비네이터 기반 임베디드 DSL, 정규표현식 DSL, 래퍼, 체커 도메인 모델
|
||||
- [[SDF-ch3-generic-procedures]]: 제네릭 산술이 도메인별 확장을 지원하는 DSL의 기반 인프라
|
||||
- [[SDF-ch4-pattern-matching]]: 패턴+규칙 시스템이 변환 DSL. 대수 단순화 규칙들이 대수 DSL
|
||||
- [[SDF-ch5-evaluation]]: 완전한 인터프리터 구현. lazy eval과 amb를 DSL 확장으로 추가하는 과정
|
||||
|
||||
## 실천 시 주의점
|
||||
|
||||
**DSL의 복잡성 비용**: DSL을 만들면 DSL 자체를 배워야 하는 학습 비용이 생긴다. "이 DSL을 쓰는 사람이 나 혼자인가?"를 먼저 물어야 한다. 팀의 크기와 도메인의 복잡성이 DSL 투자를 정당화해야 한다.
|
||||
|
||||
**임베디드 vs 독립 DSL**: 임베디드 DSL(EDSL)은 호스트 언어의 도구를 모두 쓸 수 있지만, 호스트 언어의 문법 제약을 받는다. 독립 DSL은 문법 자유도가 높지만 파서·인터프리터 구현 비용이 크다. 대부분의 경우 EDSL이 더 실용적이다.
|
||||
|
||||
**도메인 모델의 안정성**: DSL은 도메인 모델이 안정적일 때 투자할 가치가 있다. 도메인 모델이 자주 변하면 DSL 자체를 수정하는 비용이 DSL이 주는 이점을 상회한다.
|
||||
|
||||
**에러 메시지 품질**: 사용자가 DSL을 잘못 사용했을 때 의미 있는 에러 메시지를 제공해야 한다. 호스트 언어의 에러가 DSL 사용자에게 노출되면 "내부 구현"이 드러나 추상화가 새는 것이다.
|
||||
|
||||
## 관련 개념
|
||||
|
||||
- [[additive-programming]] — DSL은 가산적 확장이 자연스러운 언어 레벨 추상화
|
||||
- [[combinators]] — 임베디드 DSL의 핵심 구현 메커니즘
|
||||
- [[generic-procedures]] — 제네릭 프로시저로 도메인 연산을 확장 가능하게 구현
|
||||
- [[propagation]] — 배선도 언어 자체가 제약 기반 DSL
|
||||
105
wiki/concepts/generic-procedures.md
Normal file
105
wiki/concepts/generic-procedures.md
Normal file
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: 제네릭 프로시저
|
||||
tags: [concept, SDF]
|
||||
sources: [SDF-ch3-generic-procedures, SDF-ch5-evaluation, SDF-ch6-layering]
|
||||
updated: 2026-04-30
|
||||
---
|
||||
|
||||
# 제네릭 프로시저
|
||||
|
||||
## 한 줄 정의
|
||||
|
||||
인수의 타입(혹은 임의의 술어 조합)에 따라 적절한 핸들러를 동적으로 선택·호출하며, 기존 구조를 수정하지 않고 새 타입 핸들러를 추가함으로써 가산적으로 확장 가능한 프로시저.
|
||||
|
||||
## 핵심 내용
|
||||
|
||||
### 컴비네이터에서 제네릭 프로시저로의 이행
|
||||
|
||||
Ch2의 컴비네이터는 아름다운 폐쇄 구조를 만들지만, 완성된 "다이아몬드"에 새 면을 추가하기는 어렵다:
|
||||
|
||||
> "Systems built by combinators result in beautiful diamond-like systems... but it is very hard to add to a diamond. If a system is built as a ball of mud, it is easy to add more mud."
|
||||
|
||||
제네릭 프로시저는 이 균형점을 다르게 설정한다. 구조 자체는 개방적(open)이고, 핸들러는 언제든지 추가 가능하다. "진흙 덩어리"가 아니라 핵심 디스패치 인프라는 단단하되 확장 지점은 열려있는 구조다.
|
||||
|
||||
### 술어-디스패치 메커니즘
|
||||
|
||||
SDF의 제네릭 프로시저는 **술어-디스패치(predicate dispatch)** 방식을 사용한다. 각 핸들러는 *적용 가능성 명세(applicability specification)*, 즉 인수 각각에 대한 술어 리스트와 함께 등록된다:
|
||||
|
||||
```scheme
|
||||
;; 기본 수치 연산
|
||||
(define-generic-procedure-handler plus
|
||||
(all-args 2 number?)
|
||||
(lambda (a b) (+ a b)))
|
||||
|
||||
;; 기호 산술 확장
|
||||
(define-generic-procedure-handler plus
|
||||
(any-arg 2 symbolic? number?)
|
||||
(lambda (a b) (list '+ a b)))
|
||||
```
|
||||
|
||||
단일 디스패치(OOP의 메서드)가 수신자 하나의 타입만 보는 것과 달리, 이 방식은 **모든 인수의 조합**에 따라 핸들러를 결정한다. 어드벤처 게임 예시(Ch3.5)에서 4인수 `generic-move!`가 이를 잘 보여준다.
|
||||
|
||||
### 구현의 세 축
|
||||
|
||||
1. **generic-procedure-constructor**: 디스패치 전략을 인자로 받아 제네릭 프로시저 생성기를 반환. 디스패치 전략 자체가 교체 가능한 컴포넌트다.
|
||||
|
||||
2. **dispatch-store**: 핸들러 저장·검색 전략을 캡슐화. 단순 선형 스캔, 트라이(trie) 인덱스, 캐시 래핑 등 다양한 구현을 교체 가능.
|
||||
|
||||
3. **메타데이터 테이블**: 제네릭 프로시저에 규칙 목록을 "스티키 노트"처럼 첨부. 런타임 확장의 기반.
|
||||
|
||||
### 폐쇄성(Closure): 제네릭 산술의 자기 참조
|
||||
|
||||
제네릭 프로시저의 폐쇄성 속성을 제대로 활용하면, 확장들이 자기 자신을 기반으로 구축될 수 있다:
|
||||
|
||||
```scheme
|
||||
(let ((g (make-generic-arithmetic make-simple-dispatch-store)))
|
||||
(add-to-generic-arithmetic! g numeric-arithmetic)
|
||||
(extend-generic-arithmetic! g symbolic-extender)
|
||||
(extend-generic-arithmetic! g function-extender)
|
||||
(install-arithmetic! g))
|
||||
```
|
||||
|
||||
이 구조에서 `function-extender`가 추가하는 함수 산술은 기호 산술을 포함한 전체 제네릭 산술을 재귀적으로 사용한다. 컴비네이터 방식에서는 이런 자기 참조 구조를 만들기 어렵다.
|
||||
|
||||
### 자동 미분: 제네릭 프로시저의 킬러 애플리케이션
|
||||
|
||||
Ch3.3의 자동 미분은 제네릭 프로시저의 힘을 가장 인상적으로 보여준다. 미분 객체 `[x, δx]`를 새 타입으로 도입하고, 기존 산술 연산들에 이 타입을 처리하는 핸들러만 추가하면 연쇄 법칙이 자동으로 적용된다. 기존 수치·기호 산술 코드는 한 줄도 수정하지 않는다.
|
||||
|
||||
```scheme
|
||||
;; sqrt 연산에 미분 핸들러 추가
|
||||
(define diff:sqrt
|
||||
(diff:unary-proc sqrt (lambda (x) (/ 1 (* 2 (sqrt x))))))
|
||||
```
|
||||
|
||||
이것이 가산적 프로그래밍의 이상적인 형태다.
|
||||
|
||||
### 효율적 디스패치
|
||||
|
||||
핸들러가 많아지면 단순 선형 스캔은 느려진다. 두 가지 최적화:
|
||||
|
||||
- **트라이(Trie) 인덱스**: 인수 시퀀스를 트라이로 인덱싱하여 첫 번째 인수 타입으로 후보 집합을 좁힘
|
||||
- **캐시 래핑**: 인수 타입 태그 조합을 키로 이전 디스패치 결과를 캐시. `cache-wrapped-dispatch-store`로 임의의 디스패치 전략에 투명하게 추가 가능
|
||||
|
||||
## SDF에서의 등장
|
||||
|
||||
- [[SDF-ch3-generic-procedures]]: 핵심 챕터. 술어-디스패치 제네릭 프로시저 전 구현, 자동 미분, 트라이 디스패치, 추상 술어, 어드벤처 게임 예시
|
||||
- [[SDF-ch5-evaluation]]: `g:eval`이 제네릭 프로시저로 구현됨. 새 표현식 타입을 핸들러 추가만으로 인터프리터에 도입
|
||||
- [[SDF-ch6-layering]]: 레이어드 프로시저는 제네릭 프로시저를 기반으로 구현됨. "레이어드 데이텀의 decoration은 제네릭 연산을 지원하는 태깅의 일반화"
|
||||
|
||||
## 실천 시 주의점
|
||||
|
||||
**규칙 충돌과 순서 의존성**: 적용 가능성이 겹치는 두 핸들러가 있을 때, 어느 것이 선택되는지는 추가 순서에 의존할 수 있다. 이 암묵적 의존성은 시스템이 커질수록 관리하기 어려워진다. 의식적으로 술어들 사이의 포함 관계(`set-predicate<=!`)를 명시적으로 선언해 우선순위를 명확히 해야 한다.
|
||||
|
||||
**디스패치 비용**: 모든 호출에서 인수 타입을 동적으로 확인하는 비용이 발생한다. 캐싱으로 많이 줄일 수 있지만, 성능이 중요한 내부 루프에는 직접 호출을 고려해야 한다.
|
||||
|
||||
**디버깅 어려움**: 어떤 핸들러가 선택됐는지 추적하기 어렵다. 디스패치 로직을 투명하게 볼 수 있는 디버깅 도구 없이는 버그 원인 파악이 힘들다.
|
||||
|
||||
**"100 functions on 1 data structure"**: Alan Perlis의 격언처럼, 제네릭 프로시저는 많은 연산이 하나의 공통 인터페이스를 통해 다양한 타입에 적용되는 구조를 지향한다. 반대로 타입별로 별도의 함수 집합을 만드는 것은 제네릭 프로시저의 이점을 살리지 못한다.
|
||||
|
||||
## 관련 개념
|
||||
|
||||
- [[additive-programming]] — 핸들러 추가가 가산적 확장의 핵심 메커니즘
|
||||
- [[combinators]] — 컴비네이터의 한계를 극복하는 상보적 접근
|
||||
- [[layered-data]] — 제네릭 프로시저 위에 구축된 레이어링
|
||||
- [[domain-specific-language]] — g:eval을 제네릭 프로시저로 만들어 DSL 확장성 확보
|
||||
- [[partial-information]] — 추상 술어를 통한 타입 태깅이 부분 정보 처리와 연결
|
||||
122
wiki/concepts/layered-data.md
Normal file
122
wiki/concepts/layered-data.md
Normal file
@@ -0,0 +1,122 @@
|
||||
---
|
||||
title: 레이어드 데이터
|
||||
tags: [concept, SDF]
|
||||
sources: [SDF-ch2-dsl, SDF-ch3-generic-procedures, SDF-ch6-layering, SDF-ch7-propagation]
|
||||
updated: 2026-04-30
|
||||
---
|
||||
|
||||
# 레이어드 데이터
|
||||
|
||||
## 한 줄 정의
|
||||
|
||||
데이터와 프로시저를 여러 독립적인 계층으로 구성하여, 기본 계산 코드를 전혀 수정하지 않고 단위·의존성·출처 등의 메타데이터를 병렬로 처리할 수 있는 구조.
|
||||
|
||||
## 핵심 내용
|
||||
|
||||
### 레이어링의 직관
|
||||
|
||||
프로그램에는 두 종류의 코드가 있다:
|
||||
- **주요 계산**: 실제 값을 만들어내는 핵심 로직 (`F = G*m1*m2/r²`)
|
||||
- **메타데이터 처리**: 단위 검사, 출처 추적, 오차 전파, 로깅, 단언(assertion)
|
||||
|
||||
일반적인 방식은 메타데이터 처리 코드를 주요 계산 코드에 섞어 넣는다. 이러면 코드가 복잡해지고, 새 메타데이터(예: 오차 범위 추가)를 더하려면 기존 코드를 수정해야 한다.
|
||||
|
||||
레이어링은 이 두 관심사를 완전히 분리한다:
|
||||
|
||||
> "The processing of the underlying data is not affected by the metadata, and the code for processing the underlying data does not even know about or reference the metadata. However, the metadata is processed by its own procedures, effectively in parallel with the data."
|
||||
|
||||
### 레이어드 데이텀의 구조
|
||||
|
||||
각 데이터 항목이 여러 계층의 정보를 담은 레이어드 데이텀이다:
|
||||
|
||||
```scheme
|
||||
(define G
|
||||
(layered-datum 6.67408e-11
|
||||
unit-layer (unit 'meter 3 'kilogram -1 'second -2)
|
||||
support-layer (support-set 'CODATA-2018)))
|
||||
```
|
||||
|
||||
여기서 `6.67408e-11`은 기본 레이어의 값이고, `unit-layer`와 `support-layer`는 메타데이터 레이어다. 기본 계층의 수치 연산은 단위나 출처를 전혀 모른다.
|
||||
|
||||
계산 결과:
|
||||
```scheme
|
||||
(pp (F M-earth M-Moon a-moon))
|
||||
; (base-layer 1.9805035857209e20)
|
||||
; (unit-layer (unit kilogram 1 meter 1 second -2))
|
||||
; (support-layer (support-set Wieczorek-2006 NASA-2006 ...))
|
||||
```
|
||||
|
||||
기본 레이어는 수치를, 단위 레이어는 자동으로 전파된 단위를, 지원 레이어는 사용된 모든 데이터 소스의 합집합을 보여준다.
|
||||
|
||||
### 레이어드 프로시저
|
||||
|
||||
데이터가 레이어드이면, 그것을 처리하는 프로시저도 레이어드여야 한다. 레이어드 프로시저는 각 레이어를 독립적으로 처리하는 향상된 프로시저다.
|
||||
|
||||
기본 레이어 프로시저(`*`)는 `number × number → number`를 계산한다.
|
||||
레이어드 `*`는 `layered-datum × layered-datum → layered-datum`을 계산하되, 각 레이어를 해당 레이어의 연산으로 처리한다:
|
||||
- 기본 레이어: 일반 수치 곱셈
|
||||
- 단위 레이어: 지수를 더함 (`m³ × kg = m³·kg`)
|
||||
- 지원 레이어: 소스 집합의 합집합
|
||||
|
||||
### 제네릭 프로시저와의 관계
|
||||
|
||||
레이어링은 제네릭 프로시저 위에 구축된다:
|
||||
|
||||
> "The decoration of a value is a generalization of the tagging used to support extensible generic operations."
|
||||
|
||||
Ch2에서 아리티를 해시 테이블로 함수에 첨부하는 것이 레이어링의 씨앗이다. Ch3의 추상 술어에서 타입 태그를 객체에 붙이는 것이 중간 단계다. Ch6의 레이어드 데이텀은 이 개념의 완전한 일반화다.
|
||||
|
||||
각 레이어를 별도의 제네릭 프로시저로 처리하면, 새 레이어는 새 제네릭 핸들러 세트로 표현된다. 기본 레이어의 코드는 전혀 모른다.
|
||||
|
||||
### 레이어가 될 수 있는 것들
|
||||
|
||||
SDF Ch6이 제시하는 레이어 후보들:
|
||||
- 수치 정밀도 선언
|
||||
- 타입 정보 (컴파일러용)
|
||||
- 단언(assertions) / 사전조건 / 사후조건
|
||||
- 대안 구현 제안 (퇴화성 지원)
|
||||
- 의존성 / 출처 추적
|
||||
- 로깅 / 프로파일링
|
||||
- 오차 범위
|
||||
|
||||
이 목록에서 중요한 점: 이것들 대부분은 전통적으로 코드에 직접 삽입되거나(예: 계약 프로그래밍의 assert), AOP로 처리하려 시도된다. 레이어링은 더 구조적인 방법이다.
|
||||
|
||||
### AOP와의 비교
|
||||
|
||||
AOP(관점 지향 프로그래밍, Aspect-Oriented Programming)도 "횡단 관심사(cross-cutting concerns)"를 분리하려는 시도다. 차이:
|
||||
|
||||
- AOP는 포인트컷(pointcut)이라는 별도 메커니즘으로 코드 삽입 위치를 지정. 코드와 관점이 별개 위치에 있어 이해하기 어렵다.
|
||||
- 레이어링은 데이터 자체가 레이어 정보를 담고, 프로시저가 이를 투명하게 처리. 새 레이어는 새 데이터 타입과 새 핸들러로 자연스럽게 추가된다.
|
||||
|
||||
### 의존성 레이어와 Ch7과의 연결
|
||||
|
||||
Ch6의 지원(support) 레이어는 Ch7에서 전파 시스템의 의존성 추적으로 발전한다. 각 전파된 값이 어떤 전제(premise)에서 파생됐는지를 추적하면:
|
||||
- 모순 발생 시 원인 전제를 특정할 수 있음
|
||||
- 의존성 지향 백트래킹이 가능해짐: 관련 없는 탐색 공간을 재방문하지 않음
|
||||
|
||||
레이어링이 없으면 이런 정교한 백트래킹은 구현하기 매우 어렵다.
|
||||
|
||||
## SDF에서의 등장
|
||||
|
||||
- [[SDF-ch2-dsl]]: 아리티를 함수에 첨부하는 것이 레이어링의 씨앗
|
||||
- [[SDF-ch3-generic-procedures]]: 타입 태그와 추상 술어가 레이어링의 중간 단계
|
||||
- [[SDF-ch6-layering]]: 핵심 챕터. 레이어드 데이텀, 레이어드 프로시저, 단위 레이어, 의존성 레이어. AOP와의 비교
|
||||
- [[SDF-ch7-propagation]]: 의존성 레이어를 전파 시스템에 통합하여 의존성 지향 백트래킹 구현
|
||||
|
||||
## 실천 시 주의점
|
||||
|
||||
**레이어 간 독립성 보장**: 기본 레이어의 코드가 다른 레이어의 존재를 모르도록 해야 한다. 기본 코드가 레이어를 인식하기 시작하면 레이어링의 핵심 이점(분리)이 사라진다.
|
||||
|
||||
**레이어 수의 관리**: 레이어가 너무 많아지면 각 연산에서 처리해야 하는 계층이 늘어난다. 실제로 필요한 레이어만 추가하고, 사용하지 않는 레이어는 제거하는 정책이 필요하다.
|
||||
|
||||
**레이어 값의 일관성**: 여러 레이어가 동일한 계산의 다른 측면을 처리하므로, 레이어들이 서로 일관된 결과를 내야 한다. 특히 단위 레이어가 오류를 감지할 때 기본 레이어도 올바른 값인지 확인해야 한다.
|
||||
|
||||
**성능 비용**: 모든 연산에서 모든 레이어를 처리하면 기본 계산의 여러 배 시간이 소요될 수 있다. 성능이 중요한 코드에서는 레이어 처리를 선택적으로 비활성화하는 메커니즘이 필요하다.
|
||||
|
||||
## 관련 개념
|
||||
|
||||
- [[additive-programming]] — 새 레이어 추가가 기존 코드 수정 없이 기능을 확장하는 가산적 방법
|
||||
- [[generic-procedures]] — 레이어드 프로시저의 기술적 기반
|
||||
- [[partial-information]] — 의존성 레이어가 부분 정보의 출처 추적을 담당
|
||||
- [[propagation]] — 의존성 레이어가 전파 시스템의 핵심 재료
|
||||
- [[degeneracy]] — 레이어로 대안 구현을 시스템에 명시적으로 등록 가능
|
||||
101
wiki/concepts/partial-information.md
Normal file
101
wiki/concepts/partial-information.md
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: 부분 정보
|
||||
tags: [concept, SDF]
|
||||
sources: [SDF-ch1-flexibility, SDF-ch4-pattern-matching, SDF-ch6-layering, SDF-ch7-propagation]
|
||||
updated: 2026-04-30
|
||||
---
|
||||
|
||||
# 부분 정보
|
||||
|
||||
## 한 줄 정의
|
||||
|
||||
완전한 정보 없이도 알려진 부분만으로 작업을 진행하고, 새 정보가 도착할 때 기존 정보와 일관성 있게 병합함으로써 점진적으로 더 완전한 답을 만들어가는 계산 전략.
|
||||
|
||||
## 핵심 내용
|
||||
|
||||
### "부분적으로 알기"의 계산적 의미
|
||||
|
||||
대부분의 계산 모델은 완전한 정보를 전제한다. 함수는 모든 인수가 주어진 후 실행되고, 결과는 하나의 확정된 값이다. 하지만 현실의 많은 문제는 처음부터 완전한 정보를 갖지 않는다:
|
||||
|
||||
- 센서 데이터는 불확실성을 가진다
|
||||
- 타입 추론은 각 식에서 국소적 제약만 얻고 전체 타입을 점진적으로 결정한다
|
||||
- 물리 상수는 측정 오차가 있으며, 여러 독립 측정을 결합하면 더 정확해진다
|
||||
|
||||
SDF는 이 문제를 "부분 정보를 일급 시민(first-class citizen)으로 취급"함으로써 해결한다.
|
||||
|
||||
### 부분 정보의 세 가지 표현 형태
|
||||
|
||||
**1. 패턴 변수 (Ch4)**
|
||||
|
||||
패턴에서 `(? a)`, `(?? t)` 같은 변수는 "아직 모르는" 부분을 나타낸다. 패턴 매칭은 이 미지수가 무엇이어야 하는지를 데이터로부터 결정(바인딩)한다.
|
||||
|
||||
단일화(unification)는 한 걸음 더 나간다: 두 패턴 *모두*에 미지수가 있을 때, 두 패턴을 동시에 만족하는 가장 일반적인 바인딩을 찾는다. 타입 추론에서 각 식이 국소적으로 생성하는 타입 제약들을 결합하여 전체 타입을 추론하는 것이 이 과정이다.
|
||||
|
||||
**2. 구간 값 (Ch7)**
|
||||
|
||||
```scheme
|
||||
(make-interval 2.5 3.1) ; "2.5 이상 3.1 이하인 무언가"
|
||||
```
|
||||
|
||||
두 구간의 교집합은 더 좁은 구간, 즉 더 정확한 정보다. 서로 다른 독립 측정에서 얻은 구간들의 교집합을 취하면 더 정확한 추정치를 얻는다. 이것이 별까지의 거리 계산 예시(Ch7.1)의 핵심이다.
|
||||
|
||||
**3. 전제를 가진 값 (Ch7)**
|
||||
|
||||
```scheme
|
||||
(tell! Vega-parallax interval 'FGWvonStruve1837)
|
||||
```
|
||||
|
||||
값에 그 값을 지지하는 전제(premise/support)를 붙임으로써, 어떤 소스에서 파생된 정보인지를 함께 전달한다. 이를 통해 파생 값의 출처를 자동으로 추적할 수 있다.
|
||||
|
||||
### 부분 정보의 단조적 병합
|
||||
|
||||
부분 정보의 핵심 연산은 **병합(merge)**이다. 병합은 단조적(monotonic)이어야 한다:
|
||||
|
||||
- 기존 정보를 잃지 않는다
|
||||
- 추가 정보는 기존 정보와 일관성이 있거나, 일관성이 없으면 모순으로 감지된다
|
||||
|
||||
구간의 경우 교집합이 병합이다. 구간이 비어 있으면 모순. 전제 집합의 경우 합집합이 병합이다.
|
||||
|
||||
이 단조성이 계산의 건전성(soundness)을 보장한다. 값이 결코 "잊혀지거나" "번복되지" 않으며, 오직 더 구체화될 뿐이다.
|
||||
|
||||
### 퇴화성과의 연결
|
||||
|
||||
Ch1에서 항법 시스템이 여러 독립적인 위치 추정값을 결합하는 예시는 부분 정보의 철학적 기반이다:
|
||||
|
||||
> "When multiple mechanisms provide partial information about the same question, the combination can be more accurate than any single source."
|
||||
|
||||
퇴화성(degeneracy)은 여러 독립적 메커니즘이 동일한 답을 목표로 삼는 구조다. 각 메커니즘은 부분 정보를 제공하고, 전파 시스템은 이를 병합하여 더 정확한 답을 만든다. Ch7의 전파 모델이 이 아이디어의 계산적 완성형이다.
|
||||
|
||||
### 의존성 추적과의 관계
|
||||
|
||||
부분 정보를 사용할 때, "이 정보가 어디서 왔는가?"를 추적하면 두 가지 이점이 있다:
|
||||
|
||||
1. **설명(explanation)**: 결론이 어떤 전제들에서 유래했는지 역추적
|
||||
2. **지능적 백트래킹**: 모순 발생 시 원인이 된 전제들만 의심하여, 관련 없는 탐색 공간을 재탐색하지 않음
|
||||
|
||||
Ch6의 의존성 레이어와 Ch7의 의존성 지향 백트래킹이 이 추적의 구현이다.
|
||||
|
||||
## SDF에서의 등장
|
||||
|
||||
- [[SDF-ch1-flexibility]]: 철학적 동기. 항법 시스템, 퇴화성, "부분 정보들의 결합이 개별 결과보다 더 정확하다"
|
||||
- [[SDF-ch4-pattern-matching]]: 패턴 변수와 단일화. "패턴은 부분적으로 알려진 정보를 표현하며, 단일화로 더 구체적인 정보를 생성한다"
|
||||
- [[SDF-ch6-layering]]: 의존성·출처 레이어가 부분 정보의 메타데이터. 파생 값의 지지 집합이 자동으로 전파됨
|
||||
- [[SDF-ch7-propagation]]: 부분 정보의 완전한 구현. 구간 산술, 전제를 가진 값, 의존성 지향 백트래킹
|
||||
|
||||
## 실천 시 주의점
|
||||
|
||||
**모순 처리 정책**: 정보가 모순될 때 시스템이 어떻게 반응할지 미리 설계해야 한다. 오류를 던지는가, 아니면 백트래킹하는가, 아니면 모순을 명시적으로 표현하는가? 정책이 불명확하면 잘못된 결과를 조용히 내놓는다.
|
||||
|
||||
**성능 고려**: 구간 전파와 의존성 추적은 순수 값 계산에 비해 상당한 오버헤드를 수반한다. 모든 값에 적용할 필요는 없다.
|
||||
|
||||
**결코 정보를 잃지 말 것**: 병합이 단조적이지 않으면(즉, 이전 정보를 버리면) 시스템의 건전성이 깨진다. "나중에 온 정보가 이전 정보를 덮어쓴다"는 설계는 부분 정보 패러다임과 충돌한다.
|
||||
|
||||
**"더 정확함"의 방향성**: 구간이 좁아지는 것이 "더 많이 알게 되는 것"이다. 구간이 넓어지는 것은 정보 손실을 의미한다. 병합 연산이 항상 좁히는(narrowing) 방향이어야 함을 확인해야 한다.
|
||||
|
||||
## 관련 개념
|
||||
|
||||
- [[degeneracy]] — 여러 독립 소스에서 부분 정보를 병합하는 구조적 원칙
|
||||
- [[propagation]] — 부분 정보의 단조적 축적과 전파의 계산 모델
|
||||
- [[layered-data]] — 값에 의존성·출처 정보를 첨부하는 메타데이터 레이어
|
||||
- [[additive-programming]] — 새 정보 소스를 추가하는 것이 가산적 확장
|
||||
- [[generic-procedures]] — 추상 술어를 통한 타입 태깅이 부분 정보와 연결
|
||||
134
wiki/concepts/propagation.md
Normal file
134
wiki/concepts/propagation.md
Normal file
@@ -0,0 +1,134 @@
|
||||
---
|
||||
title: 전파 모델
|
||||
tags: [concept, SDF]
|
||||
sources: [SDF-ch1-flexibility, SDF-ch5-evaluation, SDF-ch6-layering, SDF-ch7-propagation]
|
||||
updated: 2026-04-30
|
||||
---
|
||||
|
||||
# 전파 모델
|
||||
|
||||
## 한 줄 정의
|
||||
|
||||
공유 셀로 연결된 자율적 전파기들이 부분 정보를 단조적으로 축적·전파하는 다방향 계산 모델로, 의존성 추적과 결합하면 지능적 백트래킹이 가능한 비결정적 탐색 엔진이 된다.
|
||||
|
||||
## 핵심 내용
|
||||
|
||||
### 폰 노이만 모델에서의 이탈
|
||||
|
||||
전통적 프로그래밍은 폰 노이만 모델을 따른다: 변수는 값 하나를 담고, 표현식은 트리 구조를 타고 올라가며 평가되며, 제어 흐름은 순차적·방향성을 가진다.
|
||||
|
||||
이 모델의 근본적 한계: **단방향성**. `v = a * b`는 `v`를 계산하지만, `a`나 `b`를 역으로 구하려면 별도의 수식이 필요하다. 물리 법칙 `F = G*m1*m2/r²`는 6개의 역방향 관계를 함께 담고 있지만, 코드로는 하나의 방향만 표현된다.
|
||||
|
||||
전파 모델은 이 단방향성을 깬다:
|
||||
|
||||
> "The propagator model is built on the idea that the basic computational elements are propagators, autonomous independent machines interconnected by shared cells through which they communicate."
|
||||
|
||||
### 셀과 전파기: 두 기본 요소
|
||||
|
||||
**셀(cell)**: 정보를 축적하는 공유 저장소.
|
||||
- 하나의 값 대신 *현재까지 알려진 최선의 정보*를 담는다
|
||||
- 새 정보가 도착하면 기존 정보와 **병합(merge)**: 단조적, 정보를 잃지 않음
|
||||
- 모순이 감지되면 신호를 보냄
|
||||
|
||||
**전파기(propagator)**: 셀들을 연결하는 자율 기계.
|
||||
- 입력 셀들의 값을 읽어 계산하고 출력 셀에 정보를 추가
|
||||
- 입력이 변하면 자동으로 재실행
|
||||
- 독립적: 다른 전파기의 존재를 모른다
|
||||
|
||||
```scheme
|
||||
;; 곱셈 제약: a * b = c (세 방향 모두)
|
||||
(define-c:prop (c:* a b c)
|
||||
(p:* a b c) ; c = a * b
|
||||
(p:/ c a b) ; b = c / a (a가 알려지면)
|
||||
(p:/ c b a)) ; a = c / b (b가 알려지면)
|
||||
```
|
||||
|
||||
### 배선도 언어
|
||||
|
||||
전파 네트워크는 **배선도(wiring diagram)**로 표현된다. 표현식 트리가 아니라 셀(와이어)과 전파기(게이트)로 이루어진 회로도다. 이 비유는 매우 자연스럽다:
|
||||
- 회로의 와이어는 셀
|
||||
- 회로의 게이트는 전파기
|
||||
- 회로 구성 = 전파 네트워크 구성
|
||||
|
||||
배선도의 특성:
|
||||
- **다방향**: 와이어는 양방향. 신호가 어느 방향으로도 흐를 수 있음
|
||||
- **병렬적**: 여러 게이트가 독립적으로 활성화
|
||||
- **부분 결과 즉시 사용**: 전체 완료를 기다릴 필요 없음
|
||||
- **확장 가능**: 새 게이트를 연결하면 기능 추가
|
||||
|
||||
### 부분 정보와 구간 산술
|
||||
|
||||
셀은 숫자 하나 대신 구간을 값으로 가질 수 있다:
|
||||
|
||||
```scheme
|
||||
(make-interval 2.5 3.1) ; "2.5 이상 3.1 이하"
|
||||
```
|
||||
|
||||
두 측정이 같은 셀에 정보를 제공할 때 교집합을 취한다:
|
||||
```
|
||||
구간 [2.5, 3.1] ∩ [2.8, 3.5] = [2.8, 3.1] ; 더 정확해짐
|
||||
구간 [2.5, 3.1] ∩ [4.0, 5.0] = ∅ ; 모순!
|
||||
```
|
||||
|
||||
퇴화성 원칙의 구현: 여러 독립 소스가 동일한 셀에 구간 정보를 제공하고, 시스템이 자동으로 병합하여 더 정확한 답을 낸다.
|
||||
|
||||
### 의존성 추적과 전제
|
||||
|
||||
레이어링(Ch6)의 의존성 레이어를 전파 모델에 통합하면, 각 셀의 값이 어떤 전제(premise)에서 유래했는지를 추적한다:
|
||||
|
||||
```scheme
|
||||
(tell! Vega-parallax interval 'FGWvonStruve1837)
|
||||
;; 이 값의 전제: FGWvonStruve1837
|
||||
```
|
||||
|
||||
파생된 값의 전제 집합 = 모든 기여 전제들의 합집합. 전파되는 값에 의존성 정보가 자동으로 따라간다.
|
||||
|
||||
이를 통해 두 가지 능력이 생긴다:
|
||||
1. **설명(explanation)**: "이 결론이 어떤 데이터에서 유래했는가?"
|
||||
2. **지능적 백트래킹**: 모순의 원인을 파악하여 관련 없는 선택지를 재탐색하지 않음
|
||||
|
||||
### 의존성 지향 백트래킹
|
||||
|
||||
Ch5의 amb는 실패하면 직전 선택지로 돌아가는 연대기적(chronological) 백트래킹을 사용한다. 이는 이미 검증된 부분을 불필요하게 재탐색하는 비효율이 있다.
|
||||
|
||||
의존성 지향 백트래킹(dependency-directed backtracking):
|
||||
|
||||
1. 각 결정(hypothesis)이 전제 레이블을 가짐
|
||||
2. 모순 발생 → 모순을 일으킨 전제들의 집합(nogood set) 파악
|
||||
3. 이 전제들 중 하나를 번복하는 선택지로 직접 점프
|
||||
4. 관련 없는 선택지들은 건너뜀
|
||||
|
||||
예: 100개의 선택지 중 1번과 50번이 모순을 일으킨다면, 2~49번은 탐색하지 않고 바로 1번 또는 50번을 번복하는 선택지를 찾는다.
|
||||
|
||||
### 가산적 확장의 완성형
|
||||
|
||||
전파 네트워크는 가산적 프로그래밍의 이상에 가장 가깝다:
|
||||
|
||||
> "The structure is additive: new ways to contribute information can be included just by adding new parts to a network, whether simple propagators or entire subnetworks."
|
||||
|
||||
새 추정 방법은 새 전파기 추가. 새 제약은 새 제약 전파기 추가. 기존 네트워크는 전혀 수정하지 않는다.
|
||||
|
||||
## SDF에서의 등장
|
||||
|
||||
- [[SDF-ch1-flexibility]]: 탐색적 행동, 퇴화성, 생성-검증 메커니즘으로 동기 부여. 전파 모델의 철학적 선구자
|
||||
- [[SDF-ch5-evaluation]]: amb가 연대기적 백트래킹을 구현. 전파 모델이 이를 의존성 지향으로 업그레이드한다는 관계
|
||||
- [[SDF-ch6-layering]]: 의존성 레이어가 전파 시스템의 핵심 재료. 없으면 의존성 지향 백트래킹 불가
|
||||
- [[SDF-ch7-propagation]]: 핵심 챕터. 셀/전파기 구조, 구간 산술, 전제를 가진 값, 의존성 지향 백트래킹, 배선도 언어
|
||||
|
||||
## 실천 시 주의점
|
||||
|
||||
**수렴 보장**: 전파기 네트워크가 무한 루프 없이 고정점(fixed point)에 수렴한다는 보장이 필요하다. 단조적 병합이 이를 보장하는 핵심 조건이다. 비단조적 업데이트(값을 번복하거나 구간을 넓히는 것)가 있으면 수렴이 보장되지 않는다.
|
||||
|
||||
**디버깅의 어려움**: 다방향 전파는 "이 값이 어디서 왔는가?"를 추적하기 어렵게 만들 수 있다. 의존성 추적이 이 문제를 부분적으로 해결하지만, 복잡한 네트워크에서는 여전히 어렵다.
|
||||
|
||||
**순환 의존성**: 셀 A의 값이 전파기를 통해 셀 B에 영향을 주고, 셀 B가 다시 셀 A에 영향을 줄 때 순환이 발생한다. 이것이 의미 있는 제약(예: `x + y = z, x - y = w`)인지 아니면 실수인지 구분이 필요하다.
|
||||
|
||||
**확장의 위험**: "새 전파기를 추가만 하면 된다"는 가산성이 실제로는 기존 네트워크의 동작을 변경할 수 있다. 새 전파기가 기존 셀에 더 좁은 구간을 강요하면 이전에 유효했던 값이 모순이 될 수 있다.
|
||||
|
||||
## 관련 개념
|
||||
|
||||
- [[additive-programming]] — 배선도에 전파기 추가가 가장 순수한 형태의 가산적 확장
|
||||
- [[partial-information]] — 셀은 부분 정보를 단조적으로 축적하는 저장소
|
||||
- [[degeneracy]] — 여러 전파기가 동일 셀에 독립적 추정값을 제공하는 퇴화적 구조
|
||||
- [[layered-data]] — 의존성 레이어가 전파 시스템의 지능적 백트래킹을 가능하게 함
|
||||
- [[domain-specific-language]] — 배선도 언어 자체가 제약 기반 DSL
|
||||
Reference in New Issue
Block a user