为什么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
这是为什么?我是否必须为每个可能的实现编写重写规则?
答案 0 :(得分:13)
在许多情况下,规则不会触发,因为在规则有机会触发之前,非常简单的函数f
被内联。如果你延迟内联,
{-# INLINE [1] f #-}
规则
{-# RULES "f/f" forall a. f (f a) = 4*a #-}
应解雇所有这些案件(在此处使用7.2.2和7.4.1)。
原因是规则匹配器不是过于复杂,它只匹配具有规则的句法形式的表达式(不完全正确,规则体也经历了一些规范化)。表达式f $ f 3
或f . 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]