【问题标题】:Why does laziness go well with referential transparency?为什么惰性与引用透明性相得益彰?
【发布时间】:2011-04-21 08:18:59
【问题描述】:

我正在阅读 Haskell 教程(Learn You a Haskell),其中作者说惰性与引用透明性相得益彰。经过更多的阅读和搜索,我仍然不明白为什么。请注意,我确实了解引用透明性和惰性的优点,但困扰我的是它们。

两者结合有什么特别的好处吗?

或者也许作者只是想说他们都很好,但表达得模棱两可?

【问题讨论】:

  • 我会选择不同的措辞:为了防止令人讨厌的意外,最好惰性表达式是引用透明的。与其说是美好,不如说是必需品。

标签: haskell lazy-evaluation referential-transparency


【解决方案1】:

这真的很容易。非严格(例如惰性)评估意味着可以推迟任务。但是为了推迟某些事情,你最好确保以后得到与现在相同的结果,这就是参照透明性。考虑这个命令式 Java 代码:

long start = System.currentTimeMillis(); //get the start time
runBenchmarkFunction();
System.out.println("Run took " + (System.currentTimeMillis() - start) + " ms"); 

现在懒惰的语言会推迟第一行的评估,因为 start 只需要在第三行。所以结果将是 0(或非常接近它)。可能这不是你想要的。造成这种麻烦的原因是 System.currentTimeMillis 不是引用透明的。如果它是像 sin 或 ln 这样的“数学意义上”的函数,在这种情况下你不会有任何问题,它们引用透明的。

【讨论】:

  • "状态只需要在第三行"。严格来说,这不是真的。运行第二行需要向系统询问当前时间的副作用;它是IO a 类型,而不是a。 runBenchmarkFunction 是类似的类型,为了同时运行它们,你必须将它们与一些确保操作按顺序执行的函数结合起来,>> in Haskell。所以你真的在写currentTime >>= \st -> runBenchmarkFunction >> currentTime >>= \end -> putStrLn "it took " ++ (show $ end - st) ++ " seconds"。显然第二行取决于第一行,所以一切正常。
  • @jrockway:我在这里使用一种虚构的语言,你可以称之为“惰性 Java”作为一个非常粗略且简单的示例来解释惰性和引用透明性之间的联系。在 Haskell 中如何解决问题是一个完全不同的问题。请注意,Haskell 的“monad 方式”并不是唯一的可能性,例如Clean 使用它的类型系统来处理这个问题。
  • 问题被标记为“haskell”。但是,如果您想象正确的语义,您所写的内容仍然是完全透明的。而且,如果您想象其他语义,那就不是了。但这里的关键词是“想象”。
【解决方案2】:

考虑一下这个 Python 代码,其中一个生成器用于延迟计算无限序列。由于它使用全局状态,它没有引用透明性,因此生成器的调用者不能确定他们得到的结果没有受到其他事件的影响。

foo = 0

def foo_sequence():
    global foo
    while True:
        foo += 1
        yield foo

>>> generator = foo_sequence()
>>> generator.next()
1
>>> generator.next()
2
>>> foo = 5
>>> generator.next()
6

在这种情况下,调用者更愿意以原子方式生成整个序列,以防止此类事件发生。因此,缺乏引用透明度(在这个人为的例子中)使得惰性没有吸引力。

【讨论】:

  • 非常好的例子。当然,破坏 Haskell 中的引用透明度也是可能的,尽管在此过程中通常会出现警告标志。
  • @John:你的意思是使用原始的东西还是unsafePerformIO?如果使用不当,这些被认为是有害的。无法想象其他方式。
  • @FUZxxl,这正是我的意思,unsafe 前缀将是警告。另一种方法是包装一个指向 ByteString 的指针,然后修改指针。
  • @John:请注意 unsafePerformIO(以及名称中带有 unsafe 的所有其他内容)不是 Haskell 标准的一部分,因此您实际上不能破坏“纯”Haskell 中的引用透明度(实际上并不相关)。
  • @sepp2k,虽然这可能是真的,但在 Haskell98 中仍然有可能做坏事。见mail-archive.com/haskell@haskell.org/msg21785.html。据我了解,尽管您不违反 RT,但您确实违反了因果关系。两者都不令人满意。
【解决方案3】:

参照透明意味着在给定相同输入的情况下,函数将始终返回相同的输出。因此,如果函数是惰性的还是严格的,则无关紧要。惰性函数将在​​未来某个未知时间计算其输出,但由于引用透明性,您可以保证对于给定输入,输出始终相同。

所以在某种程度上,引用透明性确保了惰性函数的正确性。

【讨论】:

    猜你喜欢
    • 2013-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-16
    • 2017-03-31
    • 2010-09-20
    • 2014-09-02
    相关资源
    最近更新 更多