単純な Haskell プログラム (インポートされたライブラリはなく、データ型と純粋な関数のみ) を型指定されていないラムダ計算の項に変換する方法を探しています。有望なアプローチは、GHC APIを使用してプログラムをGHC coreにコンパイルし、型なしラムダ計算に変換することです。
GHC API を使用して Haskell プログラムをロードし、Core にコンパイルする方法は?
単純な Haskell プログラム (インポートされたライブラリはなく、データ型と純粋な関数のみ) を型指定されていないラムダ計算の項に変換する方法を探しています。有望なアプローチは、GHC APIを使用してプログラムをGHC coreにコンパイルし、型なしラムダ計算に変換することです。
GHC API を使用して Haskell プログラムをロードし、Core にコンパイルする方法は?
ghc docsのGHC
モジュール ドキュメントから:
compileToCoreModule :: GhcMonad m => FilePath -> m CoreModule
これは、モジュールに対応する Core バインディングにアクセスする方法です。
compileToCore
モジュールの解析、型チェック、および脱糖を行い、成功した場合は結果の Core モジュール (モジュール名、型宣言、および関数宣言で構成される) を返します。
compileToCoreSimplified :: GhcMonad m => FilePath -> m CoreModule
と同様
compileToCoreModule
ですが、単純化されたコアを返すために単純化を呼び出します。
GHC
これは、モジュールのリストを調べて、Desugar
モジュールに注目ModGuts
し、 の結果に注目し、deSugar
すべてのドキュメントをダウンロードし、テキストで を検索して見つけましたModGuts
。
この例では、コアがどのように見えるかを確認できるように、単純なモジュールをコンパイルします。ghc-pathsを使用して、ghc libs ディレクトリの場所を提供します。コアは、 s のCoreModule
リストを含むによってメモリ内で表されます。で説明されている AST のインスタンスがないため、AST を直接ダンプすることはできませんが、インスタンスはコアをきれいに出力するので、コアにコンパイルしたことがわかります。CoreBind
Show
CoreSyn
Outputable
CoreModule
import GHC
import DynFlags
import Outputable (Outputable, showPpr)
import qualified GHC.Paths as Paths
import Data.Functor
runGhc'
import
noと noを使用して、モジュールをコアにコンパイルするために必要なすべてのセットアップを処理しますTemplateHaskell
。でリンカを完全にオフにし、 でNoLink
何も生成しないようにコンパイラに指示しHscNothing
ます。
runGhc' :: Ghc a -> IO a
runGhc' ga = do
runGhc (Just Paths.libdir) $ do
dflags <- getDynFlags
let dflags2 = dflags { ghcLink = NoLink
, hscTarget = HscNothing
}
setSessionDynFlags dflags2
ga
モジュールをコアにコンパイルするには、 と を使用してターゲットを設定しguessTarget
、addTarget
オプションで を使用して依存関係をロードしload
、 を使用してモジュール グラフを構築し、モジュール グラフ内で正しいものを ing し、depanel
を使用してモジュールを解析し、 を使用して型をチェックし、 を使用して脱糖し、 を使用して変換します脱糖の結果のインスタンスから、からコアを抽出します。これらはすべて、 によって素敵なパッケージにまとめられています。find
ModSummary
parseModule
typecheckModule
desugarModule
ModGuts
coreModule
DesugaredMod
ModGuts
compileToCoreModule
compileExample :: Ghc CoreModule
compileExample = compileToCoreModule "prettyPrint2dList.hs"
私たちのサンプルプログラム全体は、 でコアを出力しshowPpr
ます。
showPpr' :: (Functor m, Outputable a, HasDynFlags m) => a -> m String
showPpr' a = (flip showPpr) a <$> getDynFlags
main = runGhc' (compileExample >>= showPpr') >>= putStrLn
上記の例をコンパイルするには-package ghc
、通常は非表示の ghc api パッケージを公開するフラグが必要です。
コアにコンパイルするサンプル モジュール に"prettyPrint2dList.hs"
は、データ型と、プレリュードの関数を使用するコードが含まれています。
data X = Y | Z
deriving (Eq, Show)
type R = [X]
type W = [R]
example = map (\x -> take x (cycle [Y, Z])) [0..]
main = undefined
これにより、きれいに印刷されたコアが大量に生成されます。
%module main:Main (Safe-Inferred) [01D :-> Identifier `:Main.main',
a1f2 :-> Identifier `$c==', a1f5 :-> Identifier `$c/=',
a1fb :-> Identifier `$cshowsPrec', a1fh :-> Identifier `$cshow',
a1fk :-> Identifier `$cshowList',
r0 :-> Identifier `Main.$fShowX', r1 :-> Identifier `Main.$fEqX',
r2 :-> Type constructor `Main.R',
r3 :-> Type constructor `Main.X', r4 :-> Identifier `Main.main',
rqS :-> Type constructor `Main.W',
rrS :-> Data constructor `Main.Y', rrV :-> Identifier `Main.Y',
rrW :-> Data constructor `Main.Z', rrX :-> Identifier `Main.Z',
rL2 :-> Identifier `Main.example']
Main.example :: [[Main.X]]
[LclIdX, Str=DmdType]
Main.example =
GHC.Base.map
@ GHC.Types.Int
@ [Main.X]
(\ (x :: GHC.Types.Int) ->
GHC.List.take
@ Main.X
x
(GHC.List.cycle
@ Main.X
(GHC.Types.:
@ Main.X
Main.Y
(GHC.Types.: @ Main.X Main.Z (GHC.Types.[] @ Main.X)))))
(GHC.Enum.enumFrom
@ GHC.Types.Int GHC.Enum.$fEnumInt (GHC.Types.I# 0))
Main.main :: forall t. t
[LclIdX, Str=DmdType]
Main.main = GHC.Err.undefined
$cshowsPrec :: GHC.Types.Int -> Main.X -> GHC.Show.ShowS
[LclId, Str=DmdType]
$cshowsPrec =
\ _ [Occ=Dead] (ds_d1gG :: Main.X) ->
case ds_d1gG of _ [Occ=Dead] {
Main.Y ->
GHC.Show.showString
(GHC.Types.:
@ GHC.Types.Char
(GHC.Types.C# 'Y')
(GHC.Types.[] @ GHC.Types.Char));
Main.Z ->
GHC.Show.showString
(GHC.Types.:
@ GHC.Types.Char
(GHC.Types.C# 'Z')
(GHC.Types.[] @ GHC.Types.Char))
}
Main.$fShowX [InlPrag=[ALWAYS] CONLIKE] :: GHC.Show.Show Main.X
[LclIdX[DFunId],
Str=DmdType,
Unf=DFun: \ ->
GHC.Show.D:Show TYPE Main.X $cshowsPrec $cshow $cshowList]
Main.$fShowX =
GHC.Show.D:Show @ Main.X $cshowsPrec $cshow $cshowList;
$cshowList [Occ=LoopBreaker] :: [Main.X] -> GHC.Show.ShowS
[LclId, Str=DmdType]
$cshowList =
GHC.Show.showList__
@ Main.X
(GHC.Show.showsPrec @ Main.X Main.$fShowX (GHC.Types.I# 0));
$cshow [Occ=LoopBreaker] :: Main.X -> GHC.Base.String
[LclId, Str=DmdType]
$cshow = GHC.Show.$dmshow @ Main.X Main.$fShowX;
$c== :: Main.X -> Main.X -> GHC.Types.Bool
[LclId, Str=DmdType]
$c== =
\ (ds_d1gB :: Main.X) (ds_d1gC :: Main.X) ->
let {
fail_d1gD :: GHC.Prim.Void# -> GHC.Types.Bool
[LclId, Str=DmdType]
fail_d1gD = \ _ [Occ=Dead, OS=OneShot] -> GHC.Types.False } in
case ds_d1gB of _ [Occ=Dead] {
Main.Y ->
case ds_d1gC of _ [Occ=Dead] {
__DEFAULT -> fail_d1gD GHC.Prim.void#;
Main.Y -> GHC.Types.True
};
Main.Z ->
case ds_d1gC of _ [Occ=Dead] {
__DEFAULT -> fail_d1gD GHC.Prim.void#;
Main.Z -> GHC.Types.True
}
}
Main.$fEqX [InlPrag=[ALWAYS] CONLIKE] :: GHC.Classes.Eq Main.X
[LclIdX[DFunId],
Str=DmdType,
Unf=DFun: \ -> GHC.Classes.D:Eq TYPE Main.X $c== $c/=]
Main.$fEqX = GHC.Classes.D:Eq @ Main.X $c== $c/=;
$c/= [Occ=LoopBreaker] :: Main.X -> Main.X -> GHC.Types.Bool
[LclId, Str=DmdType]
$c/= =
\ (a :: Main.X) (b :: Main.X) ->
GHC.Classes.not (GHC.Classes.== @ Main.X Main.$fEqX a b);
:Main.main :: GHC.Types.IO GHC.Prim.Any
[LclIdX, Str=DmdType]
:Main.main =
GHC.TopHandler.runMainIO
@ GHC.Prim.Any (Main.main @ (GHC.Types.IO GHC.Prim.Any))