GHCの生成するコードを理解しようという話
と、見せかけて、ただ単にこれを読んだ小学生の感想文なので、元の論文が読める人には必要無い
GHCは中では "元 -> Core言語 -> Stg言語 -> なんか -> なんか " という構造になっていることは、どっかで読んだことあるかと思う
Core言語、STG言語は、それぞれ、言語になってて、Haskell言語はそれに変換されたあと、最適化されたりして、Cコードになる。
簡単に説明しておくと、Core言語が、型クラスとか、ややこしいCase式とかを取り除いたもの。STG言語が…(ただいま調査中あとで書く)
Core言語というのは、Haskell言語からややこしいSyntax Sugarを取り除いたものだと考えればよいと思う。GHCの中では、"Haskell言語 → Core言語" の変換の名前が"Desugar"になってるとか。(compiler/deSugar/Desugar.lhs:deSugarExpr)。ちなみに、HsExprがHaskellの式で、LHsExprが場所付き式。 CoreExprがCore言語の式(という解釈であってるはず)
例えば、↓こんなコードがあったとしたら、
c:: Eq a=> [a]->a->Int
c x k = case x of
[] -> 3
(xs:_) | k == xs -> 4
| otherwise -> 9
Core言語には、Boolによる条件判断、if式というのが存在していないので、ガードの部分は表現できない。 こういうのは全部、Case式に変換される
-ddump-simplオプション付きでコンパイルすると、途中のCore言語が見れるので、それで上のがどう変換されたか確認すると、
[GlobalId]
[Arity 3
NoCafRefs]
Nanika.c =
\ (@ a_ajB)
($dEq_ajH :: {GHC.Base.Eq a_ajB})
(x_aj8 :: [a_ajB])
(k_aj9 :: a_ajB) ->
case x_aj8 of wild_B1 {
[] -> GHC.Base.I# 3;
: xs_ajc ds_dpC ->
case $dEq_ajH of tpl_Xh { GHC.Base.:DEq tpl1_B2 tpl2_B3 ->
case tpl1_B2 k_aj9 xs_ajc of wild1_Xs { -- ここらへん --
GHC.Base.False -> GHC.Base.I# 9; GHC.Base.True -> GHC.Base.I# 4
}
}
}
こんな感じ。ガードがcase式になっとる。
…と、思ったけど、それ以前にCore言語が読めないと思った。
まず、最初の GlobalId…ちょっとわからん。
Arity…引数の数? NoCafRefs 外の変数を参照してるかどうか?
んで、次。@で始まってるのが、多相型の。んで、引数が並んで "->"で関数。
まあそういうのはいいか。とにかく、Core言語というのは、ほんと原始的な関数言語という理解でよいと思う。(compiler/coreSyn/CoreSyn.lhsらへん参考)
↓こういうのを-ddump-simplしてみると良いと思う
d::Int->Int->Int d 3 _ = 4 d _ _ = 9
ちなみに、GHCの中間表現を見るときは、数字リテラルに気を付けたほうがいい。 数字リえテラルは Num の値になって、なんか Int とは違う動きになるので、Intを明示するようにしましょう。
add x = 3+ x addI x = (3::Int)+ (x::Int)
Nanika.add =
\ (@ t_apf) ($dNum_apk :: {GHC.Num.Num t_apf}) ->
let {
lit_apg :: t_apf
[]
lit_apg =
case $dNum_apk
of tpl_Xp { GHC.Num.:DNum tpl1_B2 tpl2_B3 tpl3_B4 tpl4_B5 tpl5_B6 tpl6_B7 tpl7_B8 tpl8_B9 tpl9_Ba ->
tpl9_Ba (GHC.Num.S# 3)
}
} in
\ (x_ajz :: t_apf) ->
case $dNum_apk
of tpl_Xn { GHC.Num.:DNum tpl1_B2 tpl2_B3 tpl3_B4 tpl4_B5 tpl5_B6 tpl6_B7 tpl7_B8 tpl8_B9 tpl9_Ba ->
tpl3_B4 lit_apg x_ajz
}
Nanika.addI =
\ (x_ajB :: GHC.Base.Int) ->
case GHC.Num.$f6
of tpl_XC { GHC.Num.:DNum tpl1_B2 tpl2_B3 tpl3_B4 tpl4_B5 tpl5_B6 tpl6_B7 tpl7_B8 tpl8_B9 tpl9_Ba ->
tpl3_B4 (GHC.Base.I# 3) x_ajB
}
こんなに違う!