Как скомпилировать 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))
Другие вопросы по тегам