2019년 1월 3일 목요일

[on lisp] 3.3 Functional Interfaces

3.3 Functional Interfaces

어떤 사이드이팩트는 다른 것들보다 나쁘다.
예를들자. 이 함수는 nconc를 호출한다.
(defun qualify (expr)
  (nconc (copy-list expr) (list 'maybe)))
이것은 참조 투명성을 유지한다. 같은 인수를 넣으면 항상 같은 값을 반환할 것이다. (리스트를 수정하고 있다고는 하지만 새로 복사를 하기 때문에 상관없다.)
그러므로 호출자의 관점에서는 이건 순수 함수형 코드일 것이다.
우리는 이전에서 본 bad-reverse와 같다고 할 수 없다. (실제 매개변수를 변경하고 있는 녀석과는 다르다.)

모든 사이드이팩트를 나쁘게 다루는 것 보다. 이런 경우들과는 구별이 되도록 해야겠다.
비공식적으로, 아무도 소유하지 않은 것을 수정하는 것은 무해하다 말할 수 있다.
예를 들어, 위에 nconc는 무해하다. 왜냐하면 매개변수로 받은 expr는 새롭게 만들어져서 더해졌기 때문이다.
새롭게 만들어진 이 녀석은 아무도 소유할 수 없었다.

일반적으로, 우리는 소유권에 대해 함수가 아닌, 함수의 호출에 대해 이야기 해야 한다.
아래 코드에서 아무도 변수 x를 소유하지 않고 있다
(let ((x 0))
  (defun total (y)
    (incf x y)))
이 호출의 영향은 다음 호출에서 나타날 것이다.
따라서 다음의 규칙을 따라야 한다: 주어진 호출은 고유하게 소유하는 것을 안전하게 수정할 수 있어야 한다.

이렇게 사이드이팩트가 있어보여도 없는 것이 있고 있는 것이 있다.
책에 예제를 보면
(defun ok (x)
  (nconc (list 'a x) (list 'c)))

(defun not-ok (x)
  (nconc (list 'a) x (list 'c)))
ok함수는 nconc를 해도 매개변수로 온 아이가 문제되지 않는다. list함수로 새로 만들어졌기 때문이다.
not-ok는 그대로 사용하기 때문에 nconc가 매개변수를 수정할 여지가 있다.

이렇게 사이드이팩트가 밖으로 나가지만 않으면 괜찮다고 생각하는 것 것 같다.

어떻게 해야 좀 더 안전한가. 함수 안에 변수를 넣기만 하면 수정을 해도 사이드이팩트가 없는 것을 보장하도록 하는 방법은?
(defun f (x)
  (let ((val (g x)))
    ;; 여기서 val은 정말 안전한가?
))
안전하지 않다. g가 identity함수라면 그냥 던져진다.
어떻게 해야 하나 그러면, 함수형프로그래밍이 되는가. f라는 함수안에서만 변경되어야 하는데 밖으로 나가진다니... 그렇다면 모든 함수가 위험하고
결국 믿지 못하게 된다. 이렇게 모든 경우를 계속 확인해야 하는가.

Instead of worrying about the whole program, we now only have to consider the subtree beginning with f.
f함수ctional Interfaces

어떤 사이드이팩트는 다른 것들보다 나쁘다.
예를들자. 이 함수는 nconc를 호출한다.
(defun qualify (expr)
  (nconc (copy-list expr) (list 'maybe)))
이것은 참조 투명성을 유지한다. 같은 인수를 넣으면 항상 같은 값을 반환할 것이다. (리스트를 수정하고 있다고는 하지만 새로 복사를 하기 때문에 상관없다.)
그러므로 호출자의 관점에서는 이건 순수 함수형 코드일 것이다.
우리는 이전에서 본 bad-reverse와 같다고 할 수 없다. (실제 매개변수를 변경하고 있는 녀석과는 다르다.)

모든 사이드이팩트를 나쁘게 다루는 것 보다. 이런 경우들과는 구별이 되도록 해야겠다.
비공식적으로, 아무도 소유하지 않은 것을 수정하는 것은 무해하다 말할 수 있다.
예를 들어, 위에 nconc는 무해하다. 왜냐하면 매개변수로 받은 expr는 새롭게 만들어져서 더해졌기 때문이다.
새롭게 만들어진 이 녀석은 아무도 소유할 수 없었다.

일반적으로, 우리는 소유권에 대해 함수가 아닌, 함수의 호출에 대해 이야기 해야 한다.
아래 코드에서 아무도 변수 x를 소유하지 않고 있다
(let ((x 0))
  (defun total (y)
    (incf x y)))
이 호출의 영향은 다음 호출에서 나타날 것이다.
따라서 다음의 규칙을 따라야 한다: 주어진 호출은 고유하게 소유하는 것을 안전하게 수정할 수 있어야 한다.

이렇게 사이드이팩트가 있어보여도 없는 것이 있고 있는 것이 있다.
책에 예제를 보면
(defun ok (x)
  (nconc (list 'a x) (list 'c)))

(defun not-ok (x)
  (nconc (list 'a) x (list 'c)))
ok함수는 nconc를 해도 매개변수로 온 아이가 문제되지 않는다. list함수로 새로 만들어졌기 때문이다.
not-ok는 그대로 사용하기 때문에 nconc가 매개변수를 수정할 여지가 있다.

이렇게 사이드이팩트가 밖으로 나가지만 않으면 괜찮다고 생각하는 것 것 같다.

어떻게 해야 좀 더 안전한가. 함수 안에 변수를 넣기만 하면 수정을 해도 사이드이팩트가 없는 것을 보장하도록 하는 방법은?
(defun f (x)
  (let ((val (g x)))
    ;; 여기서 val은 정말 안전한가?
))
안전하지 않다. g가 identity함수라면 그냥 던져진다.
어떻게 해야 하나 그러면, 함수형프로그래밍이 되는가. f라는 함수안에서만 변경되어야 하는데 밖으로 나가진다니... 그렇다면 모든 함수가 위험하고
결국 믿지 못하게 된다. 이렇게 모든 경우를 계속 확인해야 하는가.

Instead of worrying about the whole program, we now only have to consider the subtree beginning with f.
f함수의 하위구조를 한번 보자.
변경할 내용을 함수로 감싸서 실행하는 방식이 수정 사이드이팩트를 막아주지 못한다는 것을 알았다. 우리는 또 다른 법칙이 필요하다.

1. Thus one should avoid writing functions whose return values incorporate quoted objects.
따라서 반환 값에 (quote가 붙은)객체가 포함 된 함수를 작성하지 않아야합니다.
(defun exclaim (expression)
  (append expression '(oh my)))

> (exclaim '(lions and tigers and bears))
(LIONS AND TIGERS AND BEARS OH MY)
> (nconc * '(goodness))
(LIONS AND TIGERS AND BEARS OH MY GOODNESS)
이렇게 되면 안에 있는 함수를 바꿀 수 있게 된다.
> (exclaim '(fixnums and bignums and floats))
(FIXNUMS AND BIGNUMS AND FLOATS OH MY GOODNESS)
왜냐하면 '(oh my)가 리턴되었고 그 값은 함수 안에 있는 참조랑 같게 되는 것이다. 그러니 밖에서 GOODNESS가 더해져도
함수 안에 있는 '(oh my)도 변해서 '(oh my goodness)가 되는 것이다.

해결책은
(defun exclaim (expression)
  (append expression (list 'oh 'my)))
이렇게 하면 새로 리스트가 만들어 진거니까 괜찮아진다.

댓글 없음:

댓글 쓰기