拥有一些有什么意义
类型是不可变的,而其他类型不是?
Without some可变类型,你必须全力以赴地采用纯函数式编程——这是一种与目前最流行的面向对象编程和过程式方法完全不同的范式,虽然非常强大,但显然对很多程序员来说非常具有挑战性(什么当你发生时do在一种没有任何东西可变的语言中需要副作用,在现实世界的编程中,当然你不可避免地会这样做,这是挑战的一部分——HaskellMonads http://en.wikipedia.org/wiki/Monad_(functional_programming)例如,这是一种非常优雅的方法,但是您知道有多少程序员完全且自信地理解它们并可以使用它们以及典型的 OOP 结构?-)。
如果您不理解拥有多种可用范式(既是 FP 又是and那些关键依赖于可变数据),我建议学习 Haridi 和 Van Roy 的杰作,计算机编程的概念、技术和模型 http://www.info.ucl.ac.be/~pvr/book.html -- "a SICP http://mitpress.mit.edu/sicp/面向 21 世纪”,正如我曾经描述的那样;-)。
大多数程序员,无论是否熟悉 Haridi 和 Van Roy,都会欣然承认至少有some可变数据类型对他们来说很重要。尽管我在上面引用了你的问题中的一句话,它采取了完全不同的观点,但我相信这也可能是你困惑的根源:not“为什么其中一些”,而是“为什么有些”不可变的根本”。
“完全可变”的方法曾经(偶然)在 Fortran 实现中获得。如果你有,说,
SUBROUTINE ZAP(I)
I = 0
RETURN
然后是一个程序片段,例如,
PRINT 23
ZAP(23)
PRINT 23
会打印 23,然后是 0——23号已发生变异,因此程序其余部分中对 23 的所有引用实际上都引用 0。从技术上讲,这不是编译器中的错误:Fortran 对于程序在传递常量和传递常量时允许做什么和不允许做什么有微妙的规则变量分配给其参数的过程,并且此代码片段违反了那些鲜为人知的、编译器不可强制执行的规则,因此它是程序中的“但”,而不是编译器中的。当然,在实践中,这种方式导致的错误数量高得令人无法接受,因此典型的编译器很快就会在这种情况下转向破坏性较小的行为(如果操作系统支持,则将常量放入只读段中以获得运行时错误;或者,传递新鲜的copy尽管有开销,但还是常数而不是常数本身;等等)即使从技术上来说它们是程序错误,允许编译器非常“正确”地显示未定义的行为;-)。
在其他一些语言中强制执行的替代方案是增加多种参数传递方式的复杂性——最明显的是在 C++ 中,通过值、通过引用、通过常量引用、通过指针、通过常量指针,...当然,你会看到程序员对诸如此类的声明感到困惑const foo* const bar
(其中最右边的const
基本上是无关的,如果bar
是某个函数的参数...但至关重要的是,如果bar
is a 局部变数...!-).
实际上 Algol-68 可能在这个方向上走得更远(如果你可以有一个值和一个引用,为什么不是引用到引用?或者引用到引用到引用?&c - Algol 68 对此没有任何限制,并且规则定义正在发生的事情可能是在“真正使用的”编程语言中发现的最微妙、最困难的组合)。早期的 C(只有按值和按显式指针——没有const
,没有参考文献,没有复杂性)无疑部分是对它的反应,就像最初的 Pascal 一样。但const
很快,并发症又开始增加。
Java 和 Python(以及其他语言)用强大的简单大刀穿过了这片丛林:所有参数传递,and所有赋值都是“通过对象引用”(从不引用变量或其他引用,从不语义上隐式复制,&c)。将(至少)数字定义为语义上不可变可以避免“哎呀”(例如上面的 Fortran 代码所展示的情况),从而保持程序员的理智(以及语言简单性的这一宝贵方面)。
像数字一样将字符串视为基元与语言预期的高语义级别非常一致,因为在现实生活中我们do需要像数字一样易于使用的字符串;诸如将字符串定义为字符列表(Haskell)或字符数组(C)之类的替代方案对编译器(在此类语义下保持高效性能)和程序员(有效地忽略这种任意结构以使字符串的使用变得简单)都提出了挑战原语,正如现实生活中的编程经常需要的那样)。
Python 更进一步,添加了一个简单的不可变容器(tuple
)并捆绑hashing到“有效的不变性”(这避免了程序员的某些意外,例如在 Perl 中,其散列允许可变字符串作为键)——为什么不呢?一旦你有了不变性(一个宝贵的概念,它可以让程序员不必学习 N 种不同的赋值和参数传递语义,其中 N 会随着时间的推移而增加;-),你可能会从中获得全部收益;-) 。