Reuben
Reuben

Reputation: 609

Language.Haskell.Interpreter: is this the right tool for the job at hand?

I have a little toy semantics for natural language, with words like:

ran :: String -> Bool
ran = (`elem` ["Bart", "Homer", "Marge"])

and:

bart :: String
bart = "Bart"

So for example, I can have (ran bart) :: Bool, and so on.

I want to write a parser which, for example takes the string "Bart ran" and returns True. I'd probably use Parsec for this.

However, the problem is being able to call functions via strings. E.g. getting from "ran" to the function ran. For this, I thought Language.Haskell.Interpreter's interpret function might be appropriate.

So my questions are:

  1. Is this a sensible way to do what I want to do?

  2. If so, why doesn't the following work, entered into GHCi, given a module called Grammar.hs in the same directory with ran defined as above:

    let a = runInterpreter $ do
              loadModules ["Grammar"]
              setImports ["Prelude"]
              interpret "ran" (as :: String -> Bool)
    let b = do
              x <- a
              return $ x <*> pure "John"
    b
    

I get the error:

"Left (WontCompile [GhcError {errMsg = "<interactive>:2:1:\n    Not in scope: \8216ran\8217\n    Perhaps you meant \8216tan\8217 (imported from Prelude)"}])"

which suggests that the import isn't working, and indeed, if I try something similar with a Prelude function, everything works.

  1. Why do I get the following type error (among many others) if I try to compile the same code as in Q2, (minus the let):

No instance for MonadIO m0 arising from a use of runInterpreter

Upvotes: 1

Views: 68

Answers (1)

Cactus
Cactus

Reputation: 27626

As for #2, you need to add "Grammar" to the setImports list as well:

runInterpreter $ do
    loadModules ["HintDefs"]
    setImports ["Prelude", "HintDefs"]
    interpret "ran" (as :: String -> Bool)

As for #3, it is because runInterpreter is monomorphic in the choice of monad to run it in:

runInterpreter :: (MonadIO m, MonadMask m) 
               => InterpreterT m a 
               -> m (Either InterpreterError a)

So you'll need to choose a particular m by running it in e.g. IO:

main :: IO ()
main = do
    ran <- runInterpreter $ do
        loadModules ["HintDefs"]
        setImports ["Prelude", "HintDefs"]
        interpret "ran" (as :: String -> Bool)
    print $ ran <*> pure "John"

Now, as for #1, I am not convinced you need something as stupidly powerful as HInt here. You could just maintain a dictionary of String -> Bool functions keyed by a String key, something simple like a Map String (String -> Bool), and then use that to look up ran etc.

Upvotes: 2

Related Questions