openlang

advice-add 전에 동적 let부터 써 보세요

advice는 실행 중인 에디터를 확장할 때 제일 좋은 기능이면서, 남의 init 파일에서는 제일 못 믿는 기능이에요. (제 것도 포함해서요.)

advice-add에 :around를 주면 정의는 건드리지 않고 함수를 감쌀 수 있고, advice-remove로 다시 벗겨 낼 수 있어요. 에디터를 끄지 않고 *scratch*에서 전부 할 수 있다는 게 매력의 전부예요. 문제는 advice가 모든 호출자에게 적용된다는 거예요. 그런 걸 원한 적 없는 패키지까지요. 다행히 describe-function은 도움말 버퍼에 걸려 있는 advice를 보여 주니까, 뭔가 이상하게 굴면 그 함수에 C-h f부터 해 봐요.

사람들이 잊는 또 하나의 도구는 동적 스코프예요. defvar로 정의한 변수는 특수 변수라서, 호출을 let으로 감싸면 그 호출이 닿는 모든 곳에서 끝까지 다시 바인딩되고, let을 빠져나오면 원래 값이 돌아와요. lexical-binding을 켜도 일반 지역 변수만 렉시컬이 되고 특수 변수는 여전히 동적이에요. 신경 쓰는 호출 하나만 case-fold-search로 감싸 바인딩하는 편이 뭔가를 advice하는 것보다 깔끔할 때가 많아요.

대충의 순서는 이래요. 패키지가 훅을 주면 훅, 동작을 정하는 변수가 있으면 동적 let, advice는 마지막이에요.

이어지는 대화 1

서로의 근거를 따라
  1. 고유 링크

    어느 스레드가 그 변경을 보는가? let과 advice를 가르는 것이 바로 이 질문이다. Clojure에서 ^:dynamic var에 대한 binding은 스레드별이고 폼을 빠져나가면 되돌려진다. alter-var-root는 모든 스레드의 루트를 바꾼다. advice가 결국 이것이다. with-redefs는 범위가 있어 보이지만 루트를 잠시 바꿔 끼우는 것이라, 그동안 다른 스레드도 바뀐 것을 본다.

    같은 함수에 with-redefs를 쓰는 테스트 두 개를 병렬로 돌려 보라. 각각 어떤 정의를 받는가? case-fold-search를 감싼 let은 binding 쪽이다. advice는 루트 쪽이고, 전역 변경으로 따져야 한다.

같은 질문의 다른 갈래

  1. using 선언 리팩터링은 Dispose를 Publish() 뒤로 미룬다

    문법은 짧아지고 수명은 길어져요. C#의 using 선언은 자원을 감싸는 스코프가 끝날 때 해제해요. 리팩터링이 단순 정리처럼 보일 때는 이 스코프를 놓치기 쉬워요. 중첩된 using 블록에서 input을 얻고, Read(i…