Scala有多纯净和懒惰?

22

这只是一个“我在想…”的问题。

Scala有不可变的数据结构和(可选的)lazy vals等特性。

一个Scala程序可以有多接近于完全纯净(从函数式编程的角度来看)和完全lazy(或者如Ingo指出的那样,它可以足够的非严格吗)?哪些值是不可避免的可变的,哪些评估是不可避免的贪婪的?


1
好问题。但请注意,完全的惰性并不理想,应该在编译器中进行严格性分析,以便生成的程序仅为“非严格”,而不是完全惰性。这就是 Haskell(据我所知)和 Frege 中的做法。 - Ingo
好的,已经更新了问题。 - The Archetypal Paul
+1,我很好奇如何避免这种情况 Counter{var count;def incr() = count += 1 } - jilen
2
@jilen case Counter(i: Int = 0) { def incr = Counter(i+1) } @jilen case Counter(i: Int = 0) { def incr = Counter(i+1) } - paradigmatic
1
据我所知,Scala没有lazy var,只有lazy val - Jus12
3个回答

15

关于懒惰性 - 目前,默认情况下将参数传递给方法时是严格的:

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,而不考虑按名称语义。如果语言允许有副作用,则此类优化并不总是适用。


“除非你做X,否则无法避免严格性”?这没有任何意义。此外,按名称传递的参数是非严格的——惰性不是非严格性的先决条件。 - Daniel C. Sobral
措辞已经修正。非严格性是懒惰的前提条件。懒惰是一种非严格性,它允许使用更好的复杂度界限/性能进行函数式编程 - 这就是为什么它在按名称参数方面具有优势的原因。虽然它可以相对容易地模拟。 - axel22

14

Scala可以像您想要的那样纯和懒惰,但是a)编译器不会强制执行纯度,b)使其懒惰需要一些额外的工作。这并不奇怪; 如果您真的想要(请参见此处;在Java中实现惰性需要令人眼花缭乱的嵌套匿名内部类),您甚至可以编写懒惰和纯净的Java代码。

纯度

虽然Haskell通过类型系统跟踪杂质,但Scala选择不走这条路,而且当您从一开始就没有将其作为目标时(以及与像Java这样彻底不纯的语言进行互操作性是语言的主要目标时),很难添加这种东西。

尽管如此,有人相信在Scala的类型系统中记录效果是可能且值得努力的。但是我认为,Scala中的纯度最好视为自律问题,并且必须对第三方代码的所谓纯度持续怀疑。

惰性

Haskell默认情况下是惰性的,但可以通过在代码中添加一些注释使其更加严格... Scala则相反:默认情况下为严格模式,但使用lazy关键字和按名称参数,您可以使其尽可能懒惰。


3

随意保持不可变性。但是,没有副作用跟踪,因此无法强制执行或验证。

至于非严格性,这就是问题所在...首先,如果您选择完全非严格,则将放弃所有Scala的类。即使是大部分Scalaz也不是非严格的。如果您愿意自己构建所有内容,则可以使方法非严格化,使值懒惰化。

接下来,我想知道隐式参数是否可以是非严格的,或者使它们非严格会有什么后果。我没有看到问题,但我可能是错的。

但是,最棘手的是,函数参数是严格的,闭包参数也是严格的。

因此,尽管理论上可以完全非严格,但这将非常不方便。


1
我非常确定,如果一切都是不可变的,非严格的隐式转换就不会引起任何问题。在没有副作用的情况下,严格和非严格的求值应该保证给出相同的答案(除了某些非严格求值策略将终止某些严格求值无法终止的程序)。而且,隐式转换总是可以被展开为一个等效的完全显式的程序。 - Ben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接