简体   繁体   中英

Equivalent of python eval in Haskell

There is function in python called eval that takes string input and evaluates it.

>>> x = 1
>>> print eval('x+1')
2
>>> print eval('12 + 32')
44
>>>  

What is Haskell equivalent of eval function?

It is true that in Haskell, as in Java or C++ or similar languages, you can call out to the compiler, then dynamically load the code and execute it. However, this is generally heavy weight and almost never why people use eval() in other languages.

People tend to use eval() in a language because given that language's facilities, for certain classes of problem, it is easier to construct a string from the program input that resembles the language itself, rather than parse and evaluate the input directly.

For example, if you want to allow users to enter not just numbers in an input field, but simple arithmetic expressions, in Perl or Python it is going to be much easier to just call eval() on the input than writing a parser for the expression language you want to allow. Unfortunately, this kind of approach, almost always results in a poor user experience overall (compiler error messages weren't meant for non-programmers) and opens security holes. Solving these problems without using eval() generally involves a fair bit of code.

In Haskell, thanks to things like Parsec , it is actually very easy to write a parser and evaluator for these kinds of input problems, and considerably removes the yearning for eval .

It doesn't have an inbuilt eval function. However are some packages on hackage that can do the same sort of thing . ( docs ). Thanks to @luqui there is also hint .

There is no 'eval' built into the language, though Template Haskell allows compile time evaluation.

For runtime 'eval' -- ie runtime metaprogramming -- there are a number of packages on Hackage that essentially import GHC or GHCi, including the old hs-plugins package, and the hint package.

没有等效的eval,Haskell是一种静态编译的语言,与C或C ++相同,也没有eval。

This question is asked 11 years ago, and now we have new choice: use the package hint we can define eval very easily , following is an example as self-contained script (you still need nix to run it)

#!/usr/bin/env nix-shell
#! nix-shell -p "haskellPackages.ghcWithPackages (p: with p; [hint])"
#! nix-shell -i "ghci -ignore-dot-ghci -fdefer-type-errors -XTypeApplications"

{-# LANGUAGE ScopedTypeVariables, TypeApplications, PartialTypeSignatures #-}

import Data.Typeable (Typeable)
import qualified Language.Haskell.Interpreter as Hint

-- DOC: https://www.stackage.org/lts-18.18/package/hint-0.9.0.4

eval :: forall t. Typeable t => String -> IO t
eval s = do
    mr <- Hint.runInterpreter $ do
        Hint.setImports ["Prelude"]
        Hint.interpret s (Hint.as :: t)
    case mr of
        Left err -> error (show err)
        Right r -> pure r

-- * Interpret expressions into values:

e1 = eval @Int "1 + 1 :: Int"
e2 = eval @String "\"hello eval\""

-- * Send values from your compiled program to your interpreted program by interpreting a function:

e3 = do
    f <- eval @(Int -> [Int]) "\\x -> [1..x]"
    pure (f 5)

This answer shows a minimal example of using the hint package, but it lacks couple of things:

  1. How to evaluate using bindings like let x = 1 in x + 1 .
  2. How to handle exceptions, specifically divide by zero.

Here is a more complete example:

import qualified Control.DeepSeq as DS
import Control.Exception (ArithException (..))
import qualified Control.Exception as Ex
import qualified Control.Monad as M
import qualified Data.Either as E
import qualified Language.Haskell.Interpreter as I

evalExpr :: String -> [(String, Integer)] -> IO (Maybe Integer)
evalExpr expr a = Ex.handle handler $ do
  i <- I.runInterpreter $ do
    I.setImports ["Prelude"]
    -- let var = value works too
    let stmts = map (\(var, val) -> var ++ " <- return " ++ show val) a
    M.forM_ stmts $ \s -> do
      I.runStmt s

    I.interpret expr (I.as :: Integer)

  -- without force, exception is not caught
  (Ex.evaluate . DS.force) (E.either (const Nothing) Just i)
  where
    handler :: ArithException -> IO (Maybe Integer)
    handler DivideByZero = return Nothing
    handler ex = error $ show ex

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM