Answers:
我以前从未听说过“田纳特书信原理”,甚至在语言设计中也很少听说过。仔细搜索这些表达式似乎可以导致2006年一个Neal Gafter博客发布该博客,阐述了他的想法以及他认为它也应如何应用于闭包。而且论坛中的大多数其他人似乎都参考了Gafter的文章。
但是,这里提到了道格拉斯·克罗克福德(Douglas Crockford)所说的“ TCP”(我知道并信任这个名字):http : //java.sys-con.com/node/793338/。部分地
有些事情不能用这种方式包含起来,例如return语句和break语句,Tennent的通信原理(TCP)的倡导者声称这是难闻的症状。ow!语言设计已经足够困难,而不必应对嗅觉幻觉。因此,为了更好地理解问题,我购买了Tennent 1981年出版的《编程语言原理》一书。
事实证明,对应原理是描述性的,而不是说明性的。他使用它来分析(现在已被遗忘的)Pascal编程语言,显示变量定义和过程参数之间的对应关系。Tennent并未将返回语句的缺乏对应性视为问题。
因此,似乎似乎没有正确使用“ Tennent的通信原理”这个名称,而Neal谈论的任何内容都应该被称为“ Gafter的想象中和可能的通用TCP”……等等。无论如何,都不足以隐藏在绝版书名的幕后
我认为这是一条通用规则的一部分,即设计良好的语言可以实现程序员自然所期望的一切。如果我有一个要重构为闭包的代码块,并且我用适当的语法包装了该块,而没有真正考虑每行代码,那么我希望该块在闭包中能做同样的事情内联。如果某些语句使用关键字“ this”(可能是隐式的)并且语言使闭包内部使用的“ this”是指用于表示该闭包的匿名类,而不是定义用于定义闭包的方法的类,则表示这些语句已更改,我的代码块不再执行我认为的操作,并且我必须找出一个错误并弄清楚如何更改我的代码才能在闭包中工作。
还可以使用具有智能重构工具的IDE缓解该问题,该工具可以提取闭包,检测潜在问题,甚至自动调整提取的代码来解决问题。
克劳斯·赖因克(Claus Reinke):关于Tennent的“基于语义原则的语言设计”
,对该原则进行了有趣的解释:
“函授是让我们说的原则
let(this=obj, x=5) { .. }
和
((function(x) { .. }).call(obj,5))
应该等效,并且在形式参数列表中我们可以做的任何事情,在声明中也应该可以做,反之亦然。” [另请参见下面的Reinke。]
RD Tennent:基于语义原理的
语言设计方法“通过基于Pascal语言的应用,描述和说明了两种基于从编程语言语义编程方法衍生而来的原理的语言设计方法。首先,这些原理是参数化与声明性机制,其次是从集合论出发的编程语言抽象原理。通过应用这些原理,对Pascal进行了一些有用的扩展和概括,包括对数组参数问题的解决方案和模块化工具。”
Claus Reinke:关于Haskell的“关于函数式编程,语言设计和持久性”
为了回答为什么Tennent的CP对于语言设计如此重要的问题,我想引用Neal Gafter的话:
Tennent的原理非常强大,因为违反它们的倾向会在语言中显示为缺陷,不合规定,不必要的限制,意外的交互作用或复杂性等。
将来,任何违反TCP的行为都可能会伤害某些程序员,因为他希望闭包像非闭包代码一样工作,但发现闭包不会违反TCP。
RE Python不遵循此原则。通常,它确实遵循该原理。基本示例:
>>> x = ['foo']
>>> x
['foo']
>>> x = (lambda: ['foo'])()
>>> x
['foo']
但是,Python分别定义表达式和语句。由于if
分支,while
循环,破坏性赋值和其他语句根本不能在lambda
表达式中使用,因此Tennent原理的字母不适用于它们。即使这样,将自己限制为仅使用Python表达式仍然会产生一个图灵完整的系统。因此,我不认为这违反了该原则。或者更确切地说,如果它确实违反了原则,那么没有任何单独定义语句和表达式的语言可能无法符合原则。
另外,如果lambda
表达式的主体正在捕获堆栈跟踪或在VM中进行其他自省,则可能导致差异。但是在我看来,这不应算作违反。如果expr
并且(lambda: expr)()
必须编译为相同的字节码,则该原理确实涉及到编译器,而不是语义。但是,如果它们可以编译为不同的字节码,则我们不应该期望VM的状态在每种情况下都相同。
尽管我相信这也不违反Tennent原则,但使用理解语法可能会遇到意外。例:
>>> [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]
这样看来,上面的“惊奇”理解就不足为奇了,并且没有违反Tennent原则。