能否在运行时生成和运行TemplateHaskell生成的代码?

11

是否可能在运行时生成和运行模板哈斯克尔(TemplateHaskell)生成的代码?

在运行时,使用C语言,我可以:

  • 创建一个函数的源码,
  • 调用gcc将其编译为.so文件(在Linux中),或者使用llvm等其他工具,
  • 加载.so文件,
  • 并调用该函数。

在模板哈斯克尔上能否做类似的事情呢?


模板Haskell生成的代码在运行时执行:P - Satvik
@Satvik - 显然是在生成的代码运行时,但我特别感兴趣的是在生成器的运行时运行生成的代码。 - fadedbee
2个回答

15

是的,这是可能的。 GHC API 将编译Template Haskell。 一个概念验证可在https://github.com/JohnLato/meta-th找到,它虽然不是非常复杂,但显示了一种通用技术,甚至提供了一定程度的类型安全性。 模板Haskell表达式是使用Meta类型构建的,然后可以编译和加载为可用函数。

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}

{-# OPTIONS_GHC -Wall #-}
module Data.Meta.Meta (
-- * Meta type
  Meta (..)

-- * Functions
, metaCompile
) where

import Language.Haskell.TH

import Data.Typeable as Typ
import Control.Exception (bracket)

import System.Plugins -- from plugins
import System.IO
import System.Directory

newtype Meta a = Meta { unMeta :: ExpQ }

-- | Super-dodgy for the moment, the Meta type should register the
-- imports it needs.
metaCompile :: forall a. Typeable a => Meta a -> IO (Either String a)
metaCompile (Meta expr) = do
  expr' <- runQ expr

  -- pretty-print the TH expression as source code to be compiled at
  -- run-time
  let interpStr = pprint expr'
      typeTypeRep = Typ.typeOf (undefined :: a)

  let opener = do
        (tfile, h) <- openTempFile "." "fooTmpFile.hs"
        hPutStr h (unlines
              [ "module TempMod where"
              , "import Prelude"
              , "import Language.Haskell.TH"
              , "import GHC.Num"
              , "import GHC.Base"
              , ""
              , "myFunc :: " ++ show typeTypeRep
              , "myFunc = " ++ interpStr] )
        hFlush h
        hClose h
        return tfile
  bracket opener removeFile $ \tfile -> do

      res <- make tfile ["-O2", "-ddump-simpl"]
      let ofile = case res of
                    MakeSuccess _ fp -> fp
                    MakeFailure errs -> error $ show errs
      print $ "loading from: " ++ show ofile
      r2 <- load (ofile) [] [] "myFunc"
      print "loaded"

      case r2 of
        LoadFailure er -> return (Left (show er))
        LoadSuccess _ (fn :: a) -> return $ Right fn

这个函数接受一个 ExpQ,首先在 IO 中运行它以创建一个普通的 Exp。然后将 Exp 漂亮地打印成源代码,在运行时编译和加载。在实践中,我发现生成的 TH 代码中指定正确的导入库是其中一个更困难的障碍。


4
有点可怕。 - Louis Wasserman

5

据我所了解,您想在运行时创建和运行代码,我认为您可以使用GHC API实现,但我不太确定您能够实现什么范围的内容。如果您想要像热代码交换这样的功能,可以查看hotswap包。


是的,看起来 GHC API + Hotswap 可以做到我想要的。 - fadedbee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接