【发布时间】:2015-07-06 13:03:51
【问题描述】:
我在从 OOP 思维转换到函数式思维时遇到了一些麻烦。我当前的问题是我有一个不可变的、持久的数据结构,它用于(比如说)构建 URL-s:
class UrlBuilder {
public UrlBuilder withHost(String domain) {
return new UrlBuilder(/*...*/);
}
public UrlBuilder withPort(Int port) {
return new UrlBuilder(/*...*/);
}
// ...
public String build() {
// ...
}
}
懒惰地评估字符串的build()方法非常昂贵,所以我想缓存结果。
在 OOP 中这没问题,因为我可以这样做:
class UrlBuilder {
private String url;
// ...
public String build() {
if (null == this.url) {
this.url = doExpensiveEvaluation();
}
return this.url;
}
}
如果我需要线程安全,我会使用双重检查锁定并完成它。但据我了解,这违反了功能范式,因为它引入了副作用(修改对象的内部状态)。
我知道在 Scala 中有 lazy 关键字,它完全符合我的需要:实现所谓的 by-need 惰性。但是我怎样才能在 OOP 语言中做同样的事情呢?我其实很好奇他们是如何在 Scala 中实现这一点的。
我试图将缓存结果的责任倒转给我的UrlBuilder 的消费者,但这在消费者端引起了同样的问题:
class Consumer {
private UrlBuilder urlBuilder;
private String url;
// ...
public String getUrl() {
if (null == this.url) {
this.url = urlBuilder.build(); // same as before!
}
return this.url;
}
}
因此我在标题中提出了问题。
编辑:要明确一点:我问的是除 Scala 之外的 OOP 语言的实现。它可以是 Java 或 C#,但我也想知道如何在 JavaScript 之类的东西中做到这一点。正如我所提到的,我可以只使用锁定,但我正在寻找一种无需使用锁定的纯功能解决方案。
我的印象是函数式编程是开箱即用的线程安全的,因此锁定对我来说就像一个丑陋的 OOP 解决方案。但当然,我也会接受一个证明这是不可能的答案。 Ben Reich 的The comment bellow 几乎说明了一切:如果 Scala 开发人员在没有锁定的情况下无法做到这一点,那么我可能会死去尝试。
【问题讨论】:
-
修改无法观察到的内部状态在函数式编程中并没有错。
-
我很困惑为什么
lazy在这里不适合你。你能解释为什么它还不够吗?您是否在问如何用另一种语言(如 Java)实现lazy?我还要小心你的一些语言:面向对象和函数式编程并不矛盾——Scala 支持这两种范式! -
使用
lazy时可以查看反编译的代码,更好的理解实现。在此处阅读更多信息:stackoverflow.com/a/17642466/1223622 -
@Bergi 你可能就在这里。如果只有一种方法可以通过开箱即用的线程安全来做到这一点。您能否指出一些参考资料来支持您的主张?
-
@MaciejSz:嗯,也许“如果它没有改变任何可观察到的东西,那么它就不是 side effect 的定义”?
标签: scala functional-programming lazy-evaluation