hint
A Haskell interpreter built on top of the GHC API
https://github.com/haskell-hint/hint
Version on this page: | 0.9.0.8 |
LTS Haskell 23.1: | 0.9.0.8@rev:1 |
Stackage Nightly 2024-12-26: | 0.9.0.8@rev:1 |
Latest on Hackage: | 0.9.0.8@rev:1 |
hint-0.9.0.8@sha256:2fcd0fc73ebf628ea5743f5195c961ad3eb24e7c892e04adfd5907d6a74d47ca,3557
Module documentation for 0.9.0.8
- Hint
- Language
hint
This library defines an Interpreter monad within which you can interpret
strings like "[1,2] ++ [3]"
into values like [1,2,3]
. You can easily
exchange data between your compiled program and your interpreted program, as
long as the data has a Typeable
instance.
You can choose which modules should be in scope while evaluating these expressions, you can browse the contents of those modules, and you can ask for the type of the identifiers you’re browsing.
Example
{-# LANGUAGE LambdaCase, ScopedTypeVariables, TypeApplications #-}
import Control.Exception (throwIO)
import Control.Monad (when)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Writer (execWriterT, tell)
import Data.Foldable (for_)
import Data.List (isPrefixOf)
import Data.Typeable (Typeable)
import qualified Language.Haskell.Interpreter as Hint
-- |
-- Interpret expressions into values:
--
-- >>> eval @[Int] "[1,2] ++ [3]"
-- Right [1,2,3]
--
-- Send values from your compiled program to your interpreted program by
-- interpreting a function:
--
-- >>> Right f <- eval @(Int -> [Int]) "\\x -> [1..x]"
-- >>> f 5
-- [1,2,3,4,5]
eval :: forall t. Typeable t
=> String -> IO (Either Hint.InterpreterError t)
eval s = Hint.runInterpreter $ do
Hint.setImports ["Prelude"]
Hint.interpret s (Hint.as :: t)
-- |
-- >>> :{
-- do Right contents <- browse "Prelude"
-- for_ contents $ \(identifier, tp) -> do
-- when ("put" `isPrefixOf` identifier) $ do
-- putStrLn $ identifier ++ " :: " ++ tp
-- :}
-- putChar :: Char -> IO ()
-- putStr :: String -> IO ()
-- putStrLn :: String -> IO ()
browse :: Hint.ModuleName -> IO (Either Hint.InterpreterError [(String, String)])
browse moduleName = Hint.runInterpreter $ do
Hint.setImports ["Prelude", "Data.Typeable", moduleName]
exports <- Hint.getModuleExports moduleName
execWriterT $ do
for_ exports $ \case
Hint.Fun identifier -> do
tp <- lift $ Hint.typeOf identifier
tell [(identifier, tp)]
_ -> pure () -- skip datatypes and typeclasses
Check example.hs for a longer example (it must be run from hint’s base directory).
Limitations
Importing a module from the current package is not supported. It might look like it works on one day and then segfault the next day. You have been warned.
To work around this limitation, move those modules to a separate package. Now the part of your code which calls hint and the code interpreted by hint can both import that module.
It is not possible to exchange a value whose type involves an implicit kind parameter. This includes type-level lists. To work around this limitation, define a newtype wrapper which wraps the type you want.
It is possible to run the interpreter inside a thread, but on GHC 8.8 and below, you can’t run two instances of the interpreter simultaneously.
GHC must be installed on the system on which the compiled executable is running. There is a workaround for this but it’s not trivial.
The packages used by the interpreted code must be installed in a package database, and hint needs to be told about that package database at runtime.
The most common use case for package databases is for the interpreted code to
have access to the same packages as the compiled code (but not compiled code
itself). The easiest way to accomplish this is via a
GHC environment file,
and the easiest way to generate a GHC environment file is
via cabal.
Compile your code using cabal build --write-ghc-environment-files=always
;
this will create a file named .ghc.environment.<something>
in the current
directory. At runtime, hint will look for that file in the current directory.
For more advanced use cases, you can use
unsafeRunInterpreterWithArgs
to pass arguments to the underlying ghc library, such as
-package-db
to specify a path to a package database, or
-package-env
to specify a path to a GHC environment file.
Changes
0.9.0.8
- Support GHC 9.8
0.9.0.7
- Support GHC 9.4
- Support GHC 9.6
- Improved documentation
0.9.0.6
- Fixes the 0.9.0.5 regression
- Small fix in documentation (thanks to Ed Behn)
0.9.0.5 (deprecated)
- Support GHC 9.2.1
- Deprecated because it breaks the common pattern of using ‘unsafeRunInterpreterWithArgs’ to load a custom package database.
0.9.0.4
- Support GHC 9.0.1
0.9.0.3
- Support GHC 8.10
- Drop support for GHC 8.4
- Hint can now be used concurrently from multiple threads on GHC 8.10+
0.9.0.2
- Support GHC 8.8
- Drop support for GHC 8.2
0.9.0.1
- Make tests pass with stack 2.1.1
0.9.0
- Support GHC 8.6
- Drop support for GHC 8.0
0.8.0
- Support GHC 8.4
- Drop support for GHC 7.8 and 7.10
- Add
runStmt
to execute statements in the IO monad and bind new names - Internal changes of temporary files for phantom modules
- The files are now called
M<nnn>.hs
instead of<nnn>
- Improved cleanup of phantom module source files
- ghc 8.4 only: phantom modules are put into a temporary directory
- The files are now called
- Add
typeChecksWithDetails
to obtain type-checking errors - Stop GHC from overwriting the Ctrl-C signal handler
- Add
SetImportsF
to allow finer imports control
0.7.0
- Support for GHC 8.2
- Support use in a dynamically-linked executable
- Add
normalizeType
, like ghci’s :kind! - Drop support for GHC 7.6
0.6.0
- Support for GHC 8.0
- Add
displayException
to InterpreterError
0.5.2
- Add
runInterpreter
variant that takes a GHC libdir at runtime - Add missing negated extensions to the
Extension
type - Do not throw GHC warnings as errors
0.5.1
- Expose
unsafeInterpret
inLanguage.Haskell.Interpreter.Unsafe
0.5.0
- Drop support for GHC 7.4
- Remove deprecated functions and modules:
glasgowExtensions
setUseLanguageExtensions
setInstalledModsAreInScopeQualified
Language.Haskell.Interpreter.GHC
Language.Haskell.Interpreter.GHC.Unsafe
- Drop dependencies on
ghc-mtl
andextensible-exceptions
0.4.3
- New maintainer and source code repo
0.4.2.3
- It builds against 7.4.2 (not 7.4.1), so update the constraints.
0.4.2.2
- Builds with GHC 7.10
- Builds again with GHC 7.4
- Drops dependency on
utf8-string
0.4.2.1
- Better error reporting (thanks to Corentin Dupont)
0.4.2.0
- Based on exceptions-0.6
0.4.1.0
- Based on exceptions-0.4
0.4.0.0
- Compiles with GHC 7.8
- Fixed an issue where
P
was available as a qualified version of Prelude (thanks to Samuel Gélineau) - Uses
exceptions
package instead ofMonadCatchIO-mtl
(API breakage expected) - No longer depends on
haskell-src
- Changelog should now appear in Hackage
- Integrated unit tests with cabal
0.3.3.7
- Fixed a race condition that would happen, for instance, when two process where run one next to the other, making them, on some platforms, to get the same random number seed (thanks to Mario Pastorelli and Samuel Gélineau)
- Small fix in documentation (thanks to Daniil Frumin)
0.3.3.6
- Works again on GHC 7.2.x (thanks to Björn Peemöller)
0.3.3.5
- Works on GHC 7.4.6
- Cleans up files for phantom modules that were left behind (thanks to Beltram Felgenhauer)
0.3.3.4
- Works on GHC 7.4.1
0.3.3.3
- Works on GHC 7.2.1
0.3.3.2
- Supports GHC 7
0.3.3.1
- Instance declaration for Applicative (InterpreterT m) works with mtl-2 (requires Applicative m, this shouldn’t break anything…)
0.3.3.0
- Add unsafeRunInterpreterWithArgs
- Check that only one instance of the interpreter is run at any time
0.3.2.3
- Can be built against MonadCatchIO-mtl-0.3.x.x
0.3.2.2
- Fixed a bug that would make expressions using heavy use of the layout rule to fail to be interpreted (see parens)
0.3.2.1
- hint.cabal includes version bounds for package ghc-mtl. This is to avoid the accidental selection of the completely unrelated ghc-mtl internal to GHC and, apparently, installed in the hackage server
0.3.2.0
- Exports functions parens and isInterpretedModule
- Experimental support for module annotations
- Uses extensible-exceptions in order to provide a uniform interface across different ghc versions
- Provides an Applicative instance for IntepreterT
- Adds an option to configurate the searchPath
0.3.1.0
- No longer uses Language.Haskell.Extension due to configuration problems with Cabal. Instead, it uses its own Language.Haskell.Interpreter.Extension module.
0.3.0.0
- Updated API:
- InterpreterT monad transformer (Interpreter = InterpreterT IO)
- No more Sessions, use runInterpreter only once
- New options handling functions
- but observe that there is no setOptimizations equivalent; since GHC does no optimization on interpreted code, this was actually doing nothing
- Works with GHC 6.10 and 6.8 (untested with 6.6)
0.2.5
- setImportsQ added (modules can be imported both qualified and unqualified)
0.2.4.1
- BUGFIX: No longer fails on expressions ending in a – comment
0.2.4
- setInstalledModsAreInScopeQualified added
- Now depends on ghc-paths (no longer needs a custom cabal script)
0.2.2
- setOptimizations added
- Module Language.Haskell.Interpreter.GHC.Unsafe added (contains unsafeSetGhcOption)
- Unit tests now based on HUnit
0.2.1
- BUGFIX: Module reloading was broken under 6.8
- GHC.GhcExceptions are catched and turned into InterpreterErrors
0.2.0.1
- Adds the requirement cabal-version < 1.3
0.2
- Works also with GHC 6.8 and 6.6
- Added the getModuleExports function
- withSession function throws a dynamic exception instead of returning Either Error a
- Requires Cabal 1.2.x