Как скомпилировать Haskell в нетипизированное лямбда-исчисление (или ядро GHC)?
Я ищу способы, как преобразовать простую программу на Haskell (без импортированных библиотек, только типы данных и чистые функции) в термин нетипизированного лямбда-исчисления. Похоже, многообещающим подходом является использование GHC API для компиляции программы в ядро GHC, которое затем можно преобразовать в нетипизированное лямбда-исчисление.
Как использовать GHC API для загрузки программы на Haskell и ее компиляции в Core?
1 ответ
От GHC
документация по модулю в ghc docs:
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. Ядро будет представлено в памяти CoreModule
содержащий список CoreBind
с. Мы не можем сбросить AST напрямую, потому что нет Show
случаи для AST описаны в CoreSyn
, Тем не менее Outputable
экземпляр для CoreModule
будет красиво печатать ядро, чтобы мы могли видеть, что мы скомпилировали ядро.
import GHC
import DynFlags
import Outputable (Outputable, showPpr)
import qualified GHC.Paths as Paths
import Data.Functor
runGhc'
заботится обо всех настройках, необходимых для компиляции ядра модуля без import
с и нет 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
, построение графа модуля с 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))