You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GHC高阶函数参数内联优化对应的实现机制问询

GHC递归函数的参数内联优化逻辑探究

我试图探究GHC中一类特定内联行为的实现逻辑,该行为指作为参数传入另一个函数的函数会被内联。举例来说,我编写了如下定义(使用自定义List类型以避免触发内置重写规则):

data MyList a = Nil | Cons a (MyList a)
  deriving (Show)

mapMyList :: (a -> b) -> MyList a -> MyList b
mapMyList f Nil = Nil
mapMyList f (Cons a rest) = Cons (f a) $ mapMyList f rest

随后的调用代码如下:

fromList :: [a] -> MyList a
fromList = ...

main = do
  print $ mapMyList (*2) $ fromList [1..5]

mapMyList是递归函数,正常来说无法直接被内联。但在生成的Core中间代码中,我看到了如下定义:

Rec {
-- RHS size: {terms: 16, types: 11, coercions: 0, joins: 0/0}
Main.main_$smapMyList [Occ=LoopBreaker] :: MyList Int -> MyList Int
[GblId, Arity=1, Str=<S,1*U>, Unf=OtherCon []]
Main.main_$smapMyList
  = \ (sc_s2Rb :: MyList Int) ->
      case sc_s2Rb of {
        Nil -> Main.Nil @Int;
        Cons a_aBe rest_aBf ->
          Main.Cons
            @Int
            (case a_aBe of { GHC.Types.I# x_a24u ->
             GHC.Types.I# (GHC.Prim.*# x_a24u 2#)
             })
            (Main.main_$smapMyList rest_aBf)
      }
end Rec }

可以注意到,生成的smapMyList不再接受函数作为参数,且传入的(* 2)已经被完全内联到代码逻辑中。我想知道是哪一个优化Pass生成了这段代码,具体运行逻辑是怎样的?
我找到了相关的GHC issue,其中提到希望通过SPECIALIZE编译指示来保证该行为,这让我最初认为是特化Pass实现了该优化。但我查阅GHC特化相关文档时发现,特化通常针对类型类字典而非普通函数参数(我的示例中完全没有用到类型类)。
我也查阅了静态参数变换(Static Argument Transformation)相关内容,它看起来与该优化高度相关,GHC源码中对该Pass的描述如下:

可被视为移除循环中的不变量:递归函数的参数若在递归调用中不会发生变化,则会从递归环节中移除,仅在本地传递实际发生变化的参数。
然而我尝试使用-fno-static-argument-transformation -fno-specialise编译参数禁用这两个Pass后,发现该转换仍然会发生。
我提出该问题的动机是我正在另一个函数式语言Koka中实现同类转换,因此希望了解GHC的具体实现方案。

内容的提问来源于stack exchange,提问作者Steven Fontanella

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 23:24:03