Last updated: 2019-06-28 15:35:39 +0100
Upstream URL: git clone http://chriswarbo.net/git/nix-eval.git
Contents of README.md follows
This Haskell package is a crude implementation of eval
,
as found in dynamic languages like Lisp, Python, Javascript, etc. It
lets us construct Haskell code programatically (either at run time, or
in Template Haskell), and attempt to evaluate it.
What sets this package apart from other eval
implementations, is the ability to control which packages and modules
are available during evaluation. This is achieved by calling out to the
Nix package manager.
Expr
is the type of expressions, which contains a list
of package names, a list of modules to import, a list of compiler flags,
a list of String
s to put in the generated module and a
String
of Haskell code to evaluate. All of these are just
String
s internally, but we use wrappers to prevent
accidentally using packages as modules, etc.
A few combinators are provided for common manipulations, for example
qualified "Foo" "bar"
will produce the expression
"Foo.bar"
with "Foo"
in its module list. The
OverloadedStrings
extension allows packages, modules, flags
and expressions to be written as literals. Note that literal expressions
are given an empty context; you will have to specify any required
modules, packages, etc. separately.
When evaluated, the Haskell code is prefixed by an import of each
module, the “preamble” strings (if any) and wrapped in
main = putStr (..)
. This code is piped into
runhaskell
. If any flags are specified, they are appended
as arguments to the runhaskell
command.
The runhaskell
process itself is invoked via the
nix-shell
command, which provides all of the required
packages via the ghcWithPackages
mechanism of nixpkgs.
Packages are taken from nixpkgs’s haskellPackages
set by
default, which can be overridden by setting the
NIX_EVAL_HASKELL_PKGS
environment variable to the path of a
Nix file. Note that the package names used in your Haskell code should
correspond to the keys in this package set, which might differ from
those used on Hackage.
If the process exits successfully, its stdout will be returned
wrapped in Just
; otherwise Nothing
is
returned. If you wish to alter the main
implementation, use
Language.Eval.Internal.eval'
This implementation is a little rough; for example, you may prefer to
use Text
rather than String
; use a better
representation like the syntax trees from TemplateHaskell or
haskell-src-exts
instead; or accumulate packages and
modules monadically.
The intention of this library is to provide a simple, minimal base to
support such design choices, and String
is the lowest
common denominator. You’re welcome, and encouraged, to build more
sophisticated APIs; as long as you can pretty-print to a
String
, they should work out of the box.
This is also why we return the contents of stdout, rather than trying to parse it into a more appropriate type: it’s not our place to choose how the result should be parsed, so we avoid the problem; by that point, our job is done.
putStr
, so the expression
must be a String
. You may need to marshall your data into a
form which is more amenable to serialising/deserialising via
String
.eval
has a very high latency, so it’s much more
efficient to eval
one big collection of values than it is
to eval
each individually.x
and y
evaluate successfully doesn’t mean that some
combination of them will. Obviously an ill-typed combination will fail,
but other reasons include:
eval
, there is absolutely no
security. Do not pass potentially-malicious user input to this library!
Not only can arbitrary Haskell code be run (eg. using
unsafePerformIO
, but the flags are also a shell injection
vector.