파라메트릭 콘트랙트

파라메트릭 콘트랙트 (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로 보호되는데, 각 xc 안에 바인딩되며 함수가 적용될 때마다 인스턴스화되는 다형성 타입을 가리켜요.

함수가 적용될 때마다 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와 쌍을 이뤄요.

더 알아보기