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

1

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.

Calliope answered 14/12, 2013 at 4:14 Comment(6)
Why not store the .hi files in a database? At worst you could base64 encode it and store it as a string that way.Southing
You're looking for the hint package. I'll find time to give an answer if someone else doesn't by tomorrow.Canova
I'd tried hint, but it was giving me random segfaults, due to thread-safety issues I think.Calliope
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.Canova
Please edit your answer adding the information about hint (and any other solutions you have tried)Outfield
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.Souvaine
M
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)
Mycosis answered 27/11, 2021 at 12:28 Comment(2)
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 :)Calliope
Looks like it's thread safe now, so this is the correct answer!Calliope

© 2022 - 2024 — McMap. All rights reserved.