这只是一个“我在想…”的问题。
Scala有不可变的数据结构和(可选的)lazy vals等特性。
一个Scala程序可以有多接近于完全纯净(从函数式编程的角度来看)和完全lazy(或者如Ingo指出的那样,它可以足够的非严格吗)?哪些值是不可避免的可变的,哪些评估是不可避免的贪婪的?
这只是一个“我在想…”的问题。
Scala有不可变的数据结构和(可选的)lazy vals等特性。
一个Scala程序可以有多接近于完全纯净(从函数式编程的角度来看)和完全lazy(或者如Ingo指出的那样,它可以足够的非严格吗)?哪些值是不可避免的可变的,哪些评估是不可避免的贪婪的?
关于懒惰性 - 目前,默认情况下将参数传递给方法时是严格的:
def square(a: Int) = a * a
但是你使用了按名称调用的参数:
def square(a: =>Int) = a * a
但这里的lazy并不是在需要时计算值只进行一次:
scala> square({println("calculating");5})
calculating
calculating
res0: Int = 25
已经有一些工作在添加惰性方法参数方面进行了,但它还没有整合进来(下面的声明应该仅从上面打印"calculating"
一次):
def square(lazy a: Int) = a * a
这里缺少一部分内容,不过你可以通过使用本地的lazy val进行模拟:
def square(ap: =>Int) = {
lazy val a = ap
a * a
}
关于可变性 - 没有什么限制你写不可变的数据结构来避免变异,你可以在Java或C中实现这个功能。事实上,一些不可变的数据结构 依赖于惰性原语以达到更好的复杂度边界,但是在其它语言中也可以模拟惰性原语 -- 只需要增加额外的语法和样板代码。
你总可以在Scala中编写不可变的数据结构,惰性计算和完全纯粹的程序。问题在于Scala编程模型允许编写非纯程序,因此类型检查器不能始终推断出程序的某些属性(例如纯度),而如果编程模型更为严格,则可以进行推断。
例如,在一个具有纯表达式的语言中,上面的按名称调用定义中的 a * a
可以优化为仅计算一次 a
,而不考虑按名称语义。如果语言允许有副作用,则此类优化并不总是适用。
Scala可以像您想要的那样纯和懒惰,但是a)编译器不会强制执行纯度,b)使其懒惰需要一些额外的工作。这并不奇怪; 如果您真的想要(请参见此处;在Java中实现惰性需要令人眼花缭乱的嵌套匿名内部类),您甚至可以编写懒惰和纯净的Java代码。
虽然Haskell通过类型系统跟踪杂质,但Scala选择不走这条路,而且当您从一开始就没有将其作为目标时(以及与像Java这样彻底不纯的语言进行互操作性是语言的主要目标时),很难添加这种东西。
尽管如此,有人相信在Scala的类型系统中记录效果是可能且值得努力的。但是我认为,Scala中的纯度最好视为自律问题,并且必须对第三方代码的所谓纯度持续怀疑。
Haskell默认情况下是惰性的,但可以通过在代码中添加一些注释使其更加严格... Scala则相反:默认情况下为严格模式,但使用lazy
关键字和按名称参数,您可以使其尽可能懒惰。
随意保持不可变性。但是,没有副作用跟踪,因此无法强制执行或验证。
至于非严格性,这就是问题所在...首先,如果您选择完全非严格,则将放弃所有Scala的类。即使是大部分Scalaz也不是非严格的。如果您愿意自己构建所有内容,则可以使方法非严格化,使值懒惰化。
接下来,我想知道隐式参数是否可以是非严格的,或者使它们非严格会有什么后果。我没有看到问题,但我可能是错的。
但是,最棘手的是,函数参数是严格的,闭包参数也是严格的。
因此,尽管理论上可以完全非严格,但这将非常不方便。
case Counter(i: Int = 0) { def incr = Counter(i+1) }
@jilencase Counter(i: Int = 0) { def incr = Counter(i+1) }
- paradigmaticlazy var
,只有lazy val
。 - Jus12