LOAD-TIME-VALUE 특수 연산자
LOAD-TIME-VALUE 특수 연산자 (load-time-value)
load-time-value는 Common Lisp의 특수 연산자로, 폼의 평가를 런타임(로드 시점) 환경까지 미뤄서 단 한 번 실행한 결과를 리터럴 객체처럼 쓸 수 있게 해요. 매번 호출 때마다 다시 계산하지 않고, 프로그램이 로드되는 시점의 값을 상수처럼 고정하고 싶을 때 유용해요.
문법 (Syntax)
load-time-value form &optional read-only-p => object
인자와 값 (Arguments and Values)
form— 폼이며, 아래 설명대로 평가돼요.read-only-p— 불리언(boolean)이며, 평가되지 않아요.object—form을 평가한 결과의 **주값(primary value)**이에요.
설명 (Description)
load-time-value는 폼의 평가를 표현식이 런타임 환경에 있을 때까지 미루는 메커니즘이에요. Section 3.2 (Compilation)를 보세요.
read-only-p는 결과를 상수 객체(constant object)로 간주할 수 있는지 나타내요. t이면 결과는 읽기 전용 수량이라서, 구현이 적절하다면 읽기 전용 공간으로 복사되거나 다른 프로그램의 유사 상수 객체와 병합(coalesce)될 수 있어요. nil(기본값)이면 결과는 절대 복사되거나 병합되면 안 되고, 수정 가능한 데이터로 간주해야 해요.
load-time-value 표현식을 compile-file이 처리하면, 컴파일러는 form에 대해 정상적인 의미 처리(매크로 확장, 기계어 번역 등)를 수행하지만 form의 실행은 **로드 시점에 빈 어휘 환경(null lexical environment)**에서 일어나도록 배치해요. 이 평가 결과는 런타임에 리터럴 객체로 취급돼요. form의 평가가 파일이 로드될 때 단 한 번만 일어나는 건 보장되지만, 파일 안의 최상위 폼 평가와 관련된 평가 순서는 구현 정의(implementation-dependent)예요.
load-time-value 표현식이 compile로 컴파일된 함수 안에 나타나면, form은 컴파일 시점에 빈 어휘 환경에서 평가돼요. 이 컴파일 타임 평가 결과는 컴파일된 코드에서 리터럴 객체로 취급돼요.
load-time-value 표현식을 eval이 처리하면, form은 빈 어휘 환경에서 평가되고 값 하나가 반환돼요. eval이 처리하는 표현식을 암묵적으로 컴파일(또는 부분 컴파일)하는 구현은, 그 컴파일이 행해질 때 form을 단 한 번만 평가할 수도 있어요.
같은 리스트 (load-time-value form)을 두 번 이상 평가하거나 컴파일하면, form이 한 번만 평가될지 여러 번 평가될지가 구현 정의예요. 이는 평가/컴파일되는 표현식이 부분 구조(substructure)를 공유할 때와, 같은 form이 eval이나 compile로 여러 번 처리될 때 둘 다 일어날 수 있어요. load-time-value 표현식은 한 곳 이상에서 참조될 수 있고 eval로 여러 번 평가될 수 있으므로, 각 실행이 새 객체를 반환할지 아니면 다른 실행과 같은 객체를 반환할지도 구현 정의예요. 결과 객체를 파괴적으로 수정할 때는 조심해야 해요.
equal로 같지만 동일(identical)하지는 않은 두 (load-time-value form) 리스트를 평가하거나 컴파일하면, 그 값들은 항상 서로 다른 form 평가에서 나와요. read-only-p가 t가 아니면 그 값들은 병합될 수 없어요.
예제 (Examples)
;;; The function INCR1 always returns the same value, even in different images.
;;; The function INCR2 always returns the same value in a given image,
;;; but the value it returns might vary from image to image.
(defun incr1 (x) (+ x #.(random 17)))
(defun incr2 (x) (+ x (load-time-value (random 17))))
;;; The function FOO1-REF references the nth element of the first of
;;; the *FOO-ARRAYS* that is available at load time. It is permissible for
;;; that array to be modified (e.g., by SET-FOO1-REF); FOO1-REF will see the
;;; updated values.
(defvar *foo-arrays* (list (make-array 7) (make-array 8)))
(defun foo1-ref (n) (aref (load-time-value (first *my-arrays*) nil) n))
(defun set-foo1-ref (n val)
(setf (aref (load-time-value (first *my-arrays*) nil) n) val))
;;; The function BAR1-REF references the nth element of the first of
;;; the *BAR-ARRAYS* that is available at load time. The programmer has
;;; promised that the array will be treated as read-only, so the system
;;; can copy or coalesce the array.
(defvar *bar-arrays* (list (make-array 7) (make-array 8)))
(defun bar1-ref (n) (aref (load-time-value (first *my-arrays*) t) n))
;;; This use of LOAD-TIME-VALUE permits the indicated vector to be coalesced
;;; even though NIL was specified, because the object was already read-only
;;; when it was written as a literal vector rather than created by a constructor.
;;; User programs must treat the vector v as read-only.
(defun baz-ref (n)
(let ((v (load-time-value #(A B C) nil)))
(values (svref v n) v)))
;;; This use of LOAD-TIME-VALUE permits the indicated vector to be coalesced
;;; even though NIL was specified in the outer situation because T was specified
;;; in the inner situation. User programs must treat the vector v as read-only.
(defun baz-ref (n)
(let ((v (load-time-value (load-time-value (vector 1 2 3) t) nil)))
(values (svref v n) v)))
여기서 핵심 대비는 incr1과 incr2예요. incr1은 #.(random 17)으로 컴파일 시점에 값을 고정하지만, incr2는 load-time-value로 이미지마다 로드 시점에 한 번 값을 정해요. 그리고 read-only-p 인자가 t인 버전(bar1-ref)은 시스템이 배열을 복사하거나 병합할 수 있게 허용하지만, nil인 버전(foo1-ref)은 그 배열을 수정해도 된다고 약속하지 않아요.
영향 (Affected By)
없음.
예외 상황 (Exceptional Situations)
없음.
함께 보기 (See Also)
compile-file,compile,eval- Section 3.2.2.2 (Minimal Compilation)
- Section 3.2 (Compilation)
참고 (Notes)
load-time-value는 인용된 구조(quoted structure) 밖, "평가(evaluation)" 위치에 나타나야 해요. 인용된 구조 안에서 load-time-value를 쓰고 싶어진다면, 아마도 backquote 리더 매크로가 더 적합해요. Section 2.4.6 (Backquote)를 보세요.
read-only-p에 nil을 지정하는 건 이미 읽기 전용이 된 객체를 수정 가능하게 만들려는 수단이 아니에요. 수정 가능한 객체에 대해, 이 연산이 그 객체를 읽기 전용으로 만들려는 게 아님을 말해주는 것뿐이에요.