Haskell重写规则和函数组合

时间:2012-02-12 23:18:53

标签: haskell rules ghc function-composition

为什么haskell需要多个重写规则,具体取决于函数组合技术和长度?有没有办法避免这种情况?

例如,给出以下代码......

{-# RULES
"f/f" forall a. f ( f a ) = 4*a
  #-}
f a = 2 * a

这适用于

test1 = f ( f 1 )

但是我们需要为

添加规则
test2 = f . f $ 1

test3 = f $ f 1

给我们留下以下规则

{-# RULES
"f/f1" forall a. f ( f a ) = 4 * a
"f/f2" forall a. f . f $ a  = 4 * a
"f/f3" forall a. f $ f $ a  = 4 * a
   #-}

但是,当我们将这些组合在一起或使用其他形式的组合时,规则就不会触发。

test4 = f . f . f $ 1
test5 = f $ f $ f $ 1
test6 = f $ 1

这是为什么?我是否必须为每个可能的实现编写重写规则?

2 个答案:

答案 0 :(得分:13)

在许多情况下,规则不会触发,因为在规则有机会触发之前,非常简单的函数f被内联。如果你延迟内联,

{-# INLINE [1] f #-}

规则

{-# RULES "f/f" forall a. f (f a) = 4*a #-}

应解雇所有这些案件(在此处使用7.2.2和7.4.1)。

原因是规则匹配器不是过于复杂,它只匹配具有规则的句法形式的表达式(不完全正确,规则体也经历了一些规范化)。表达式f $ f 3f . f $ 4与规则的句法形式不匹配。要使规则匹配,必须进行一些重写,在规则与表达式匹配之前必须内联($)(.)。但是如果你没有阻止f在简化器的第一阶段被内联,它会在($)(.)内联的同一行中被它的主体取代,所以在在下一次迭代中,简化器不再看到f,它只看到2*(2*x),它与规则不匹配。

答案 1 :(得分:3)

我原本以为默认会这样做,但是你可以添加两个重写规则来使./$简化为lambdas / application,这样总会匹配:

{-# RULES
"f/f" forall a. f ( f a ) = 4*a

"app" forall f x. f $ x = f x
"comp" forall f g. f . g = (\x -> f (g x))
  #-}

f a = 3 * a -- make this 3*a so can see the difference

测试:

main = do
    print (f . f $ 1)
    print (f (f 1))
    print (f $ f 1)
    print (f $ f $ 1)
    print (f $ f $ f $ f $ 1)
    print (f . f . f . f $ 1)
    print (f $ f $ f $ 1)
    print (f . f . f $ 1)
    print (f $ 1)

输出:

4
4
4
4
16
16
12
12
3

由于其他重写规则,这也适用于一些(但不是全部)更加模糊的案例。例如,所有这些都可行:

mapf x = map f $ map f $ [x]
mapf' x = map (f.f) $ [x]
mapf'' x = map (\x -> f (f x)) $ [x]