最近版本的GHC有一个新的“插件”功能,你可以编写普通的Haskell代码,像往常一样编译它,然后将它插入到编译器中,这样它就可以摆弄GHC的内部状态。
哪个很酷。然而,有一个小障碍:为了做到这一点,插件必须已经编译(似乎很明显)并在包DB中注册为包!
一旦插件完成,这很好;打包并把它放在Hackage上供所有人享用。但是,在尝试开发包装时,您如何解决这个问题呢?编辑 - 编译 - 执行循环在每次编辑时如何工作,您必须手动取消注册旧包,构建新包并注册它?
基本上,在尝试开发插件时,是否有某种方法可以支持这一要求?
如果你正在使用Cabal,它应该为你管理一切:
my-plugin.cabal
cabal-version: 2.4
name: my-plugin
version: 1.0.0.0
library
build-depends: base ^>= 4.12.0.0
, ghc ^>= 8.6.1
hs-source-dirs: src
exposed-modules: MyPlugin
-- could also be an internal library, executable, etc
test-suite test-plugin
type: exitcode-stdio-1.0
-- the dependency on my-plugin is everything, placing it
-- in the package DB of the GHC compiling this test
build-depends: base, my-plugin
hs-source-dirs: test
ghc-options: -fplugin=MyPlugin
main-is: Main.hs
src/MyPlugin.hs
module MyPlugin(plugin) where
import GhcPlugins
-- this is an example plugin from the manual
-- it prints the names of the non-recursive bindings in each module
plugin :: Plugin
plugin = defaultPlugin {
installCoreToDos = install
}
install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]
install _ todo = do
return (CoreDoPluginPass "Say name" pass : todo)
pass :: ModGuts -> CoreM ModGuts
pass guts = do dflags <- getDynFlags
bindsOnlyPass (mapM (printBind dflags)) guts
where printBind :: DynFlags -> CoreBind -> CoreM CoreBind
printBind dflags bndr@(NonRec b _) = do
putMsgS $ "Non-recursive binding named " ++ showSDoc dflags (ppr b)
return bndr
printBind _ bndr = return bndr
test/Main.hs
module Main where
import Numeric.Natural
import Prelude hiding (even, odd)
-- printed
x :: Int
x = 5
-- not printed
fixObvious :: (a -> a) -> a
fixObvious f = f (fixObvious f)
-- printed
fixSubtle :: (a -> a) -> a
fixSubtle f = let x = f x in x
-- neither printed
even, odd :: Natural -> Bool
even 0 = True
even n = odd (n - 1)
odd 0 = False
odd n = even (n - 1)
-- printed
main :: IO ()
main = return ()
-- if the plugin were more interesting, you may want to test that any
-- modified definitions act like you expect them to