Recent versions of GHC have a new "plugins" feature, where you can write ordinary Haskell code, compile it as usual, and then insert it into the compiler so it can fiddle with GHC's internal state.
Which is very cool. However, there's a small snag: In order to do this, the plugin has to be already compiled (seems obvious) and registered as a package in the package DB!
That's fine once the plugin is finished; package it up and put it on Hackage for all to enjoy. But how do you get around this while trying to develop the package? How does your edit-compile-execute cycle work when at every edit, you have to manually deregister the old package, build the new one, and register it?
Basically, is there some way I can side-step this requirement while trying to develop a plugin?
If you're using Cabal, it should manage everything for you:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With