简单问题:为什么不触发重写规则?
{-# RULES "fmap/fmap" forall f g xs. fmap f (fmap g xs) = fmap (f.g) xs #-}
main = do
txt <- fmap head (fmap words (readFile "foo.txt"))
print txt
Run Code Online (Sandbox Code Playgroud)
现在我想写一个提取fun
触发规则,因为它在之前的测试中做了...不是这次.
{-# RULES "fmap/fmap" forall f g xs. fmap f (fmap g xs) = fmap (f.g) xs #-}
fun f g xs = fmap f (fmap g xs)
main = do
txt <- fun (drop 1) words (readFile "foo.txt")
print txt
Run Code Online (Sandbox Code Playgroud)
直到我偶然添加了一个模块名称:
module Main where
{-# RULES "fmap/fmap" forall f g xs. fmap f (fmap g xs) = fmap (f.g) xs #-}
fun f g xs = fmap f (fmap g xs)
main = do
txt <- fun head words (readFile "foo.txt")
print txt
Run Code Online (Sandbox Code Playgroud)
现在,如果我只是在main函数中写出函数应用程序,它仍然无效.
把它们加起来:
txt <- fmap head (fmap words (readFile "foo"))
不起作用txt <- fun head words (readFile "foo")
不起作用txt <- fun head words (readFile "foo")
加模块工作fun f g xs = fmap f . fmap g $ xs
加模块不起作用fun f g xs = f <$> (g <$> xs)
加模块工作(但稍后开火)所有这一切都是通过电话完成的ghc --make -O2 -ddump-rule-firings Main.hs
.样本输出:
# ghc --make -O2 -ddump-rule-firings Main.hs
[1 of 1] Compiling Main ( Main.hs, Main.o )
Rule fired: fmap/fmap
Rule fired: unpack
Rule fired: Class op >>=
Rule fired: Class op fmap
Rule fired: Class op fmap
Rule fired: Class op show
Rule fired: Class op showList
Rule fired: unpack-list
Linking Main ...
Run Code Online (Sandbox Code Playgroud)
Ørj*_*sen 13
鉴于什么@Cactus说,我相信在这里发生的是,该规则Class op fmap
取代你fmap
与它的方法定义IO
:
instance Functor IO where
fmap f x = x >>= (pure . f)
Run Code Online (Sandbox Code Playgroud)
如果这种情况发生在任何地方,在您的规则被触发之前,那么fmap
您的代码中将不会留下(GHC的内部表示)您自己的规则来触发.
当GHC在特定类型中使用时,GHC会尝试对它们进行特化处理,因此如果单独fmap
使用monad 是完全已知的,那么一旦完成专门化就不会有任何泛型fmap
.
所以,剩下的问题是,为什么没有你的规则火,当你提供一个模块头?
module Main where
Run Code Online (Sandbox Code Playgroud)
答案在于,如果您不提供任何内容,则与使用的默认模块标头略有不同:
module Main (main) where
Run Code Online (Sandbox Code Playgroud)
请注意,这明确地出口只有 main
从模块.您的版本没有导出列表,而是导出模块中定义的所有内容,包括 main
和fun
.
当只有main
出口,GHC可以推断,fun
使用只在内部main
,和内联它完全没有,没有打扰做一个独立版本.然后它注意到fmap
s仅用于IO
,并专门化它们.或者它可能以相反的顺序执行,但最终结果是相同的.
何时fun
也导出,GHC必须假设您的模块的用户可能想要在任何monad中调用它.因此GHC然后编译独立版本的fun
一个通用的单子,这也保持fmap
通用的,您的规则能够触发这个版本.
但是,即使对于显式module
代码,Class op fmap
规则在编译时会触发两次,就像它应用于两个单独的fmap
s一样.因此,我怀疑即使在这种情况下,在您的规则简化为仅使用一个之前fun
内联和专用,因此内部使用的内联版本仍然不会将规则应用于它.main
fmap
main