GHC/As a library (up to 6.8)
From HaskellWiki
| Line 183: | Line 183: | ||
(Interactive evaluation works in BatchCompile mode too! There are still other subtle differences, so this is not recommended.) | (Interactive evaluation works in BatchCompile mode too! There are still other subtle differences, so this is not recommended.) | ||
| + | |||
| + | === Type Checking === | ||
| + | |||
| + | Once the modules are loaded in the session, they are already type-checked. | ||
| + | The type infos of a loaded module are stored in a data-structure called | ||
| + | <hask>ModuleInfo</hask>. | ||
| + | |||
| + | Recalling the example in the previous subsection, we find that the variable <hask>usermod</hask> captures the user-defined module "Main", we retrieve the module information, | ||
| + | |||
| + | <haskell> | ||
| + | mb_userModInfo <- getModuleInfo session usermod | ||
| + | case mb_userModInfo of | ||
| + | Just userModInfo -> | ||
| + | let userTyThings = modInfoTyThings userModInfo -- access the type environments | ||
| + | userTys = map (\tything -> case tything of -- we only want to access the declared ids. | ||
| + | GHC.AnId i -> Just $ (tything, GHC.idType i) | ||
| + | _ -> Nothing ) userTyThings | ||
| + | in ... -- do something with userTys | ||
| + | Nothing -> return () | ||
| + | </haskell> | ||
| + | |||
=== Queries === | === Queries === | ||
Revision as of 08:48, 31 May 2007
Using GHC as a library
Contents |
In GHC 6.5 and subsequently you can import GHC as a Haskell library, which lets you write a Haskell program that has access to all of GHC.
This page is a place for everyone to add
- Notes about how to get it working
- Comments about the API
- Suggestions for improvement
and so on.
More documentation is available on the GHC wiki: http://cvs.haskell.org/trac/ghc/wiki/Commentary/Compiler/API
1 Getting started
You'll need a version of GHC (at least 6.5) that supports the GHC API. The GHC download page offers stable releases and development versions; you can also use CVS (instructions) or darcs (e.g., darcs get --partial http://darcs.haskell.org/ghc).
To use the GHC API you say
import GHCDoing this imports the module GHC from the package ghc. This module exports the "GHC API", which is still in a state of flux. Currently it's not even Haddock-documented. You can see the source code (somewhat documented). There are also other modules of interest as you do more special things.
Here's an example main program that does it Media:Main.hs (good for GHC 6.6). You need to manually change the value of myGhcRoot to point to your GHC directory.
To compile Media:Main.hs, you have to turn on the flag "-package ghc", e.g.
ghc -package ghc Main.hs
2 Common use cases and functions
Assumes GHC 6.6.
2.1 Default exception handling
If you don't handle exceptions yourself, you are recommended to wrap all code inside the wrapper:
defaultErrorHandler :: DynFlags -> IO a -> IO a DynFlags.defaultDynFlags :: DynFlags
This catches exceptions and prints out exception details and exits your program with exit code 1.
Example:
import GHC import DynFlags(defaultDynFlags) main = defaultErrorHandler defaultDynFlags $ do {- stuff in the following subsections -}
2.2 Initialization
First create a session:
newSession :: GhcMode -- BatchCompile | Interactive | MkDepend | ... -> Maybe FilePath -- GHC installation directory -> IO Session -- your seesion; you will need it
The path to your GHC installation directory (e.g., /usr/local/lib/ghc-6.6) is in practice mandatory, even though in theory marked as optional.
The session is configurable by dynamic flags (GHC dynamic flags plus session state; think -O2, -fvia-C, -fglasgow-exts, -package). This can be done with:
getSessionDynFlags :: Session -> IO DynFlags setSessionDynFlags :: Session -> DynFlags -> IO [PackageId] -- important iff dynamic-linking parseDynamicFlags :: DynFlags -- old flags -> [String] -- e.g., all or part of getArgs -> IO (DynFlags, [String]) -- new flags, unknown args
data DynFlags = DynFlags { ..., hscTarget :: HscTarget } -- HscC | HscAsm | HscInterpreted | ...
Examples:
- vanilla compiler, use all defaults (rare but good start)
session <- newSession BatchCompile (Just "/usr/local/lib/ghc-6.6") getSessionDynFlags session >>= setSessionDynFlags session
- compiler with custom flags, easy with parser
session <- newSession BatchCompile (Just "/usr/local/lib/ghc-6.6") f0 <- getSessionDynFlags session (f1,b) <- parseDynamicFlags f0 ["-fglasgow-exts", "-O", "-package", "ghc", "-package Cabal", "foo", "-v", "bar"] -- b = ["foo", "bar"]; the other args are recognized -- in GHC 6.6 "-O" implies "-fvia-C", that kind of thing is automatic here too setSessionDynFlags session f1
- interactive session with interpreter
session <- newSession Interactive (Just "/usr/local/lib/ghc-6.6") f0 <- getSessionDynFlags session setSessionDynFlags session f0{hscTarget = HscInterpreted}
2.3 Load or compile modules
To compile code or load modules, first set one or more targets, then call theguessTarget :: String -- "filename.hs" or "filename.lhs" or "MyModule" -> Maybe Phase -- if not Nothing, specifies starting phase -> IO Target addTarget :: Session -> Target -> IO () setTargets :: Session -> [Target] -> IO () getTargets :: Session -> IO [Target] removeTarget :: Session -> TargetId -> IO () load :: Session -> LoadHowMuch -> IO SuccessFlag data LoadHowMuch = LoadAllTargets | LoadUpTo ModuleName | LoadDependenciesOf ModuleName
defaultCleanupHandler :: DynFlags -> IO a -> IO a
Example:
t <- guessTarget "Main.hs" Nothing addTarget session t -- setTargets session [t] is also good f <- getSessionDynFlags session sf <- defaultCleanupHandler f (load session LoadAllTargets) case sf of Succeeded -> ... Failed -> ...
Dependencies (both modules and packages) are processed automatically, and an executable is produced if appropriate, precisely like --make.
Modules are compiled as per the2.4 Interactive evaluation
Interactive evaluation ala GHCi is done bysetContext :: Session -> [Module] -- their top levels will be visible -> [Module] -- their exports will be visible -> IO () getContext :: Session -> IO ([Module], [Module]) findModule :: Session -> ModuleName -> Maybe PackageId -> IO Module mkModule :: PackageId -> ModuleName -> Module mkModuleName :: String -> ModuleName PackageConfig.stringToPackageId :: String -> PackageId
-- equivalent to GHCi's :m Prelude Control.Monad *Main prelude <- findModule session (mkModuleName "Prelude") Nothing monad <- findModule session (mkModuleName "Control.Monad") Nothing usermod <- findModule session (mkModuleName "Main") Nothing -- we have loaded this setContext session [usermod] [prelude,monad]
Having set a useful context, we're now ready to evaluate.
runStmt :: Session -> String -> IO RunResult data RunResult = RunOk [Name] -- names bound by the expression | RunFailed | RunException GHC.IOBase.Exception -- that's Control.Exception.Exception
Example:
runStmt session "let n = 2 + 2" -- n is bound runStmt session "n" -- 4 is printed (note "it" is bound)
(Interactive evaluation works in BatchCompile mode too! There are still other subtle differences, so this is not recommended.)
2.5 Type Checking
Once the modules are loaded in the session, they are already type-checked. The type infos of a loaded module are stored in a data-structure called
mb_userModInfo <- getModuleInfo session usermod case mb_userModInfo of Just userModInfo -> let userTyThings = modInfoTyThings userModInfo -- access the type environments userTys = map (\tything -> case tything of -- we only want to access the declared ids. GHC.AnId i -> Just $ (tything, GHC.idType i) _ -> Nothing ) userTyThings in ... -- do something with userTys Nothing -> return ()
2.6 Queries
-- Get module dependency graph getModuleGraph :: Session -> IO ModuleGraph -- ModuleGraph = [ModSummary] -- Get bindings getBindings :: Session -> IO [TyThing]
2.7 Messages
Compiler messages (including progress, warnings, errors) are controlled by verbosity and routed through a callback mechanism. These are fields indata DynFlags = DynFlags { ..., verbosity :: Int, log_action :: Severity -> SrcLoc.SrcSpan -> Outputable.PprStyle -> ErrUtils.Message -> IO () }
You can set the callback to your logger, like
f <- getSessionDynFlags session setSessionDynFlags session f{log_action = my_log_action}
This sets the session's logger, but it will not see exceptions.
If you callmain = defaultErrorHandler defaultDynFlags{log_action = my_log_action} $ do ...
3 Interactive mode example
The file Media:Interactive.hs (also requires Media:MyPrelude.hs) serves as an example for using GHC as a library in interactive mode. It also shows how to replace some of the standard prelude functions with modified versions. See the comments in the code for further information.
4 Using the GHC library from inside GHCi
This works, to some extent. However, beware about loading object code, because there is only a single linker symbol table in the runtime, so GHCi will be sharing the symbol table with the new GHC session.
$ ghci -package ghc Prelude> :m + GHC PackageConfig Prelude GHC> session <- newSession Interactive (Just "/usr/local/lib/ghc-6.6") Prelude GHC> setSessionDynFlags session =<< getSessionDynFlags session Prelude GHC> setContext session [] [mkModule (stringToPackageId "base") (mkModuleName "Prelude")] Prelude GHC> runStmt session "let add1 x = x + 1" Prelude GHC> runStmt session "add1 2" 3
