Ответ 1
Я считаю, что нашел ответ. Я вспомнил, как читал Почему GHC делает исправление настолько путающим? и это предложило мне решение.
Проблема с прежним определением catam
заключается в том, что она рекурсивна, поэтому любая попытка INLINE игнорируется, Компиляция исходной версии с помощью -ddump-simpl -ddump-to-file
и чтение core:
Main.depth1 = Main.catam_$scatam @ GHC.Types.Int Main.depth3
Main.depth3 =
\ (ds_dyI :: Main.TreeT GHC.Types.Int) ->
case ds_dyI of _ {
Main.Leaf -> Main.depth4;
Main.Tree l_aah r_aai -> GHC.Classes.$fOrdInt_$cmax l_aah r_aai
}
Main.depth4 = GHC.Types.I# 0
Rec {
Main.catam_$scatam =
\ (@ a_ajB)
(eta_B1 :: Main.TreeT a_ajB -> a_ajB)
(eta1_X2 :: Main.Fix Main.TreeT) ->
eta_B1
(case eta1_X2
`cast` (Main.NTCo:Fix <Main.TreeT>
:: Main.Fix Main.TreeT ~# Main.TreeT (Main.Fix Main.TreeT))
of _ {
Main.Leaf -> Main.Leaf @ a_ajB;
Main.Tree l_aan r_aao ->
Main.Tree
@ a_ajB
(Main.catam_$scatam @ a_ajB eta_B1 l_aan)
(Main.catam_$scatam @ a_ajB eta_B1 r_aao)
})
end Rec }
явно хуже (создание/устранение конструктора в catam_$scatam
, больше вызовов функций) по сравнению с
Main.depth2 =
\ (w_s1Rz :: Main.Tree) ->
case Main.$wdepth2 w_s1Rz of ww_s1RC { __DEFAULT ->
GHC.Types.I# ww_s1RC
}
Rec {
Main.$wdepth2 [Occ=LoopBreaker] :: Main.Tree -> GHC.Prim.Int#
[GblId, Arity=1, Caf=NoCafRefs, Str=DmdType S]
Main.$wdepth2 =
\ (w_s1Rz :: Main.Tree) ->
case w_s1Rz
`cast` (Main.NTCo:Fix <Main.TreeT>
:: Main.Fix Main.TreeT ~# Main.TreeT (Main.Fix Main.TreeT))
of _ {
Main.Leaf -> 0;
Main.Tree l_aaj r_aak ->
case Main.$wdepth2 l_aaj of ww_s1RC { __DEFAULT ->
case Main.$wdepth2 r_aak of ww1_X1Sh { __DEFAULT ->
case GHC.Prim.<=# ww_s1RC ww1_X1Sh of _ {
GHC.Types.False -> ww_s1RC;
GHC.Types.True -> ww1_X1Sh
}
}
}
}
end Rec }
Но если мы определяем catam
как
{-# INLINE catam #-}
catam :: (Functor f) => (f a -> a) -> (Fix f -> a)
catam f = let u = f . fmap u . unfix
in u
то он больше не рекурсивный, а внутри u
. Таким образом, GHC строит catam
в определении depth1
и предохранители fmap
с depth1
g
- именно то, что мы хотим:
Main.depth1 =
\ (w_s1RJ :: Main.Tree) ->
case Main.$wdepth1 w_s1RJ of ww_s1RM { __DEFAULT ->
GHC.Types.I# ww_s1RM
}
Rec {
Main.$wdepth1 [Occ=LoopBreaker] :: Main.Tree -> GHC.Prim.Int#
[GblId, Arity=1, Caf=NoCafRefs, Str=DmdType S]
Main.$wdepth1 =
\ (w_s1RJ :: Main.Tree) ->
case w_s1RJ
`cast` (Main.NTCo:Fix <Main.TreeT>
:: Main.Fix Main.TreeT ~# Main.TreeT (Main.Fix Main.TreeT))
of _ {
Main.Leaf -> 0;
Main.Tree l_aar r_aas ->
case Main.$wdepth1 l_aar of ww_s1RM { __DEFAULT ->
case Main.$wdepth1 r_aas of ww1_X1So { __DEFAULT ->
case GHC.Prim.<=# ww_s1RM ww1_X1So of _ {
GHC.Types.False -> ww_s1RM;
GHC.Types.True -> ww1_X1So
}
}
}
}
end Rec }
который теперь совпадает с дампом depth2
.