Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I compile a haskell function from a string at runtime (using plugins)?

I have an application where, for various reasons, I need to run arbitrary, user supplied code. (SafeHaskell makes this nice and secure). I've looked at the plugins package, which is really nice for loading from a .hi file on disc.

However, for my program design, it would be ideal if I could store these user programs in a database, then directly compile them to functions which I can use in my program.

So, if the function I'm compiling has the following type:

someFunction :: MyIn -> MyOut

I'm looking to write some function that will generate that function from a string:

hotCompile :: String -> IO (MyIn -> MyOut)

where string contains the haskell code code for "someFunction".

Does anybody know if there's a way to do this, preferably using the plugins package? I have come across the GHC API a little bit, but I don't know much about it and how it would relate to this.

Note that I've tried hint, but it is unsuitable for my application because it is not threadsafe.

like image 416
jmite Avatar asked Dec 14 '13 04:12

jmite


1 Answers

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)
like image 168
luochen1990 Avatar answered Oct 18 '22 18:10

luochen1990