매크로를 사용하거나 사용하지 않을 때 [closed]


12

프로그램에서 매크로를 언제 사용해야합니까?

이 질문은 @tarsius 의 유익한 답변에서 영감을 얻었습니다 . 많은 사람들이 다른 사람들과 공유 할 수있는 훌륭한 경험이 있다고 생각합니다. 이 질문이 위대한 주관적인 질문 중 하나가되어 Emacs 프로그래머에게 도움 이되기를 바랍니다 .

답변:


13

엄지 손가락의 법칙

구문 에는 매크로를 사용 하지만 의미 에는 사용 하지 마십시오 .

일부 구문 설탕에는 매크로를 사용하는 것이 좋지만 매크로 자체 또는 확장에 상관없이 매크로 본문에 논리를 넣는 것은 좋지 않습니다. 그렇게해야한다고 생각되면 대신 함수를 작성하고 구문 설탕에 대한 "컴패니언 매크로"를 작성하십시오. 간단히 말해서, let매크로에 들어 있으면 문제가 있습니다.

왜?

매크로에는 많은 단점이 있습니다.

  • 컴파일 타임에 확장되므로 여러 버전에서 바이너리 호환성 을 유지하기 위해 신중하게 작성해야합니다 . 예를 들어, 매크로 확장에 사용 된 함수 또는 변수를 제거하면이 매크로를 사용하는 모든 종속 패키지의 바이트 코드가 손상됩니다. 즉, 호환되지 않는 방식으로 매크로 구현을 변경하면 모든 종속 패키지를 다시 컴파일해야합니다. 바이너리 호환성이 항상 명백한 것은 아닙니다.
  • 직관적 인 평가 순서 를 유지하기 위해주의를 기울여야합니다 . 양식을 너무 자주 또는 잘못 평가하거나 잘못된 장소 등을 평가하는 매크로를 작성하는 것은 쉬운 일입니다.
  • 바인딩 의미론을주의 해야합니다 . 매크로는 확장 사이트 에서 바인딩 컨텍스트를 상속하므로 어휘 변수가 무엇인지, 어휘 바인딩이 사용 가능한지 여부를 미리 알 수 없습니다. 매크로는 두 바인딩 의미론 모두에서 작동해야합니다.
  • 이와 관련하여 클로저만드는 매크로를 작성할 때주의해야합니다 . 어휘 정의가 아닌 확장 사이트에서 바인딩을 가져 오므로 닫는 내용과 클로저를 만드는 방법에 대해주의하십시오 (확장 사이트에서 어휘 바인딩이 활성화되어 있는지 여부와 사용자가 모르는 것을 기억하십시오) 클로저를 사용할 수도 있습니다).

1
어휘 바인딩 대 매크로 확장에 대한 노트가 특히 흥미 롭습니다. 감사합니다 루나 리온. (내가 작성한 코드에 대해 어휘 바인딩을 사용하도록 설정 한 적이 없으므로 생각했던 충돌이 아닙니다.)
phils

6

필자의 경험과 다른 사람의 Elisp 코드를 읽고, 디버깅하고, 수정하면서 가능한 한 매크로를 피하는 것이 좋습니다.

매크로에 대한 분명한 점은 인수를 평가하지 않는다는 것입니다. 즉, 매크로에 래핑 된 복잡한 표현식이있는 경우 매크로 정의를 찾지 않으면 해당 표현식이 평가되는지 여부와 그 컨텍스트를 알 수 없습니다. 자신의 매크로에 대한 정의를 알고 있기 때문에 (현재는 몇 년이 지나지 않을 것이므로) 이것은 큰 문제가되지 않을 수 있습니다.

이 단점은 함수에는 적용되지 않습니다. 모든 인수는 함수 호출 전에 평가됩니다. 이는 알려지지 않은 디버깅 상황에서 평가 복잡성을 구축 할 수 있음을 의미합니다. 간단한 데이터를 반환하고 전역 상태를 건드리지 않는다고 가정하는 한 알려지지 않은 함수의 정의를 바로 건너 뛸 수도 있습니다.

다시 말하면, 매크로는 언어의 일부가됩니다. 알 수없는 매크로가있는 코드를 읽는 경우, 모르는 언어로 된 코드를 읽는 것입니다.

상기 예약에 대한 예외 :

표준 매크로 좋아 push, pop, dolist당신을 위해 정말 많은 작업을 수행하고, 다른 프로그래머에게 잘 알려져있다. 기본적으로 언어 사양의 일부입니다. 그러나 자체 매크로를 표준화하는 것은 사실상 불가능합니다. 위의 예제와 같이 간단하고 유용한 것을 생각해 내기가 어려울뿐만 아니라 모든 프로그래머가 그것을 배우도록 설득하는 것이 더 어렵습니다.

또한, 나는 매크로를 신경 쓰지 않습니다 save-selected-window: 그들은 상태를 저장하고 복원하고 같은 방식으로 인수를 평가합니다 progn. 매우 간단하여 실제로 코드를 더 단순하게 만듭니다.

OK 매크로의 또 다른 예는 defhydra또는 과 같은 것일 수 있습니다 use-package. 이 매크로는 기본적으로 자체 미니 언어와 함께 제공됩니다. 그리고 그들은 여전히 ​​간단한 데이터를 취급하거나 다음과 같이 행동 progn합니다. 또한 이들은 일반적으로 최상위 수준이므로 매크로 인수에 의존하는 호출 스택에는 변수가 없으므로 조금 더 간단합니다.

나쁜 매크로의 예는, 내 의견에서, magit-section-action그리고 magit-section-caseMagit있다. 첫 번째 것은 이미 제거되었지만 두 번째는 여전히 남아 있습니다.


4

나는 무뚝뚝해질 것이다. "시맨틱이 아닌 구문을위한 사용"이 무엇을 의미하는지 이해하지 못한다. 이것이이 답변의 일부가 lunaryon 의 답변을 다루는 이유 이고, 다른 부분은 원래 질문에 대답하려는 나의 시도 일 것입니다.

프로그래밍에서 "의미"라는 단어가 사용될 때, 언어 작성자가 언어를 해석하기로 선택한 방식을 나타냅니다. 이것은 일반적으로 언어의 문법 규칙을 독자가 잘 알고있는 다른 규칙으로 변환하는 일련의 공식으로 요약됩니다. 예를 들어, Plotkin은 자신의 저서 " 작업 의미론에 대한 구조적 접근 방식"에서 논리 파생 언어를 사용하여 추상 구문이 평가하는 내용 (프로그램 실행의 의미)을 설명합니다. 다른 말로하면, 문법이 어떤 수준에서 프로그래밍 언어를 구성하는 것이라면, 그것은 의미론을 가져야 만한다.

그러나 공식적인 정의를 잊어 버리십시오. 결국 의도를 이해하는 것이 중요합니다. 따라서 lunaryon은 프로그램에 새로운 의미가 추가되지 않고 일종의 약어 인 경우 독자가 매크로를 사용하도록 권장하는 것처럼 보입니다. 나에게 이것은 매크로가 실제로 어떻게 사용되는지와는 이상하게 들린다. 다음은 새로운 의미를 명확하게 만드는 예입니다.

  1. defun 언어의 필수 부분 인 친구는 함수 호출을 설명하는 것과 같은 용어로 설명 할 수 없습니다.

  2. setf함수의 작동 방식을 설명하는 규칙은 식의 효과를 설명하기에 부적합합니다 (setf (aref x y) z).

  3. with-output-to-string또한 매크로 내부의 코드 의미를 변경합니다. 마찬가지로 with-current-buffer다른 with-매크로도 많이 있습니다.

  4. dotimes 기능 측면에서 유사한 것은 설명 될 수 없다.

  5. ignore-errors 랩핑하는 코드의 의미를 변경합니다.

  6. Emacs Lisp에서 사용되는 cl-lib패키지, eieio패키지 및 기타 거의 모든 유비쿼터스 라이브러리 에는 매크로가 많이 포함되어 있지만 함수 형식은 다른 해석을하는 것처럼 보일 수 있습니다.

따라서 매크로는 새로운 의미를 언어에 도입하는 도구입니다. 나는 그것을하는 다른 방법을 생각할 수 없습니다 (적어도 Emacs Lisp에서는 아닙니다).


매크로를 사용하지 않을 때 :

  1. 그들이 새로운 의미를 가진 새로운 구조를 도입하지 않을 때 (즉, 함수가 그처럼 잘 작동 할 것입니다).

  2. 이것이 일종의 편의 일 때 (즉 , 더 짧게 만들기 위해 mvb확장되는 이름의 매크로 cl-multiple-value-bind를 만드는 것).

  3. 많은 오류 처리 코드가 매크로에 의해 숨겨 질 것으로 예상 할 때 (알려진 바와 같이, 매크로는 디버그하기 어렵습니다).


함수보다 매크로를 강력하게 선호하는 경우 :

  1. 함수 호출에 의해 도메인 특정 개념이 가려 질 때 예를 들어, 동일한 context인수를 연속해서 호출해야하는 여러 함수에 전달해야하는 경우 context인수가 숨겨진 매크로로 랩핑하는 것이 좋습니다.

  2. 함수 형태의 일반 코드가 지나치게 장황한 경우 (Emacs Lisp의 베어 반복 구조는 너무 장황하고 프로그래머가 저수준 구현 세부 사항에 불필요하게 노출시킵니다).


삽화

아래는 컴퓨터 과학에서 의미론이 의미하는 바에 대한 직관을 제공하기위한 요약본입니다.

다음 구문 규칙만으로 매우 간단한 프로그래밍 언어가 있다고 가정하십시오.

variable := 1 | 0
operation := +
expression := variable | (expression operation expression)

이 언어와 우리는 같은 "프로그램"만들 수 있습니다 (1+0)+1또는 0또는 ((0+0))

그러나 시맨틱 규칙을 제공하지 않는 한 이러한 "프로그램"은 의미가 없습니다.

이제 우리의 언어에 (의미 적) 규칙이 있다고 가정 해 봅시다.

0+0  0+1  1+0  1+1  (exp)
---, ---, ---, ---, -----
 0   1+0   1    0    exp

이제이 언어를 사용하여 실제로 계산할 수 있습니다. 즉, 우리는 구문 표현을 취하여 추상적으로 이해하고 (즉, 추상적 구문으로 변환) 의미 론적 규칙을 사용하여이 표현을 조작 할 수 있습니다.

Lisp 계열 언어에 대해 이야기 할 때, 함수 평가의 기본 의미 론적 규칙은 대략 람다 계산법의 규칙입니다.

(f x)  (lambda x y)   x
-----, ------------, ---
 fx        ^x.y       x

인용 및 매크로 확장 메커니즘은 메타 프로그래밍 도구라고도합니다. 즉 , 프로그래밍 대신 프로그램 에 대해 이야기 할 수 있습니다. 그들은 "메타 레이어"를 사용하여 새로운 의미를 만들어서 이것을 달성합니다. 이러한 간단한 매크로의 예는 다음과 같습니다.

(a . b)
-------
(cons a b)

이것은 실제로 Emacs Lisp의 매크로는 아니지만 가능했을 수도 있습니다. 나는 단순성을 위해서만 선택했다. 가장 가까운 후보 가 함수로 (f x)해석 f되지만 a반드시 함수일 필요는 없으므로 위에 정의 된 의미 규칙은이 경우에 적용 되지 않습니다.


1
"Syntactic sugar"는 실제로 컴퓨터 과학의 용어가 아닙니다. 엔지니어가 다양한 것을 의미하기 위해 사용하는 것입니다. 그래서 나는 확실한 대답이 없습니다. 의미론에 대해 이야기하고 있기 때문에, 다음과 같은 형식의 구문을 해석하는 규칙 (x y)은 대략 "y를 평가 한 다음 이전 평가의 결과로 x를 호출합니다"입니다. 을 쓸 때 (defun x y)y는 평가되지 않으며 x도 아닙니다. 다른 의미를 사용하여 동등한 효과를 가진 코드를 작성할 수 있는지 여부는 자체 의미가있는이 코드 조각을 거부하지 않습니다.
wvxvw

1
두 번째 의견을 다시 말씀해주십시오. 사용중인 매크로의 정의를 참조하십시오. 방금 매크로를 통해 새로운 의미 규칙이 도입 된 예를 들었습니다. 적어도 CS의 정확한 의미에서. 나는 정의에 대한 논쟁이 생산적이라고 생각하지 않으며, CS에 사용 된 정의가이 논의에 가장 적합하다고 생각한다.
wvxvw

1
글쎄 ... 당신은 "의미론"이라는 단어가 의미하는 바에 대한 자신의 아이디어를 가지고 있습니다. 무시하십시오. 대답에서 링크 된 책은 CS 해당 과정에서 가르치는 의미에 대한 표준 교과서입니다. Wiki의 해당 기사 ( en.wikipedia.org/wiki/Semantics_%28computer_science%29) 를 읽으면 실제 내용을 볼 수 있습니다. 예제는 (defun x y)함수 대 애플리케이션 을 해석하기위한 규칙입니다 .
wvxvw

글쎄, 나는 이것이 나의 토론 방식이라고 생각하지 않는다. 시도하고 시작하는 것은 실수였습니다. 이 모든 점에서 아무런 의미가 없기 때문에 의견을 제거했습니다. 시간을 낭비해서 죄송합니다.
lunaryorn
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.