2019년 1월 17일 목요일

[on lisp] 5.2 Orthogonality (직교성)

5.2 Orthogonality (직교성)

An orthogonal language is one in which you can express a lot by combining a small number of operators a lot of different ways.
직교 언어는 많은 다른 방식으로 소수의 연산자를 결합하여 많은 것을 표현할 수 있는 언어이다.

장난감 블록은 서로 직각이다. 플라스틱 모델 키트는 전혀 직각이 아니다.
complement의 가장 큰 장점을 언어를 보다 직관적으로 만든다.
complement 이전, Common Lisp는 remove-if, remove-if-not 혹은 subst-if, subst-if-not 같은 함수 쌍들이 있었다.
이제는 complement를 통해 그들 중 절반이 없어도 일이 가능하다.

setf 매크로 또한 Lisp의 직교성을 향상시킨다.
이전의 Lisp방언은 종종 데이터 읽기와 쓰기를 위한 함수 쌍을 가지고 있었다.
예를들어, property-lists에서는 속성을 설정하는 한 가지 기능과 그 속성에 대해 질의 하는 다른 기능이 있다.
Common Lisp에는 후자만 있다. 속성을 설정하려면 setf와 함께 사용하면 된다.
;; Figure 5.1: Returning destructive equivalents.
(setf (get 'ball 'color) 'red)

(defvar *!equivs* (make-hash-table))

(defun ! (fn)
  (or (gethash fn *!equivs*) fn))  ;; 해시맵에 들어간 함수를 가져온다.

(defun def! (fn fn!) 
  (setf (gethash fn *!equivs*) fn!))  ;; 파괴적인 해시맵을 가져와서 fn의 키값에 fn!를 넣는다.
우리는 Common Lisp를 더 작게 만들지는 못하겠지만, 거의 비슷한 것을 할 수 있다: use a smller subset of it(더 작은 부분 집합을 사용하라)
complement함수와 setf함수 같이 새로운 operator(연산자)를 정의하면 이 목표를 달성하는데 도움이 될까?
기능을 쌍으로 그룹화하는 방법은 적어도 한 가지 더 있다.(반대기능을 하는 걸로 그룹핑하는 것 외에 다른방식으로 그룹핑할 수도 있다.)
remove-if와 delete-if, reverse와 nreverse, append와 nconc와 같이 많은 기능이 파괴적인 버전(desctructive version)을 쌍으로 제공한다.
이렇게 파괴적인,비파괴적인 함수를 대응(쌍)으로 연산자를 정의함으로써, 파괴함수를 직접 참조하지 않아도 된다.

Figure 5.1은 파괴적인 대응(쌍) 개념을 지원하는 코드를 포함하고있다.
일단 *!equivs*는 글로벌 변수이며 (make-hash-table)로 파괴적인 함수에 매핑된다.
느낌표(!)는 파괴적인 동등을 반환한다.
def!는 그것들을 설정한다.
!(bang)연산자의 이름은 Scheme규약에 따온 것인데 !가 사이드이펙트를 생성한다는 것이다.
자 이제 우리가 정의를 해보자.
(def! #'remove-if #'delete-if)
;; 이제 아래처럼 할 필요없이
(delete-if #'oddp lst)
;; 아래처럼 사용한다.
(funcall (! #'remove-if) #'oddp lst)
;; 여기 Common Lisp의 어색함(awkwardness)는 
;; Scheme에서 더 잘 볼 수 있는 생각의 우아함을 숨긴다.
;; 아래가 스킴이 생각하는 무언가인듯. 꽤나 우아하다.
((! remove-if) oddp lst)

특이하게 조립을 한다.
더 큰 직교성 뿐만 아니라, !연산자는 몇 가지 다른 이점을 가지고 있다.
프로그램을 더 선명하게 만든다. 왜냐하면 우리는 즉시 (! #'foo)가 foo와 같지만(여기에는 호출방식도 같고) 파괴적인 것을 볼 수 있다.
또한, 파괴적인 연산자는 소스코드에서 뚜렷하고 인지할 수 있는 형태로 나타나는데,
이것은 우리가 버그를 찾을 때 특별한 주의를 기울여야 하기 때문에 좋다.

function과 destructive counterpart사이의 관계는 일반적으로 런타임 이전에 알려 지므로 정의하는 것이 가장 효율것이다,
'!' 연산자를 정의할 때 매크로로 정의하던가, 혹은 !를 위한 read macro를 제공한다.

댓글 없음 :

댓글 쓰기