8

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.

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
jmite
  • 8,171
  • 6
  • 40
  • 81
  • 1
    Why not store the `.hi` files in a database? At worst you could base64 encode it and store it as a string that way. – bheklilr Dec 14 '13 at 04:40
  • 2
    You're looking for the `hint` package. I'll find time to give an answer if someone else doesn't by tomorrow. – Thomas M. DuBuisson Dec 14 '13 at 04:47
  • I'd tried hint, but it was giving me random segfaults, due to thread-safety issues I think. – jmite Dec 14 '13 at 04:56
  • Well that's a different problem that shouldn't occur and warrants investigation. Since you say you've already tried hint, I won't bother re-explaining it, but that is a solid tool and should solve your problem. – Thomas M. DuBuisson Dec 14 '13 at 05:16
  • 3
    Please edit your answer adding the information about `hint` (and any other solutions you have tried) – RasmusWL Dec 14 '13 at 11:03
  • 2
    `hint` isn't thread-safe, despite the claims to the contrary in its documentation - and that's entirely because the ghc api it relies on isn't thread-safe. – Carl Dec 15 '13 at 01:15

1 Answers1

2

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)
luochen1990
  • 3,689
  • 1
  • 22
  • 37
  • 1
    In my original: "Note that I've tried hint, but it is unsuitable for my application because it is not threadsafe." That said, this was 8 years ago, so I have no idea whether Hint is currently threadsafe :) – jmite Nov 27 '21 at 19:13
  • 1
    Looks like it's thread safe now, so this is the correct answer! – jmite Nov 27 '21 at 19:16