License | BSD-style |
---|---|
Maintainer | mvdan@mvdan.cc |
Stability | experimental |
Portability | non-portable (GHC API) |
Safe Haskell | None |
Language | Haskell98 |
A Haskell interpreter built on top of the GHC API
Synopsis
- class (MonadIO m, MonadMask m) => MonadInterpreter m where
- fromSession :: FromSession m a
- modifySessionRef :: ModifySessionRef m a
- runGhc :: RunGhc m a
- data InterpreterT m a
- type Interpreter = InterpreterT IO
- runInterpreter :: (MonadIO m, MonadMask m) => InterpreterT m a -> m (Either InterpreterError a)
- data Option m a
- data OptionVal m = forall a. (Option m a) := a
- get :: MonadInterpreter m => Option m a -> m a
- set :: MonadInterpreter m => [OptionVal m] -> m ()
- languageExtensions :: MonadInterpreter m => Option m [Extension]
- availableExtensions :: [Extension]
- data Extension
- = OverlappingInstances
- | UndecidableInstances
- | IncoherentInstances
- | DoRec
- | RecursiveDo
- | ParallelListComp
- | MultiParamTypeClasses
- | MonomorphismRestriction
- | FunctionalDependencies
- | Rank2Types
- | RankNTypes
- | PolymorphicComponents
- | ExistentialQuantification
- | ScopedTypeVariables
- | PatternSignatures
- | ImplicitParams
- | FlexibleContexts
- | FlexibleInstances
- | EmptyDataDecls
- | CPP
- | KindSignatures
- | BangPatterns
- | TypeSynonymInstances
- | TemplateHaskell
- | ForeignFunctionInterface
- | Arrows
- | Generics
- | ImplicitPrelude
- | NamedFieldPuns
- | PatternGuards
- | GeneralizedNewtypeDeriving
- | ExtensibleRecords
- | RestrictedTypeSynonyms
- | HereDocuments
- | MagicHash
- | TypeFamilies
- | StandaloneDeriving
- | UnicodeSyntax
- | UnliftedFFITypes
- | InterruptibleFFI
- | CApiFFI
- | LiberalTypeSynonyms
- | TypeOperators
- | RecordWildCards
- | RecordPuns
- | DisambiguateRecordFields
- | TraditionalRecordSyntax
- | OverloadedStrings
- | GADTs
- | GADTSyntax
- | MonoPatBinds
- | RelaxedPolyRec
- | ExtendedDefaultRules
- | UnboxedTuples
- | DeriveDataTypeable
- | DeriveGeneric
- | DefaultSignatures
- | InstanceSigs
- | ConstrainedClassMethods
- | PackageImports
- | ImpredicativeTypes
- | NewQualifiedOperators
- | PostfixOperators
- | QuasiQuotes
- | TransformListComp
- | MonadComprehensions
- | ViewPatterns
- | XmlSyntax
- | RegularPatterns
- | TupleSections
- | GHCForeignImportPrim
- | NPlusKPatterns
- | DoAndIfThenElse
- | MultiWayIf
- | LambdaCase
- | RebindableSyntax
- | ExplicitForAll
- | DatatypeContexts
- | MonoLocalBinds
- | DeriveFunctor
- | DeriveTraversable
- | DeriveFoldable
- | NondecreasingIndentation
- | SafeImports
- | Safe
- | Trustworthy
- | Unsafe
- | ConstraintKinds
- | PolyKinds
- | DataKinds
- | ParallelArrays
- | RoleAnnotations
- | OverloadedLists
- | EmptyCase
- | AutoDeriveTypeable
- | NegativeLiterals
- | BinaryLiterals
- | NumDecimals
- | NullaryTypeClasses
- | ExplicitNamespaces
- | AllowAmbiguousTypes
- | JavaScriptFFI
- | PatternSynonyms
- | PartialTypeSignatures
- | NamedWildCards
- | DeriveAnyClass
- | DeriveLift
- | StaticPointers
- | StrictData
- | Strict
- | ApplicativeDo
- | DuplicateRecordFields
- | TypeApplications
- | TypeInType
- | UndecidableSuperClasses
- | MonadFailDesugaring
- | TemplateHaskellQuotes
- | OverloadedLabels
- | TypeFamilyDependencies
- | NoOverlappingInstances
- | NoUndecidableInstances
- | NoIncoherentInstances
- | NoDoRec
- | NoRecursiveDo
- | NoParallelListComp
- | NoMultiParamTypeClasses
- | NoMonomorphismRestriction
- | NoFunctionalDependencies
- | NoRank2Types
- | NoRankNTypes
- | NoPolymorphicComponents
- | NoExistentialQuantification
- | NoScopedTypeVariables
- | NoPatternSignatures
- | NoImplicitParams
- | NoFlexibleContexts
- | NoFlexibleInstances
- | NoEmptyDataDecls
- | NoCPP
- | NoKindSignatures
- | NoBangPatterns
- | NoTypeSynonymInstances
- | NoTemplateHaskell
- | NoForeignFunctionInterface
- | NoArrows
- | NoGenerics
- | NoImplicitPrelude
- | NoNamedFieldPuns
- | NoPatternGuards
- | NoGeneralizedNewtypeDeriving
- | NoExtensibleRecords
- | NoRestrictedTypeSynonyms
- | NoHereDocuments
- | NoMagicHash
- | NoTypeFamilies
- | NoStandaloneDeriving
- | NoUnicodeSyntax
- | NoUnliftedFFITypes
- | NoInterruptibleFFI
- | NoCApiFFI
- | NoLiberalTypeSynonyms
- | NoTypeOperators
- | NoRecordWildCards
- | NoRecordPuns
- | NoDisambiguateRecordFields
- | NoTraditionalRecordSyntax
- | NoOverloadedStrings
- | NoGADTs
- | NoGADTSyntax
- | NoMonoPatBinds
- | NoRelaxedPolyRec
- | NoExtendedDefaultRules
- | NoUnboxedTuples
- | NoDeriveDataTypeable
- | NoDeriveGeneric
- | NoDefaultSignatures
- | NoInstanceSigs
- | NoConstrainedClassMethods
- | NoPackageImports
- | NoImpredicativeTypes
- | NoNewQualifiedOperators
- | NoPostfixOperators
- | NoQuasiQuotes
- | NoTransformListComp
- | NoMonadComprehensions
- | NoViewPatterns
- | NoXmlSyntax
- | NoRegularPatterns
- | NoTupleSections
- | NoGHCForeignImportPrim
- | NoNPlusKPatterns
- | NoDoAndIfThenElse
- | NoMultiWayIf
- | NoLambdaCase
- | NoRebindableSyntax
- | NoExplicitForAll
- | NoDatatypeContexts
- | NoMonoLocalBinds
- | NoDeriveFunctor
- | NoDeriveTraversable
- | NoDeriveFoldable
- | NoNondecreasingIndentation
- | NoSafeImports
- | NoSafe
- | NoTrustworthy
- | NoUnsafe
- | NoConstraintKinds
- | NoPolyKinds
- | NoDataKinds
- | NoParallelArrays
- | NoRoleAnnotations
- | NoOverloadedLists
- | NoEmptyCase
- | NoAutoDeriveTypeable
- | NoNegativeLiterals
- | NoBinaryLiterals
- | NoNumDecimals
- | NoNullaryTypeClasses
- | NoExplicitNamespaces
- | NoAllowAmbiguousTypes
- | NoJavaScriptFFI
- | NoPatternSynonyms
- | NoPartialTypeSignatures
- | NoNamedWildCards
- | NoDeriveAnyClass
- | NoDeriveLift
- | NoStaticPointers
- | NoStrictData
- | NoStrict
- | NoApplicativeDo
- | NoDuplicateRecordFields
- | NoTypeApplications
- | NoTypeInType
- | NoUndecidableSuperClasses
- | NoMonadFailDesugaring
- | NoTemplateHaskellQuotes
- | NoOverloadedLabels
- | NoTypeFamilyDependencies
- | UnknownExtension String
- installedModulesInScope :: MonadInterpreter m => Option m Bool
- searchPath :: MonadInterpreter m => Option m [FilePath]
- type ModuleName = String
- isModuleInterpreted :: MonadInterpreter m => ModuleName -> m Bool
- data ModuleImport = ModuleImport {}
- data ModuleQualification
- data ImportList
- = NoImportList
- | ImportList [String]
- | HidingList [String]
- loadModules :: MonadInterpreter m => [String] -> m ()
- getLoadedModules :: MonadInterpreter m => m [ModuleName]
- setTopLevelModules :: MonadInterpreter m => [ModuleName] -> m ()
- setImports :: MonadInterpreter m => [ModuleName] -> m ()
- setImportsQ :: MonadInterpreter m => [(ModuleName, Maybe String)] -> m ()
- setImportsF :: MonadInterpreter m => [ModuleImport] -> m ()
- reset :: MonadInterpreter m => m ()
- data ModuleElem
- type Id = String
- name :: ModuleElem -> Id
- children :: ModuleElem -> [Id]
- getModuleExports :: MonadInterpreter m => ModuleName -> m [ModuleElem]
- getModuleAnnotations :: (Data a, MonadInterpreter m) => a -> String -> m [a]
- getValAnnotations :: (Data a, MonadInterpreter m) => a -> String -> m [a]
- typeChecksWithDetails :: MonadInterpreter m => String -> m (Either [GhcError] String)
- typeOf :: MonadInterpreter m => String -> m String
- typeChecks :: MonadInterpreter m => String -> m Bool
- kindOf :: MonadInterpreter m => String -> m String
- normalizeType :: MonadInterpreter m => String -> m String
- interpret :: (MonadInterpreter m, Typeable a) => String -> a -> m a
- as :: Typeable a => a
- infer :: Typeable a => a
- eval :: MonadInterpreter m => String -> m String
- runStmt :: MonadInterpreter m => String -> m ()
- data InterpreterError
- newtype GhcError = GhcError {}
- data MultipleInstancesNotAllowed = MultipleInstancesNotAllowed
- ghcVersion :: Int
- parens :: String -> String
- module Control.Monad.Trans
The interpreter monad transformer
class (MonadIO m, MonadMask m) => MonadInterpreter m where Source #
fromSession :: FromSession m a Source #
modifySessionRef :: ModifySessionRef m a Source #
Instances
(MonadIO m, MonadMask m, Functor m) => MonadInterpreter (InterpreterT m) Source # | |
Defined in Hint.InterpreterT fromSession :: FromSession (InterpreterT m) a Source # modifySessionRef :: ModifySessionRef (InterpreterT m) a Source # runGhc :: RunGhc (InterpreterT m) a Source # |
data InterpreterT m a Source #
Instances
type Interpreter = InterpreterT IO Source #
Running the interpreter
runInterpreter :: (MonadIO m, MonadMask m) => InterpreterT m a -> m (Either InterpreterError a) Source #
Executes the interpreter. Returns Left InterpreterError
in case of error.
NB. In hint-0.7.0 and earlier, the underlying ghc was accidentally overwriting certain signal handlers (SIGINT, SIGHUP, SIGTERM, SIGQUIT on Posix systems, Ctrl-C handler on Windows).
Interpreter options
Available options are:
get :: MonadInterpreter m => Option m a -> m a Source #
Retrieves the value of an option.
set :: MonadInterpreter m => [OptionVal m] -> m () Source #
Use this function to set or modify the value of any option. It is invoked like this:
set [opt1 := val1, opt2 := val2,... optk := valk]
languageExtensions :: MonadInterpreter m => Option m [Extension] Source #
Language extensions in use by the interpreter.
Default is: []
(i.e. none, pure Haskell 98)
availableExtensions :: [Extension] Source #
List of the extensions known by the interpreter.
This represents language extensions beyond Haskell 98
that are supported by GHC (it was taken from
Cabal's Language.Haskell.Extension
)
installedModulesInScope :: MonadInterpreter m => Option m Bool Source #
When set to True
, every module in every available package is implicitly
imported qualified. This is very convenient for interactive
evaluation, but can be a problem in sandboxed environments
(e.g. unsafePerformIO
is in scope).
Default value is True
.
Observe that due to limitations in the GHC-API, when set to False
, the
private symbols in interpreted modules will not be in scope.
searchPath :: MonadInterpreter m => Option m [FilePath] Source #
The search path for source files. Observe that every time it is set,
it overrides the previous search path. The default is ["."]
.
Keep in mind that by a limitation in ghc, "."
is always in scope.
Context handling
type ModuleName = String Source #
Module names are _not_ filepaths.
isModuleInterpreted :: MonadInterpreter m => ModuleName -> m Bool Source #
Returns True if the module was interpreted.
data ModuleImport Source #
Represent module import statement.
See setImportsF
Instances
Show ModuleImport Source # | |
Defined in Hint.Base showsPrec :: Int -> ModuleImport -> ShowS # show :: ModuleImport -> String # showList :: [ModuleImport] -> ShowS # |
data ModuleQualification Source #
Instances
Eq ModuleQualification Source # | |
Defined in Hint.Base (==) :: ModuleQualification -> ModuleQualification -> Bool # (/=) :: ModuleQualification -> ModuleQualification -> Bool # | |
Show ModuleQualification Source # | |
Defined in Hint.Base showsPrec :: Int -> ModuleQualification -> ShowS # show :: ModuleQualification -> String # showList :: [ModuleQualification] -> ShowS # |
data ImportList Source #
Instances
Eq ImportList Source # | |
Defined in Hint.Base (==) :: ImportList -> ImportList -> Bool # (/=) :: ImportList -> ImportList -> Bool # | |
Show ImportList Source # | |
Defined in Hint.Base showsPrec :: Int -> ImportList -> ShowS # show :: ImportList -> String # showList :: [ImportList] -> ShowS # |
loadModules :: MonadInterpreter m => [String] -> m () Source #
Tries to load all the requested modules from their source file. Modules my be indicated by their ModuleName (e.g. "My.Module") or by the full path to its source file.
The interpreter is reset
both before loading the modules and in the event
of an error.
IMPORTANT: Like in a ghci session, this will also load (and interpret) any dependency that is not available via an installed package. Make sure that you are not loading any module that is also being used to compile your application. In particular, you need to avoid modules that define types that will later occur in an expression that you will want to interpret.
The problem in doing this is that those types will have two incompatible representations at runtime: 1) the one in the compiled code and 2) the one in the interpreted code. When interpreting such an expression (bringing it to program-code) you will likely get a segmentation fault, since the latter representation will be used where the program assumes the former.
The rule of thumb is: never make the interpreter run on the directory with the source code of your program! If you want your interpreted code to use some type that is defined in your program, then put the defining module on a library and make your program depend on that package.
getLoadedModules :: MonadInterpreter m => m [ModuleName] Source #
Returns the list of modules loaded with loadModules
.
setTopLevelModules :: MonadInterpreter m => [ModuleName] -> m () Source #
Sets the modules whose context is used during evaluation. All bindings of these modules are in scope, not only those exported.
Modules must be interpreted to use this function.
setImports :: MonadInterpreter m => [ModuleName] -> m () Source #
Sets the modules whose exports must be in context.
Warning: setImports
, setImportsQ
, and setImportsF
are mutually exclusive.
If you have a list of modules to be used qualified and another list
unqualified, then you need to do something like
setImportsQ ((zip unqualified $ repeat Nothing) ++ qualifieds)
setImportsQ :: MonadInterpreter m => [(ModuleName, Maybe String)] -> m () Source #
setImportsF :: MonadInterpreter m => [ModuleImport] -> m () Source #
reset :: MonadInterpreter m => m () Source #
All imported modules are cleared from the context, and
loaded modules are unloaded. It is similar to a :load
in
GHCi, but observe that not even the Prelude will be in
context after a reset.
Module querying
data ModuleElem Source #
Instances
Eq ModuleElem Source # | |
Defined in Hint.Reflection (==) :: ModuleElem -> ModuleElem -> Bool # (/=) :: ModuleElem -> ModuleElem -> Bool # | |
Read ModuleElem Source # | |
Defined in Hint.Reflection readsPrec :: Int -> ReadS ModuleElem # readList :: ReadS [ModuleElem] # readPrec :: ReadPrec ModuleElem # readListPrec :: ReadPrec [ModuleElem] # | |
Show ModuleElem Source # | |
Defined in Hint.Reflection showsPrec :: Int -> ModuleElem -> ShowS # show :: ModuleElem -> String # showList :: [ModuleElem] -> ShowS # |
name :: ModuleElem -> Id Source #
children :: ModuleElem -> [Id] Source #
getModuleExports :: MonadInterpreter m => ModuleName -> m [ModuleElem] Source #
Gets an abstract representation of all the entities exported by the module.
It is similar to the :browse
command in GHCi.
Annotations
getModuleAnnotations :: (Data a, MonadInterpreter m) => a -> String -> m [a] Source #
getValAnnotations :: (Data a, MonadInterpreter m) => a -> String -> m [a] Source #
Type inference
typeChecksWithDetails :: MonadInterpreter m => String -> m (Either [GhcError] String) Source #
Similar to typeChecks
, but gives more information, e.g. the type errors.
typeOf :: MonadInterpreter m => String -> m String Source #
Returns a string representation of the type of the expression.
typeChecks :: MonadInterpreter m => String -> m Bool Source #
Tests if the expression type checks.
NB. Be careful if there is `-fdefer-type-errors` involved.
Perhaps unsurprisingly, that can falsely make typeChecks
and getType
return True
and Right _
respectively.
kindOf :: MonadInterpreter m => String -> m String Source #
Returns a string representation of the kind of the type expression.
normalizeType :: MonadInterpreter m => String -> m String Source #
Returns a string representation of the normalized type expression.
This is what the :kind!
GHCi command prints after =
.
Evaluation
interpret :: (MonadInterpreter m, Typeable a) => String -> a -> m a Source #
Evaluates an expression, given a witness for its monomorphic type.
as :: Typeable a => a Source #
Convenience functions to be used with interpret
to provide witnesses.
Example:
interpret "head [True,False]" (as :: Bool)
interpret "head $ map show [True,False]" infer >>= flip interpret (as :: Bool)
infer :: Typeable a => a Source #
Convenience functions to be used with interpret
to provide witnesses.
Example:
interpret "head [True,False]" (as :: Bool)
interpret "head $ map show [True,False]" infer >>= flip interpret (as :: Bool)
eval :: MonadInterpreter m => String -> m String Source #
eval expr
will evaluate show expr
.
It will succeed only if expr
has type t and there is a Show
instance for t.
runStmt :: MonadInterpreter m => String -> m () Source #
Evaluate a statement in the IO
monad, possibly binding new names.
Example:
runStmt "x <- return 42" runStmt "print x"
Error handling
data InterpreterError Source #
UnknownError String | |
WontCompile [GhcError] | |
NotAllowed String | |
GhcException String | GhcExceptions from the underlying GHC API are caught and rethrown as this. |
Instances
Show InterpreterError Source # | |
Defined in Hint.Base showsPrec :: Int -> InterpreterError -> ShowS # show :: InterpreterError -> String # showList :: [InterpreterError] -> ShowS # | |
Exception InterpreterError Source # | |
Defined in Hint.Base |
data MultipleInstancesNotAllowed Source #
The installed version of ghc is not thread-safe. This exception
is thrown whenever you try to execute runInterpreter
while another
instance is already running.
Instances
Miscellaneous
ghcVersion :: Int Source #
Version of the underlying ghc api. Values are:
804
for GHC 8.4.x806
for GHC 8.6.x- etc...
parens :: String -> String Source #
Conceptually, parens s = "(" ++ s ++ ")"
, where s is any valid haskell
expression. In practice, it is harder than this.
Observe that if s
ends with a trailing comment, then parens s
would
be a malformed expression. The straightforward solution for this is to
put the closing parenthesis in a different line. However, now we are
messing with the layout rules and we don't know where s
is going to
be used!
Solution: parens s = "(let {foo =n" ++ s ++ "\n ;} in foo)"
where foo
does not occur in s
module Control.Monad.Trans