나는이 원리가 무엇인지, 언어 디자인에 왜 그토록 중요한지 이해하려고 애 쓰고있었습니다.
기본적으로 expr
언어의 모든 표현 에 대해이 구문과 정확히 동일해야합니다.
(function () { return expr; })()
또한 루비는이 원칙을 준수하지만 파이썬은 그렇지 않다는 이야기를 들었습니다. 왜 이것이 사실인지 또는 전혀 사실인지 이해할 수 없습니다.
expr
현재 스택 트레이스를 얻는다고 가정 합니다.
나는이 원리가 무엇인지, 언어 디자인에 왜 그토록 중요한지 이해하려고 애 쓰고있었습니다.
기본적으로 expr
언어의 모든 표현 에 대해이 구문과 정확히 동일해야합니다.
(function () { return expr; })()
또한 루비는이 원칙을 준수하지만 파이썬은 그렇지 않다는 이야기를 들었습니다. 왜 이것이 사실인지 또는 전혀 사실인지 이해할 수 없습니다.
expr
현재 스택 트레이스를 얻는다고 가정 합니다.
답변:
나는 "테넌트의 대응 원리"에 대해 들어 본 적이 없으며 언어 디자인에서 그 중요성이 훨씬 적습니다. 인터넷 검색을 통해 2006 년 Neal Gafter 블로그를 통해 자신이 생각하는 것과 폐쇄에 적용해야한다고 생각하는 블로그를 만들 수 있습니다. 그리고 포럼의 다른 모든 사람들은 Gafter의 입장을 언급하는 것 같습니다.
그러나 Douglas Crockford ( http://java.sys-con.com/node/793338/ )의 "TCP"에 대한 언급은 다음과 같습니다 . 일부
반환 진술 및 중단 진술과 같이 이러한 방식으로 묶을 수없는 몇 가지 사항이 있습니다. 테넌트의 통신 원칙 (또는 TCP) 주장을지지하는 사람들은 악취의 증상입니다. 아뿔싸! 후각 환각에 대처할 필요없이 언어 디자인은 이미 충분히 어렵다. 문제를 더 잘 이해하기 위해 Tennent의 1981 년 책 Principles of Programming Languages를 구입했습니다.
통신 원칙은 규범이 아니라 서술 적이라는 것이 밝혀졌다 . 그는이를 사용하여 (지금은 잊어 버린) 파스칼 프로그래밍 언어를 분석하여 변수 정의와 프로 시저 매개 변수 사이의 일치 성을 보여줍니다. Tennent는 반환 진술의 대응이 부족하다는 것을 문제로 식별하지 않습니다 .
따라서 "Tennent 's Corspondence Principle"이라는 이름이 잘못 사용 된 것 같습니다. Neal이 말한 모든 것을 "Gafter 's Imagined and Possibly Generalized TCP"라고 부릅니다. 어떤 경우에도 절판 된 책 표지 뒤에 숨길만큼 충분하지 않은 경우
나는 이것이 잘 설계된 언어가 프로그래머가 자연스럽게 기대하는 것을 수행한다는 일반적인 규칙의 일부로 본다. 클로저로 리팩토링하려는 코드 블록이 있고 개별 코드 행에 대해 실제로 생각하지 않고 적절한 구문으로 해당 블록을 래핑하면 해당 블록이 클로저에서 동일한 작업을 수행 할 것으로 예상됩니다 인라인했다. 일부 문장에서 "this"(아마도 암시 적으로)라는 키워드를 사용하고 클로저 내부에서 "this"라는 언어가 클로저를 정의하는 메서드를 정의하는 클래스가 아니라이를 나타내는 데 사용되는 익명 클래스를 참조하게되면 그 문장이 바뀌었고, 내 코드 블록은 더 이상 내가 생각하는 것을하지 않으며 버그를 추적하고 코드를 변경하여 클로저에서 작동하는 방법을 찾아야합니다.
스마트 리팩토링 도구를 갖춘 IDE를 사용하면 클로저를 추출하고 잠재적 인 문제를 감지하며 추출 된 코드를 자동으로 조정하여 문제를 해결할 수도 있습니다.
Claus Reinke : Tennent의 "시맨틱 원칙에 기반한 언어 디자인"에 관한 원리에
대한 흥미로운 해석을 제공합니다.
"통신은 우리가
let(this=obj, x=5) { .. }
과
((function(x) { .. }).call(obj,5))
형식 매개 변수 목록에서 수행 할 수있는 모든 작업은 선언에서 수행 할 수 있어야하며 그 반대도 가능해야합니다. "[아래의 Reinke도 참조하십시오.]
RD Tennent : 시맨틱 원리를 기반으로 한 언어 설계 방법
"언어 시맨틱을 프로그래밍 방식으로 접근하는 방식에서 파생 된 원리를 기반으로하는 두 가지 언어 설계 방법은 언어 파스칼에 대한 응용 프로그램에서 설명하고 설명합니다. 우선 원칙은 파라 메트릭과 선언 메커니즘과 둘째로, 세트 이론으로부터 적응 된 프로그래밍 언어에 대한 추상화의 원리. 파스칼의 몇 가지 유용한 확장과 일반화는 배열 파라미터 문제에 대한 솔루션과 모듈화 기능을 포함하여 이러한 원리를 적용함으로써 나타난다.
Claus Reinke : Haskell의 "기능적 프로그래밍, 언어 설계 및 지속성"
언어 디자인에 Tennent의 CP가 왜 중요한지 질문에 답하기 위해 Neal Gafter 를 인용 하고 싶습니다 .
테넌트의 원칙은 위반이 언어, 결함, 불규칙, 불필요한 제한, 예기치 않은 상호 작용 또는 합병증 등으로 나타나는 경향이 있기 때문에 매우 강력합니다.
TCP를 위반하면 클로저가 비 폐쇄 코드처럼 작동하지만 TCP를 위반하면 그렇지 않다는 것을 알게되면 미래에 일부 프로그래머에게 피해를 줄 수 있습니다.
RE Python은이 원칙을 따르지 않습니다. 일반적으로 원칙을 따릅니다. 기본 예 :
>>> x = ['foo']
>>> x
['foo']
>>> x = (lambda: ['foo'])()
>>> x
['foo']
그러나 파이썬은 표현식을 정의 하고 문장을 따로 . 이후 if
분기, while
루프, 파괴 할당 및 다른 문을 사용할 수 없습니다 lambda
전혀 표현의 테넌트 원리의 문자는 그들에게 적용되지 않습니다. 그럼에도 불구하고 파이썬 표현 만 사용하도록 자신을 제한하면 여전히 Turing complete 시스템이 생성됩니다. 따라서 나는 이것이 원칙을 위반하는 것으로 보지 않는다. 또는 그것이 원칙을 위반한다면, 진술과 표현을 따로 정의하는 언어는 그 원칙을 따를 수 없습니다.
또한 lambda
표현식 의 본문이 스택 추적을 캡처하거나 VM에서 다른 내부 검사를 수행하는 경우 차이가 발생할 수 있습니다. 그러나 제 생각에는 이것이 위반으로 간주되어서는 안됩니다. 만약expr
와 (lambda: expr)()
반드시 동일한 바이트 코드로 컴파일 한 후 원리는 정말 컴파일러하지 의미에 관한; 그러나 그들이 다른 바이트 코드로 컴파일 할 수 있다면 VM 상태가 각각 동일하다고 기 대해서는 안됩니다.
이해 구문을 사용하면 놀라운 일이 발생할 수 있지만 테넌트 원칙을 위반하는 것은 아닙니다. 예:
>>> [x for x in xrange(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [f() for f in [lambda: x for x in xrange(10)]] # surprise!
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
>>> # application of Tennent principle to first expression
... [(lambda: x)() for x in xrange(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [f() for f in [(lambda x: lambda: x)(x) for x in xrange(10)]] # force-rebind x
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> map(lambda f:f(), map(lambda x: lambda: x, xrange(10))) # no issue with this form
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
놀랍게도 목록 이해가 정의 된 결과입니다. 위의 '놀람'이해는이 코드와 동일합니다.
>>> result = []
>>> for x in xrange(10):
... # the same, mutable, variable x is used each time
... result.append(lambda: x)
...
>>> r2 = []
>>> for f in result:
... r2.append(f())
...
>>> r2
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
이런 식으로 볼 때, 위의 '놀람'이해력은 놀랍지 않으며 테넌트 원칙을 위반하지 않습니다.