类型系统经常受到批评,因为它具有限制性,限制了编程语言并禁止程序员编写有趣的程序。
Chris Smith claims:
我们确保程序是正确的(在此类型检查程序检查的属性中),但我们必须拒绝一些有趣的程序。
和
此外,还有一个铁定的数学证据表明任何利益的类型检查器总是保守的。构建一个不拒绝任何正确程序的类型检查器并不困难;不可能。
有人可以概述这可能是什么样的有趣程序吗?哪里证明类型检查器必须保守?
更一般:类型检查和类型系统的限制是什么?
答案 0 :(得分:6)
我认为第一个陈述在技术上是错误的,尽管在实践中是正确的。
静态类型与证明程序属性基本相同,并且通过使用足够强大的逻辑,您可以证明所有有趣的程序都是正确的。
问题在于强大的逻辑类型推断不再有效,并且您必须提供证明作为程序的一部分,以便类型检查器可以完成其工作。一个具体的例子是更高阶的证明,例如Coq。 Coq使用了一个非常强大的逻辑,但是在Coq中完成任何事情也非常繁琐,因为你必须提供非常详细的证据。
那些给你带来最多麻烦的有趣程序将是口译员,因为他们的行为完全取决于输入。基本上,您需要反思性地证明该输入的类型检查的正确性。
关于第二个陈述,他可能指的是Gödels不完备性定理。它指出,对于任何给定的证明系统,都存在无法在证明系统中证明的算术的真实陈述(自然数的加法和乘法的逻辑)。转换为静态类型系统你会有一个程序没有做任何坏事,但静态类型系统无法证明这一点。
这些反例是通过回顾证明系统的定义来构建的,基本上说“我不能被证明”被翻译成算术,这不是很有趣。恕我直言,类似地构建的程序也不会有趣。
答案 1 :(得分:5)
您可以用静态和动态语言表达所有内容。证明==您可以用任何图灵完整语言编写任何语言编译器。因此无论语言是什么,您都可以创建执行X的静态语言。
动态打字有什么好处?...有了足够好的鸭子打字,您可以通过网络与对象进行交互,而无需了解其类型并将结果(对您来说类型未知)作为参数传递给本地函数实际上可能会做一些有用的事情。
该问题的静态答案是将所有内容包装在“可导出的界面”中,提供.call()和.provides?()处理文本名称,但这肯定会更难。
这是我所知道的最“限制”的情况,它确实拉伸了一些东西(仅对模拟对象非常有用?)。至于理论上的限制,没有 - 你只需要一些额外的代码来克服实际问题。
答案 2 :(得分:5)
一个有趣的例子是This Paper,这可能是静态与动态类型上唯一的苹果对苹果比较。他们使用类型推断(静态)和类型反馈(动态)来实现 self (像smalltalk这样的语言,但使用原型而不是类)。
最有趣的结果是类型推理引擎在机器整数和任意精度整数之间解决时遇到很多麻烦 - 它们在vanilla self中自动升级,因此不能被类型系统分开,这意味着编译器必须包含在每个整数操作中提升到BigInt的代码。
他们遇到了类型系统的限制:它无法检查整数的实际值。
我认为一般来说系统类型没有理论限制,但任何给定的类型检查器只能处理特定的有限类型系统,并且会有程序无法确定发生了什么。由于自我类型推断器允许类型集合,因此它只编译了两个路径。需要在单一类型上收敛的类型检查器必须拒绝该程序。 (虽然它可能有这种情况的特殊代码。)
答案 3 :(得分:4)
我认为存在误解。确实,任何类型系统都会拒绝正确的程序(我不记得结果的确切名称,因此我现在无法查找,抱歉)。同样,任何图灵完整语言都可以与任何其他语言完全相同,因此动态类型语言中的某些程序无法在Haskell中重现,这是错误的。
问题在于类型系统拒绝程序这一事实并不意味着它将拒绝所有与之相当的程序。因此,某些程序将被拒绝,但您可以将其替换为其他等效的程序。作为示例,请使用以下Scala程序
def foo: Int =
if (1 > 0) 15 else "never happens"
类型检查器会拒绝它,因为表达式if (1 > 0) 15 else "never happens"
正式为Any
类型。当你运行它时,它肯定会返回一个整数,但是如果没有评估1 > 0
,你就不能确定它不会返回一个字符串。你可以用Python编写
def foo():
if 1 > 0:
return 15
else:
return "never happens"
并且Python编译器不关心。
当然,有些程序可以在Scala中编写,最简单的是
def foo: Int = 15
答案 4 :(得分:3)
很难找到静态与动态类型问题的客观实用比较,因为它常常是一场宗教战争。你引用的那些小小的摘要blubs往往是相同的样板免责声明,这些似乎对所有人来说都是“可接受的”。
由于某人的体验主要是静态类型的语言,我试图了解博客系列中的一些权衡。很多警告,但你可以查看this blog entry的后半部分进行一些比较,这些比较可以作为你问题的答案。
这是一个引用权衡的窗口:
从某种意义上说,这个微小的功能 捕捉正在进行的本质 静态和动态之间的争论 打字。程序员可以创建 特定于域的静态类型 构建程序,传达意图, 并排除一类错误和 编译时的行为,在 必须创作的价格 结构和中介结构 模块边界不匹配。要么 程序员可以选择计算 大多数东西只有标量和 列表,在这种情况下数据很容易流动 到处都是,导致代码短, 但随着编译时间的减少 检查和传达意图。
并且运行示例显示了静态类型程序本质上不允许有用/有趣行为的情况。
答案 5 :(得分:1)
我认为an eval function有时会很方便,但从来没有必要(对于静态类型的语言来说,这种情况并不常见,请参阅链接上的解释)。
答案 6 :(得分:0)
人们发现,如果您首先以真正的TDD方式编写测试,那么您的测试通常在验证正确性方面比严格的类型检查系统更好。因此,为了衡量正确性,强类型系统并没有真正起作用。
强类型通常可以为您带来一些速度,因为编译器可以轻松使用本机类型,而不必在运行时进行类型检查。举个例子:如果你正在进行大量的整数运算,你会发现一个强类型的实现可能会超过一个弱类型的实现,因为你的数字通常只能由CPU立即使用,而不必是在运行时验证。
“有趣”的节目?当然。从动态语言编写扩展更容易。此外,在分布式系统中,拥有弱类型的UI并且不必生成特定的数据传输对象可能非常方便。例如,如果你有一个JavaScript前端和一个C#后端,你可以在C#端使用LINQ来生成匿名类,并通过JSON将它们发送到你的Javascript。从JS层,您可以使用这些匿名类型,就好像它们是一流的对象,而不必经历明确编码所有数据协定的痛苦。
答案 7 :(得分:0)
这是一个简单的例子(在Python中,但与其输入问题完全无关):
# The company deals with rectangles. They have horizontal and vertical lengths
# that should not be mixed. Programmer A writes:
class Rectange:
def __init__(self, width, height):
enforce_type('horizontal', width)
enforce_type('vertical', height)
#
# A: Hehe! I'm so smart! With my patented type system if I try to
# write "width * width" I'll have a loud error so I'll know I'm wrong.
#
area = width * height
enforce_type('square_meters', area)
...
# Everyone's happy. The company shows off A's type system on the conference.
...
# Much later, the clients request ability to specify square by entering only
# one length. Programmer B writes:
class Square(Rectangle):
def __init__(self, width):
Rectange.__init__(self, width, width)
# !!
# Error happens! 'horizontal' is not 'vertical'!
#
# B: Dear Management, I would like to request a weeklong leave since I
# have to track Programmer A's house and either talk him into changing
# his patented type system or BEAT HIM WITH MY LAPTOP until he agrees.
#
很难创建一个可以预见规则中任何类型的可能异常的类型系统,特别是如果你正在创建将在很晚以后被人们使用的基础框架。
要回答你的直接问题,你在哪一方:程序员A或程序员B?
答案 8 :(得分:0)
我不确定,但我相信你所指的问题是代数类型系统,如Haskell和ML。在您运行程序之前,这些语言会尝试对类型进行非常完整的“静态”分析。
代数类型系统中非常严格的静态分析的一些有趣的烦恼是,容器中包含不同类型的对象非常困难。
例如,在大多数主流语言中,您可以在列表中使用异类混合类型。 python中的一个例子是:
["a",1,"b",2]
要在严格类型系统中执行此操作,您必须创建一个包装统一类型,然后模式匹配所有可能的类型。
但是语言中缺少的真正有趣的东西是你在大多数现代语言中所拥有的强大内省(例如C#,Java,Python,Ruby,Lisp)。
因此,这些语言允许您进行一些功能强大的元编程和数据绑定,而完全静态分析无法做到这一点。
答案 9 :(得分:0)
有很多复杂的例子,但似乎大多数人错过了这个简单的例子。
这是一个正确的python程序,它回答了5和-5的绝对值是什么的问题。
def abs(x):
def signum(f):
if f > 0:
return 1
else:
return "non-positive"
if signum(x) == 1:
return x
else:
return -x
print("abs(5) = %r and abs(-5) = %r" % (abs(5), abs(-5)))
显然abs和signum以int为参数; abs总是返回int,但是signum可以返回int或string。现在,如果我们引入了一个类型检查器(但不是任何类型检查器; scala的类型检查器只会说“signum是int->Any
”!)这个程序将被拒绝......但是,它是正确的,永远不会崩溃以非强类型一致性作为崩溃原因。
答案 10 :(得分:0)
Spec#有一个非null类型的类型系统,它是静态安全的。 (http://research.microsoft.com/en-us/projects/specsharp/)
答案 11 :(得分:0)
类型检查和类型系统的限制是什么?
我将假设“类型系统”意味着静态类型的编程语言。
静态类型意味着在编译时检查类型。这里的限制是编译器只能根据编译时可用的信息进行优化。这可以被视为一种限制。在动态类型编程语言中,解释器可以在运行时分析和优化程序。这意味着它可以根据使用模式进行优化。
答案 12 :(得分:0)
如果你看一下Mitchell's book Concepts in Programming Languages第134页,你会发现一些所谓的“编译 - 时间检查的保守性”的细节。问题是一些“有趣”的功能,如数组的越界访问不能静态检查,因为它们需要评估程序/每个可能的程序运行。标准的undecidability-result告诉你必须解决Halting-problem才能确实检查每个数组访问。