Qual é a boa explicação do Princípio da Correspondência da Tennent?

21

Eu me vi lutando para ver o que é esse princípio e por que é tão importante para o design da linguagem.

Basicamente, afirma que para cada expressão exprna linguagem deve ser exatamente o mesmo que esta construção:

(function () { return expr; })()

Também ouvi dizer que Ruby obedece a esse princípio, enquanto Python não. Não entendo por que isso é verdade, ou se é verdade.

Andrew
fonte
3
Não consigo entender por que está fora do tópico, alguém poderia me explicar?
Andrew
3
Existem algumas questões; Eu retocamos e estou enviando para os programadores, onde discussões como essa são um pouco mais bem-vindas.
Arrancado
1
Ruby não obedece a este princípio: Assume exprobtém o rastreamento de pilha atual.
Landei

Respostas:

18

Eu nunca tinha ouvido falar antes sobre o "Princípio da Correspondência de Tennent" e menos ainda sobre isso ser importante no design da linguagem. Pesquisando as expressões, parece que tudo leva a um blog de Neal Gafter, de 2006, que estabelece o que ele pensa que é e como ele acha que deve se aplicar aos fechamentos. E quase todos os outros nos fóruns parecem se referir à entrada de Gafter.

Aqui está, no entanto, uma menção do referido "TCP" por Douglas Crockford (um nome que eu conheço e confio): http://java.sys-con.com/node/793338/ . Em parte

Há algumas coisas que não podem ser incluídas dessa maneira, como declarações de retorno e declarações de quebra, que os defensores do Princípio da Correspondência da Tennent (ou TCP) alegam ser um sintoma de mau cheiro. Yow! O design da linguagem já é bastante difícil sem ter que lidar com alucinações olfativas. Então, para entender melhor o problema, comprei uma cópia do livro de 1981 da Tennent, Principles of Programming Languages.

Acontece que o Princípio da Correspondência é descritivo , não prescritivo . Ele o usa para analisar a (já esquecida) linguagem de programação Pascal, mostrando uma correspondência entre definições de variáveis ​​e parâmetros de procedimento. A Tennent não identifica a falta de correspondência das declarações de retorno como um problema .

Parece, portanto, que o nome "Princípio da Correspondência de Tennent" é mal utilizado, e tudo o que Neal fala possivelmente deve ser chamado de "TCP imaginado e possivelmente generalizado de Gafter" ... ou algo assim. De qualquer forma, não é o suficiente para se esconder atrás de uma cortina de nome de livro esgotada

Nas Banov
fonte
1
+1 para "TCP imaginado e possivelmente generalizado de
Gafter
9

Eu vejo isso como parte de uma regra geral de que uma linguagem bem projetada faz o que um programador naturalmente esperaria. Se eu tenho um bloco de código que quero refatorar em um fechamento e envolvo esse bloco com a sintaxe apropriada sem realmente pensar nas linhas de código individuais, espero que esse bloco faça a mesma coisa no fechamento como ele fez inline. Se algumas declarações usam a palavra-chave "this" (talvez implicitamente) e a linguagem faz com que "this" usado dentro do fechamento se refira a uma classe anônima usada para representá-lo, em vez da classe que define o método que define o fechamento, o significado de essas declarações foram alteradas, meu bloco de código não faz mais o que penso e preciso rastrear um bug e descobrir como alterar meu código para funcionar no fechamento.

O problema também pode ser mitigado com um IDE com ferramentas inteligentes de refatoração, que podem extrair fechamentos, detectar possíveis problemas e até ajustar automaticamente o código extraído para resolvê-los.

JGWeissman
fonte
3

Claus Reinke: sobre o "Design da linguagem baseado em princípios semânticos" da Tennent
Dá uma interpretação interessante dos princípios:
"A correspondência é o princípio que nos permite dizer que

let(this=obj, x=5) { .. }  

e

((function(x) { .. }).call(obj,5))  

deve ser equivalente, e que qualquer coisa que possamos fazer em listas formais de parâmetros, também devemos poder fazer declarações e vice-versa. "[veja também Reinke, abaixo.]

RD Tennent: Métodos de design de linguagem baseados em princípios semânticos
"Dois métodos de design de linguagem baseados em princípios derivados da abordagem denotacional da semântica da linguagem de programação são descritos e ilustrados por uma aplicação no Pascal. Os princípios são, em primeiro lugar, a correspondência entre parâmetros paramétricos e mecanismos declarativos e, em segundo lugar, um princípio de abstração para linguagens de programação adaptadas da teoria dos conjuntos. Diversas extensões e generalizações úteis do Pascal emergem aplicando esses princípios, incluindo uma solução para o problema do parâmetro da matriz e um recurso de modularização ".

Claus Reinke: "Sobre programação funcional, design de linguagem e persistência" em Haskell

Kris
fonte
Claus Reinke: "Sobre programação funcional, design de linguagem e persistência" em Haskell em community.haskell.org/~claus/publications/fpldp.html
Kris
2

Para responder à pergunta por que o CP da Tennent é tão importante para o design de idiomas, gostaria de citar Neal Gafter :

Os princípios da Tennent são muito poderosos porque as violações tendem a aparecer no idioma como falhas, irregularidades, restrições desnecessárias, interações ou complicações inesperadas e assim por diante.

Qualquer violação do TCP provavelmente prejudicará algum programador no futuro, quando ele espera que os fechamentos funcionem como código de não fechamento, mas descobre que, violando o TCP, eles não o fazem.

thiton
fonte
1

RE Python não segue este princípio. Geralmente, segue o princípio. Exemplo básico:

>>> x = ['foo']
>>> x
['foo']
>>> x = (lambda: ['foo'])()
>>> x
['foo']

No entanto, o Python define expressões e instruções separadamente. Como iframificações, whileloops, designações destrutivas e outras declarações não podem ser usadas em lambdaexpressões, a letra do princípio de Tennent não se aplica a elas. Mesmo assim, restringir-se a usar apenas expressões Python ainda produz um sistema completo de Turing. Portanto, não vejo isso como uma violação do princípio; ou melhor, se violar o princípio, nenhuma linguagem que defina afirmações e expressões separadamente poderá estar em conformidade com o princípio.

Além disso, se o corpo da lambdaexpressão estivesse capturando um rastreamento de pilha ou realizando outra introspecção na VM, isso poderia causar diferenças. Mas, na minha opinião, isso não deve ser considerado uma violação. Se expre (lambda: expr)() necessariamente compilar no mesmo bytecode, o princípio realmente diz respeito aos compiladores, não à semântica; mas se eles puderem compilar em diferentes códigos de bytes, não devemos esperar que o estado da VM seja idêntico em cada caso.

Uma surpresa pode ser encontrada usando a sintaxe de compreensão, embora eu acredite que isso também não seja uma violação do princípio de Tennent. Exemplo:

>>> [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]

A surpresa é o resultado de como as compreensões da lista são definidas. A compreensão "surpresa" acima é equivalente a este código:

>>> 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]

Visto dessa maneira, a compreensão da "surpresa" acima é menos surpreendente, e não uma violação do princípio de Tennent.

amora
fonte