파라메트릭 콘트랙트
파라메트릭 콘트랙트 (Parametric Contracts)
파라메트릭 콘트랙트는 다형성(polymorphic) 함수를 보호하기 위한 콘트랙트예요. contract-out의 #:∃, #:exists, #:∀, #:forall 키워드가 가장 편리하고, racket/contract/parametric 라이브러리는 그 밖의 일반 목적 파라메트릭 콘트랙트 몇 개를 더 제공해요.
출처: Racket Reference
본문
(require racket/contract/parametric)
package: base
파라메트릭 콘트랙트를 사용하는 가장 편리한 방법은 contract-out의 #:∃, #:exists, #:∀, #:forall 키워드를 사용하는 거예요. racket/contract/parametric 라이브러리는 몇 가지 더 일반 목적의 파라메트릭 콘트랙트를 제공해요.
syntax
(parametric->/c (x ...) c)
파라메트릭 다형성 함수를 위한 콘트랙트를 만들어요. 각 함수는 c로 보호되는데, 각 x는 c 안에 바인딩되며 함수가 적용될 때마다 인스턴스화되는 다형성 타입을 가리켜요.
함수가 적용될 때마다 parametric->/c 콘트랙트는 각 x에 대해 새 불투명 래퍼(opaque wrapper)를 구성해요. 다형성 함수로 들어가는 값(즉 parametric->/c에 대해 음의 위치(negative position)에서 어떤 x로 보호되는 값)은 해당 불투명 래퍼로 감싸져요. 다형성 함수에서 나오는 값(즉 parametric->/c에 대해 양의 위치(positive position)에서 어떤 x로 보호되는 값)은 적절한 래퍼가 있는지 검사돼요. 래퍼가 있으면 풀리고, 없으면 콘트랙트 위반이 신호돼요.
Examples:
> (define/contract (good-swap a b)
swap-ctc
(values b a))
> (good-swap 1 2)
2
1
> (define/contract (bad-swap a b)
swap-ctc
(values a b))
> (bad-swap 1 2)
bad-swap: broke its own contract
promised: B
produced: #<A>
in: the range of
(parametric->/c (A B) (-> A B (values B A)))
contract from: (function bad-swap)
blaming: (function bad-swap)
(assuming the contract is correct)
at: eval:5:0
> (define/contract (copy-first a b)
swap-ctc
(values a a))
> (let ((v 'same-symbol)) (copy-first v v))
copy-first: broke its own contract
promised: B
produced: #<A>
in: the range of
(parametric->/c (A B) (-> A B (values B A)))
contract from: (function copy-first)
blaming: (function copy-first)
(assuming the contract is correct)
at: eval:7:0
> (define/contract (inspect-first a b)
swap-ctc
(if (integer? a)
(+ a b)
(raise-user-error "an opaque wrapped value is not an integer")))
> (inspect-first 1 2)
an opaque wrapped value is not an integer
procedure
(new-∀/c [name]) → contract?
name : (or/c symbol? #f) = #f
새 보편(universal) 콘트랙트를 구성해요.
보편 콘트랙트는 음의 위치(예: 함수 입력)에서 모든 값을 받아들이고 정확한 값을 숨기도록 불투명 구조체로 감싸요. 양의 위치(예: 함수 반환)에서는 이전에 음의 위치에서 받아들여진 값들만(래퍼를 검사해서) 받아들여요.
name은 오류 메시지에서 콘트랙트를 식별하는 데 사용되며, 기본값은 new-∀/c의 어휘적 문맥에 기반한 이름이에요.
예를 들어 이 콘트랙트:
(let ([a (new-∀/c 'a)])
(-> a a))
는 항등 함수(또는 종료되지 않는 함수)를 기술해요. 즉 a의 첫 번째 사용은 음의 위치에 나타나므로 그 함수에 대한 입력은 불투명 구조체로 감싸져요. 그런 다음 함수가 반환할 때, a의 두 번째가 양의 위치에 있으므로 결과가 감싸졌는지 검사돼요.
new-∀/c 콘트랙트 생성자는 new-∃/c와 쌍을 이뤄요.
procedure
(new-∃/c [name]) → contract?
name : (or/c symbol? #f) = #f
새 존재(existential) 콘트랙트를 구성해요.
존재 콘트랙트는 양의 위치(예: 함수 반환)에서 모든 값을 받아들이고 정확한 값을 숨기도록 불투명 구조체로 감싸요. 음의 위치(예: 함수 입력)에서는 이전에 양의 위치에서 받아들여진 값들만(래퍼를 검사해서) 받아들여요.
name은 오류 메시지에서 콘트랙트를 식별하는 데 사용되며, 기본값은 new-∀/c의 어휘적 문맥에 기반한 이름이에요.
예를 들어 이 콘트랙트:
(let ([a (new-∃/c 'a)])
(-> (-> a a)
any/c))
는 항등 함수(또는 종료되지 않는 함수)를 받아들이고 임의의 값을 반환하는 함수를 기술해요. 즉 a의 첫 번째 사용은 양의 위치에 나타나므로 그 함수에 대한 입력은 불투명 구조체로 감싸져요. 그런 다음 함수가 반환할 때, a의 두 번째가 음의 위치에 있으므로 결과가 감싸졌는지 검사돼요.
new-∃/c 콘트랙트 생성자는 new-∀/c와 쌍을 이뤄요.
더 알아보기
- Contracts — 콘트랙트 전반.
- contract-out — 콘트랙트로 모듈 계약을 지정하는 폼.