WITH-SLOTS — 슬롯을 변수처럼 다루는 매크로
WITH-SLOTS — 슬롯을 변수처럼 다루는 매크로
CLOS 객체의 슬롯 값을 읽고 쓰려면 보통 slot-value를 쓰는데, 매번 긴 표현을 반복하다 보면 코드가 지저분해져요. 슬롯을 마치 일반 변수처럼 쓸 수 있다면 훨씬 읽기 좋겠죠. with-slots가 바로 그 일을 해주는 매크로예요. 이 문서는 매크로(macro) 문서예요.
출처: CLHS WITH-SLOTS
본문
문법 (Syntax)
with-slots (slot-entry*) instance-form declaration* form*
=> result*
slot-entry ::= slot-name | (variable-name slot-name)
인자와 값 (Arguments and Values)
slot-name— 슬롯 이름(slot name)이에요. 평가되지 않아요.variable-name— 변수 이름이에요. 평가되지 않아요.instance-form— form이에요. 평가되어instance를 만들어요.instance— 객체(object)예요.declaration—declare표현식이에요. 평가되지 않아요.forms— implicit progn이에요.results—forms가 반환하는 값들이에요.
설명 (Description)
with-slots는 instance 안의 slot-name들이 가리키는 슬롯을 마치 변수처럼 참조할 수 있는 **어휘 환경(lexical environment)**을 확립해요. 그런 문맥에서 슬롯의 값은 슬롯 이름을 어휘 바인딩된 변수처럼 써서 읽을 수 있어요. 슬롯 값의 설정은 setf와 setq를 모두 쓸 수 있어요.
with-slots는 변수처럼 나타난 슬롯 이름을 slot-value 호출로 번역해요.
예제 (Examples)
with-slots에서 슬롯 이름이 실제로 값에 접근하는지, 그리고 (variable-name slot-name) 형태로 다른 이름을 붙여 쓸 수 있는지 확인해 볼게요.
(defclass thing ()
((x :initarg :x :accessor thing-x)
(y :initarg :y :accessor thing-y)))
=> #<STANDARD-CLASS THING 250020173>
(defmethod (setf thing-x) :before (new-x (thing thing))
(format t "~&Changing X from ~D to ~D in ~S.~%"
(thing-x thing) new-x thing))
(setq thing (make-instance 'thing :x 0 :y 1)) => #<THING 62310540>
(with-slots (x y) thing (incf x) (incf y)) => 2
(values (thing-x thing) (thing-y thing)) => 1, 2
(setq thing1 (make-instance 'thing :x 1 :y 2)) => #<THING 43135676>
(setq thing2 (make-instance 'thing :x 7 :y 8)) => #<THING 43147374>
(with-slots ((x1 x) (y1 y))
thing1
(with-slots ((x2 x) (y2 y))
thing2
(list (list x1 (thing-x thing1) y1 (thing-y thing1)
x2 (thing-x thing2) y2 (thing-y thing2))
(setq x1 (+ y1 x2))
(list x1 (thing-x thing1) y1 (thing-y thing1)
x2 (thing-x thing2) y2 (thing-y thing2))
(setf (thing-x thing2) (list x1))
(list x1 (thing-x thing1) y1 (thing-y thing1)
x2 (thing-x thing2) y2 (thing-y thing2)))))
>> Changing X from 7 to (9) in #<THING 43147374>.
=> ((1 1 2 2 7 7 8 8)
9
(9 9 2 2 7 7 8 8)
(9)
(9 9 2 2 (9) (9) 8 8))
안쪽 with-slots에서 x1에 새 값을 setq하면 그 값(9)이 thing1의 슬롯에 반영되고, (setf (thing-x thing2) ...)는 밖의 with-slots가 이미 평가된 뒤라 x1 변수에는 영향이 없어요.
영향받는 요소 (Affected By)
defclass
예외 상황 (Exceptional Situations)
어떤 slot-name이 instance의 슬롯 이름이 아니면 결과가 정의되어 있지 않아요.
더 알아보기 (See Also)
with-accessorsslot-valuesymbol-macrolet
참고 (Notes)
다음과 같은 with-slots 표현식은
(with-slots (slot-entry1 ... slot-entryn) instance-form form1 ... formk)
다음과 같은 코드로 확장돼요.
(let ((in instance-form))
(symbol-macrolet (Q1 ... Qn) form1 ... formk))
여기서 Qi는 slot-entryi가 심볼이면
(slot-entryi () (slot-value in 'slot-entryi))
이고, (variable-namei slot-namei) 형태면
(variable-namei () (slot-value in 'slot-namei))
이에요.