2010-10-05 50 views
5

我正在阅读一篇Haskell教程(学习你一个Haskell),作者说,懒惰与引用透明度很好。更多的阅读和一些搜索后,我仍然不明白为什么。请注意,我明白参照透明度和懒惰有什么好处,但它们在一起令我感到困扰。为什么懒惰与参照透明度一致?

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

或者,也许作者只是想说,他们都很好,并表示含糊不清?

回答

11

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

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

现在一个懒惰的语言将推迟第一线的evalutation,因为开始只需要在第三行。所以结果将是0(或者非常接近它)。可能这不是你想要的。造成这种麻烦的原因是System.currentTimeMillis是而不是参照透明。如果它是像sin或ln这样的“数学意义上的”函数,那么在这种情况下就不会有任何问题,是参考透明的。

+1

”状态只在第三行需要“。这不完全正确。运行第二行需要系统询问当前时间的副作用;它是'IO a'类型,而不是'a'。 runBenchmarkFunction类型相似,为了同时运行,你必须将它们和一些函数结合起来,确保按照Haskell中的'>>'顺序执行操作。所以你真的写了'currentTime >> = \ st - > runBenchmarkFunction >> currentTime >> = \ end - > putStrLn“它花费了++(show $ end - st)++”seconds“'。很明显,第二行取决于第一行,所以这一切都有效。 – jrockway 2010-10-06 21:46:26

+0

@jrockway:我在这里使用了一种虚构的语言,你可以称之为“懒惰的Java”,作为一个非常简单而简单的例子来解释懒惰和引用透明性之间的关系。 Haskell如何解决这个问题是一个完全不同的**问题。请注意,Haskell的“monad方式”不是唯一的可能性,例如Clean使用它的类型系统来处理这个问题。 – Landei 2010-10-07 07:10:44

+1

这个问题被标记为“haskell”。但是,如果你想象正确的语义,那么你写的是完全透明的。而且,如果你想象其他的语义,那么它不是。但关键词是“想象”。 – jrockway 2010-10-07 22:23:59

5

引用透明意味着该函数将始终在给定相同输入的情况下返回相同的输出。所以如果函数是懒惰的或者严格的话就没有关系。懒惰函数将在未来某个未知时间计算其输出,但由于参照透明性,您可以保证对于给定的输入,输出总是相同的。

所以在某种意义上,参照透明度保证了懒惰函数的正确性。

4

考虑这个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 

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

+0

非常好的例子。当然,也可能会破坏Haskell的参照透明度,尽管通常会有警告标志。 – 2010-10-05 16:36:46

+0

@John:你的意思是使用原始的东西或'unsafePerformIO'?如果使用不当,这些被认为是有害的。无法想象另一种方式。 – fuz 2010-10-06 05:49:00

+0

@FUZxxl,这正是我的意思,而'unsafe'前缀是警告。另一种方法是将指针包装成ByteString,然后修改指针。 – 2010-10-06 07:34:42