如何强制where子句中的绑定为CAF?

时间:2016-12-30 19:16:19

标签: haskell

我有一些绑定,我想保持一个函数的私有(所以,不在顶级模块命名空间中声明),但是计算起来很昂贵,所以我更喜欢它们是CAF。是否有一个pragma或其他技巧可以用来强制GHC为这些绑定分配CAF?

示例代码:

header :: HeaderProps -> Element
header = div_ [style_ headerStyle] [str_ "Here be headerz"]
  where
    -- Creating the 'headerStyle' is expensive! Do it only once
    -- regardless of how many times the 'header' function is used.
    headerStyle = mkStyle $ do
        display flex
        flexDirection column
        padding (px 20) (px 40)

如果那是不可能的,我会看到一些选项,但每个选项都有其自身的缺点:

  1. 将每个函数移动到自己的模块中,这样我就可以将昂贵的绑定移动到顶级模块命名空间,而不必担心访问它们的其他函数。由于我希望有许多这样的功能,模块的数量会爆炸(大型项目中有数百个)。我并不是每个模块宗教只有一个函数的粉丝,就像它在node.js社区中传播一样。
  2. 将绑定移动到顶级模块名称空间,并为每个名称提供足够唯一的名称以避免冲突,并清楚地表明这些是私有的(例如headerStyle - > header__headerStyle)。
  3. 使用TemplateHaskell将昂贵的计算卸载到编译时。

1 个答案:

答案 0 :(得分:4)

如果我理解你的意图是正确的,这实际上很简单:只需将任何函数参数移动到lambda中,所以整个函数(包括where块)都是CAF:

foo :: Int -> Int
foo = \x -> x * nFermat
 where nFermat = length [() | a<-[1..m], b<-[1..m], c<-[1..m], a^3+b^3==c^3]
       m = 200

main = interact $ show . foos . read
 where foos n = foo <$> [0..n]

这样,无论您使用哪个x参数,都会重复使用相同的nFermat

sagemuej@sagemuej-X302LA:/tmp$ time runhaskell fermata.hs <<< 1
[0,0]
real    0m23.199s
user    0m23.177s
sys 0m0.045s
sagemuej@sagemuej-X302LA:/tmp$ time runhaskell fermata.hs <<< 100
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
real    0m22.629s
user    0m22.601s
sys 0m0.052s