Programming

Lisp 계열 언어에서의 mutation 방법에 대한 고찰

steloflute 2021. 5. 29. 00:31

Common Lisp에서는 setf macro가 container에 대해 mutation할 때, 매크로로 처리한다.

즉, container에 대한 read 함수와 별개로 write 함수가 구현되어 있어야 한다. (여기서는 rplaca)

예제:

* (let ((a (list 1 2))) (setf (nth 0 a) 3) a)
(3 2)
* (macroexpand '(setf (nth 0 a) 3))
(LET* ((#:LIST (NTHCDR 0 A)) (#:NEW 3)) (SB-KERNEL:%RPLACA #:LIST #:NEW))

 

Clojure에서는 mutation이 필요한 것은 atom 등으로 되어 있어야 한다.

그래서 container에 대해 read 함수만 있고 특별한 mutation 함수가 없어도 된다. container에는 box를 넣어 놓고, box에 물건을 바꿔 넣는다. (마치 C++의 pass by reference 개념과 비슷하다.)

이렇게 mutation할 수 있는 type을 따로 만드는 것이 우아한 방법인 것 같다. 구현도 편하고...

 

코드 예제:

Clojure 1.10.1
user=> (def a (list (atom 1) (atom 2)))
#'user/a
user=> a
(#object[clojure.lang.Atom 0x71652c98 {:status :ready, :val 1}] #object[clojure.lang.Atom 0x1a1d3c1a {:status :ready, :val 2}])
user=> (reset! (nth a 0) 3)
3
user=> a
(#object[clojure.lang.Atom 0x71652c98 {:status :ready, :val 3}] #object[clojure.lang.Atom 0x1a1d3c1a {:status :ready, :val 2}])