haskell – 评估策略

关于功能评估的一个原因如何在Haskell中如下:

let f x = ...
    x = ...
in map (g (f x)) xs

在GHC中,有时候(f x)只评估一次,有时候对于xs中的每个元素一次,这取决于f和g究竟是什么。当f x是昂贵的计算时,这可能很重要。它刚刚跳过了我正在帮助的Haskell初学者,我不知道告诉他什么,而不是编译器。有更好的故事吗?

更新

在下面的例子中(f x)将被评估4次:

let f x = trace "!" $ zip x x
    x = "abc"
in map (\i -> lookup i (f x)) "abcd" 
最佳答案
使用语言扩展,我们可以创建必须重复计算f x的情况:

{-# LANGUAGE GADTs, Rank2Types #-}
module MultiEvG where

data BI where
    B :: (Bounded b, Integral b) => b -> BI

foo :: [BI] -> [Integer]
foo xs = let f :: (Integral c, Bounded c) => c -> c
             f x = maxBound - x
             g :: (forall a. (Integral a, Bounded a) => a) -> BI -> Integer
             g m (B y) = toInteger (m + y)
             x :: (Integral i) => i
             x = 3
         in map (g (f x)) xs

关键是要具有fx多态,甚至作为g的参数,我们必须创建一个情况,其中需要它的类型是无法预测的(我的第一个stab使用一个ab而不是BI,但是当优化,当然最终只能对fx进行两次评估)。

对于其使用的每种类型,必须至少评估多态表达式。这是单态限制的一个原因。然而,当可能需要的类型范围受到限制时,可以记录每种类型的值,在某些情况下,GHC会这样做(需要优化,我预计所涉及的类型数量不能太大)大)。在这里,我们与基本上是一个不均匀的列表进行对比,所以在每次调用g(fx)时,都可以在满足约束的任意类型的情况下进行需求,所以计算不能在map之外被解除(技术上,编译器仍然可以在每个使用的类型中构建值的缓存,因此每个类型将只评估一次,但是GHC并不会非常值得麻烦。

单态表达式只需要一次评估,就可以共享。是否符合实施的要求?通过纯度,它不会改变程序的语义。如果表达式被绑定到一个名字,实际上你可以依靠它被共享,因为它很容易,明显地是程序员想要的。如果它没有绑定到一个名称,这是一个优化的问题。使用字节码生成器或没有优化,表达式将经常被重复评估,但是优化重复的评估将指示编译器错误。
必须对所使用的每种类型至少对多态表达式进行一次评估,但是在进行优化时,当GHC可以看到它可以在同一类型下被多次使用时,通常仍然会为该类型共享多形态表达式一个更大的计算。

底线:始终使用优化进行编译,通过将要共享的表达式绑定到名称来帮助编译器,并在可能的情况下给出单形类型签名。

转载注明原文:haskell – 评估策略 - 代码日志