Files
softwaredesign/wiki/sources/SDF-ch3-generic-procedures.md
minsung ea46da91db feat: SDF wiki 컴파일 — 챕터별 한국어 소스 페이지 8개
Software Design for Flexibility (Hanson & Sussman 2021) 전문을
wiki/sources/ 아래 챕터별 한국어 wiki 페이지로 컴파일

- SDF-overview: 전체 개요, 챕터 관계도, 공통 테마
- SDF-ch1: 가산적 프로그래밍 철학, 퇴화성, 유연성 비용
- SDF-ch2: 컴비네이터, DSL, 래퍼, 도메인 모델
- SDF-ch3: 제네릭 프로시저, 자동 미분, 트라이 디스패치
- SDF-ch4: 패턴 매칭, 항 재작성, 단일화, 타입 추론
- SDF-ch5: eval/apply, lazy eval, amb, call/cc
- SDF-ch6: 레이어드 데이텀/프로시저, 단위 산술, 의존성 추적
- SDF-ch7: 전파 모델, 부분 정보 결합, 의존성 지향 백트래킹

wiki/index.md Sources 섹션 등록, wiki/log.md 기록

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 14:42:02 +09:00

157 lines
6.3 KiB
Markdown

---
title: "Ch3: Variations on an Arithmetic Theme"
tags: [source, SDF]
source: "Software Design for Flexibility, Hanson & Sussman (2021)"
chapter: 3
updated: 2026-04-30
---
# Ch3: Variations on an Arithmetic Theme
## 핵심 아이디어
**술어-디스패치 제네릭 프로시저(predicate-dispatched generic procedures)**를 중심으로, 기존 프로그램의 동작을 확장하는 강력하지만 위험한 기법을 다룬다. 산술 연산의 의미를 수치에서 기호, 함수, 미분 객체로 확장하는 과정을 통해 제네릭 시스템의 설계 원칙을 보여준다.
## 주요 개념
### 3.1 산술 패키지 컴비네이터
**산술 패키지(arithmetic package)**: 연산자 이름에서 구현으로의 매핑. `install-arithmetic!`으로 사용자 환경에 설치.
**적용 가능성 명세(applicability specification)**: 술어 리스트의 리스트. 인수들이 케이스 중 하나를 만족하면 해당 핸들러 적용.
```scheme
;; 수치 산술: (number? number?) 케이스만 적용
;; 기호 산술: (number? symbolic?), (symbolic? number?),
;; (symbolic? symbolic?) 케이스에 적용
(define combined-arithmetic
(extend-arithmetic symbolic-extender numeric-arithmetic))
```
`add-arithmetics` 컴비네이터로 산술들을 결합. 그러나 컴비네이터 방식은 한계가 있다:
- 부품의 형태를 미리 결정해야 함
- 계층화된 구조(예: 함수 산술)를 나중에 확장하기 어려움
- 자기 참조 구조 불가
### 3.2 확장 가능한 제네릭 프로시저
컴비네이터의 한계를 극복하기 위해 **동적으로 확장 가능한 제네릭 프로시저** 도입.
```scheme
;; 생성
(define plus (simple-generic-procedure 'plus 2 #f))
;; 핸들러 추가
(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)))
```
**구현 메커니즘**:
1. `generic-procedure-constructor`: 디스패치 전략을 인자로 받아 제네릭 프로시저 생성기 반환
2. `dispatch-store`: 핸들러 저장/검색 전략 캡슐화
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))
```
**주의**: 핸들러 추가 순서에 의존성이 생길 수 있다. 적용 가능성이 겹치는 규칙들의 순서에 따라 결과가 달라짐.
### 3.3 자동 미분 (Automatic Differentiation)
제네릭 프로시저의 탁월한 응용 예시. **미분 객체(differential object)** `[x, δx]`를 새 데이터 타입으로 도입:
$$[x, \delta x] \xrightarrow{f} [f(x), Df(x)\delta x]$$
각 산술 연산에 미분 객체 처리 핸들러를 추가하기만 하면, 함수의 합성을 통해 연쇄 법칙이 자동으로 적용된다.
```scheme
(define (derivative f)
(define (the-derivative x)
(let* ((dx (make-new-dx))
(value (f (d:+ x (make-infinitesimal dx)))))
(extract-dx-part value dx)))
the-derivative)
;; sqrt의 미분 핸들러
(define diff:sqrt
(diff:unary-proc sqrt (lambda (x) (/ 1 (* 2 (sqrt x))))))
```
고차 함수(함수를 반환하는 함수)에서의 미분은 기술적 복잡성이 있다: dx 태그 충돌 문제를 `replace-dx` 메커니즘으로 해결.
### 3.4 효율적인 디스패치
**단순 선형 스캔의 문제**: 많은 규칙에서 동일한 술어를 반복 호출.
**트라이(Trie) 기반 디스패치**:
- 인수 시퀀스를 트라이로 인덱싱
- 첫 번째 인수의 타입으로 후보 규칙 집합을 좁힘
- 중복 술어 평가 제거
**캐싱**:
- 인수의 구현 타입 태그를 키로 사용
- 이전 디스패치 결과를 해시 테이블에 캐시
- `cache-wrapped-dispatch-store`로 임의의 디스패치 전략에 캐싱 추가
### 3.5 사용자 정의 타입
**추상 술어(abstract predicate)**: 비용이 높은 술어 평가를 메모이즈. 객체에 태그를 붙여 이후 디스패치에서 비싼 술어 재평가 불필요.
```scheme
(define prime-number?
(simple-abstract-predicate 'prime-number slow-prime?))
(set-predicate<=! prime-number? exact-integer?)
;; 이후 exact-integer? 도 prime-number 객체를 인식
```
**서브타입 관계 선언**: `set-predicate<=!`로 부분집합 관계 명시. 가장 특수적인 핸들러가 선택됨.
### 3.5.4 어드벤처 게임
제네릭 프로시저와 사용자 정의 타입의 실용적 응용 예시.
```scheme
;; 이동 행동이 4개 인수 타입의 조합에 따라 달라짐
(define generic-move!
(most-specific-generic-procedure 'generic-move! 4 #f))
(define-generic-procedure-handler generic-move!
(match-args mobile-thing? place? bag? person?) ...)
(define-generic-procedure-handler generic-move!
(match-args mobile-thing? bag? place? person?) ...)
(define-generic-procedure-handler generic-move!
(match-args person? place? place? person?) ...)
```
단일 디스패치 OOP와 달리, 모든 인수 타입의 조합에 따라 행동이 결정되므로 더 자연스러운 분해가 가능하다.
## 핵심 인용
> "Systems built by combinators, as in section 3.1, result in beautiful diamond-like systems. This is sometimes the right idea... 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."
> "Construction of a system on a substrate of extensible generic procedures is a powerful idea... a user may extend them to support arithmetic on quaternions, vectors, matrices, integers modulo a prime, functions, tensors, differential forms..."
> "It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures." (Alan Perlis)
## 관련 개념
- [[SDF-ch2-dsl]] — 컴비네이터 기반 접근의 한계에서 제네릭 프로시저로 이행
- [[SDF-ch4-pattern-matching]] — 제네릭 프로시저의 패턴 기반 확장
- [[SDF-ch6-layering]] — 제네릭 프로시저와 밀접한 레이어링
- [[SDF-ch7-propagation]] — 타입 태그와 의존성 추적의 결합
- [[SDF-overview]]