I need to have something like
-- Main.hs
module Main where
main :: IO ()
main = do
<import Plugin>
print Plugin.computation
With a Plugin like
-- Plugin.hs
module Plugin where
computation :: Int
computation = 4
However, I need the plugin to be compiled alongside the main application. They need to be deployed together. Only the import (not the compilation) of the module should happen dynamically.
I found Dynamically loading compiled Haskell module - GHC 7.6 along the way and it works just fine with GHC 8.0.2 except for the fact that it requires the source file of the plugin to be in the current working directory when executing the application.
Edit (07.12.2017)
Is it possible to load a module from a String instead of a file using the GHC API? http://hackage.haskell.org/package/ghc-8.2.1/docs/GHC.html#t:Target suggests that it's possible, but the documentation has many holes and I can't find a way to actually do this. If this can be accomplished, I can use file-embed to include the plugin source file into the compiled binary.
Example:
module Main where
-- Dynamic loading of modules
import GHC
import GHC.Paths ( libdir )
import DynFlags
import Unsafe.Coerce
import Data.Time.Clock (getCurrentTime)
import StringBuffer
pluginModuleNameStr :: String
pluginModuleNameStr = "MyPlugin"
pluginSourceStr :: String
pluginSourceStr = unlines
[ "module MyPlugin where"
, "computation :: Int"
, "computation = 4"
]
pluginModuleName :: ModuleName
pluginModuleName = mkModuleName pluginModuleNameStr
pluginSource :: StringBuffer
pluginSource = stringToStringBuffer pluginSourceStr
main :: IO ()
main = do
currentTime <- getCurrentTime
defaultErrorHandler defaultFatalMessager defaultFlushOut $ do
result <- runGhc (Just libdir) $ do
dflags <- getSessionDynFlags
setSessionDynFlags dflags
let target = Target { targetId = TargetModule $ pluginModuleName
, targetAllowObjCode = True
, targetContents = Just ( pluginSource
, currentTime
)
}
setTargets [target]
r <- load LoadAllTargets
case r of
Failed -> error "Compilation failed"
Succeeded -> do
setContext [IIDecl $ simpleImportDecl pluginModuleName]
result <- compileExpr ("MyPlugin.computation")
let result' = unsafeCoerce result :: Int
return result'
print result
This, however, results in
<command-line>: panic! (the 'impossible' happened)
(GHC version 8.0.2 for x86_64-apple-darwin):
module ‘MyPlugin’ is a package module
Edit (08.12.2017)
I can compile the "plugin" directly into the final binary by writing the source to a temp file and then loading it like in the linked post (Dynamically loading compiled Haskell module - GHC 7.6). However, this does not play well if the plugin imports packages from Hackage:
module Main where
import Control.Monad.IO.Class (liftIO)
import DynFlags
import GHC
import GHC.Paths (libdir)
import System.Directory (getTemporaryDirectory, removePathForcibly)
import Unsafe.Coerce (unsafeCoerce)
pluginModuleNameStr :: String
pluginModuleNameStr = "MyPlugin"
pluginSourceStr :: String
pluginSourceStr = unlines
[ "module MyPlugin where"
, "import Data.Aeson"
, "computation :: Int"
, "computation = 4"
]
writeTempFile :: IO FilePath
writeTempFile = do
dir <- getTemporaryDirectory
let file = dir ++ "/" ++ pluginModuleNameStr ++ ".hs"
writeFile file pluginSourceStr
return file
main :: IO ()
main = do
moduleFile <- writeTempFile
defaultErrorHandler defaultFatalMessager defaultFlushOut $ do
result <- runGhc (Just libdir) $ do
dflags <- getSessionDynFlags
setSessionDynFlags dflags
target <- guessTarget moduleFile Nothing
setTargets [target]
r <- load LoadAllTargets
liftIO $ removePathForcibly moduleFile
case r of
Failed -> error "Compilation failed"
Succeeded -> do
setContext [IIDecl $ simpleImportDecl $ mkModuleName pluginModuleNameStr]
result <- compileExpr "MyPlugin.computation"
let result' = unsafeCoerce result :: Int
return result'
print result
Is there a way to load packages when, for instance, MyPlugin contains the statement import Data.Aeson? If I add it to the plugin string, it fails with
/var/folders/t2/hp9y8x6s6rs7zg21hdzvhbf40000gn/T/MyPlugin.hs:2:1: error:
Failed to load interface for ‘Data.Aeson’
Perhaps you meant Data.Version (from base-4.9.1.0)
Use -v to see a list of the files searched for.
haskell-loader-exe: panic! (the 'impossible' happened)
(GHC version 8.0.2 for x86_64-apple-darwin):
Compilation failed
CallStack (from HasCallStack):
error, called at app/Main.hs:40:19 in main:Main
The reason for my request is database support: We use Persistent to access a database and the dynamic import is needed to support multiple databases (MySQL, PostgreSQL and SQLite) while still allowing the end user to only install one of the three database servers (with other words: not requiring the user to install all of them if they only use, for instance, PostgreSQL). The module that is database-specific should only be loaded when the user actually configures the main application to use that module.
If I don't import Database.Persist.MySQL, then the application does not require MySQL to be installed. Otherwise, the application fails with, for instance,
dyld: Library not loaded:
/usr/local/opt/mysql/lib/libmysqlclient.20.dylib
on macOS.
A file with a matching module name must exist by the looks of it - it doesn't seem to matter what the file's content is.
On Linux I can even make it be a symlink to /dev/null and things work but a symlink to itself doesn't.
I am currently working on a project that requires parsing command line arguments. So far I have been following this tutorial, which has been incredibly helpful, but I can't figure out how to return a variable (--author=example) in the arguments. I also can't figure out why parse [] = getContents causes an error (I had to uncomment it).
Here is my code:
module Main where
import qualified System.Environment as SE
import qualified System.Exit as E
import qualified Lib as Lib
main = do
args <- SE.getArgs
rem <- parse args
Lib.someFunc
putStrLn rem
putStrLn "Hello"
tac = unlines . reverse . lines
parse ["--help"] = usage >> exit
parse ["--version"] = version >> exit
parse ["--author=xyz"] = return "xyz"
-- parse ["--author=?"] = ?
{-
this is the code I am trying to figure out... how do I get parse the passed in variable name?
-}
-- parse [] = getContents
{-
the above line generates this error when I run 'main' in GHCi:
*Main> <stdin>: hIsEOF: illegal operation (handle is semi-closed)
Process intero exited abnormally with code 1
-}
parse fs = concat `fmap` mapM readFile fs
usage = putStrLn "Usage: gc2"
version = putStrLn "gc2 -- git-cal in Haskell2010 - 0.1"
exit = E.exitWith E.ExitSuccess
die = E.exitWith (E.ExitFailure 1)
To follow up on #ThomasM.DuBuisson comment optparse-applicative is a great package for cli and arguments parsing. There is also another package optparse-simple that is built on top of the previous one and has a few helpers that simplify things a bit.
Just so you can get started with optparse-applicative here is an implementation of your example:
data Options = Options
{ author :: String
}
main :: IO ()
main = do
let ver = "gc2 -- git-cal in Haskell2010 - 0.1"
args <-
execParser $
info
(Options <$>
strOption (long "author" <>
short 'a' <>
help "Name of the author.") <*
infoOption ver (long "version" <>
short 'v' <>
help "Display version and exit.") <*
abortOption ShowHelpText (long "help" <>
short 'h' <>
help "Display this message."))
(progDesc "Very powerful tool." <> fullDesc)
putStrLn $ author args
And usage example from GHCi:
λ> :main
Missing: (-a|--author ARG)
Usage: <interactive> (-a|--author ARG) [-v|--version] [-h|--help]
Very powerful tool.
*** Exception: ExitFailure 1
λ> :main --version
gc2 -- git-cal in Haskell2010 - 0.1
*** Exception: ExitSuccess
λ> :main --help
Usage: <interactive> (-a|--author ARG) [-v|--version] [-h|--help]
Very powerful tool.
Available options:
-a,--author ARG Name of the author.
-v,--version Display version and exit.
-h,--help Display this message.
*** Exception: ExitSuccess
λ> :main --author Me
Me
I have a program that works. However, I wasn't exactly sure why it was compiling.
Here's my main function
module Main where
import System.Environment
import Cover5
main :: IO ()
main = do
args <- getArgs
let numGames = if null args then 13 else read $ head args
putStrLn $ "Making picks for " ++ show numGames ++ " games."
print $ run numGames
where the function run has the signature run :: Int -> RVar [(Int, Char)].
What is confusing is that there is no instance of Show for RVar [(Int,Char)] so I figure it shouldn't compile, but it does (see Travis-CI build and the related source for that commit of Main.hs). I can force a warning with this command:
cabal build --ghc-options="-fforce-recomp -fno-code"
Preprocessing executable 'cover5' for cover5-0.1.0.1...
[1 of 2] Compiling Cover5 ( src/Cover5.hs, nothing )
[2 of 2] Compiling Main ( src/Main.hs, nothing )
src/Main.hs:13:10: error:
• No instance for (Show (RVar [(Int, Char)]))
arising from a use of ‘print’
• In a stmt of a 'do' block: print $ run numGames
In the expression:
do { args <- getArgs;
let numGames = ...;
putStrLn $ "Making picks for " ++ show numGames ++ " games.";
print $ run numGames }
In an equation for ‘main’:
main
= do { args <- getArgs;
let numGames = ...;
putStrLn $ "Making picks for " ++ show numGames ++ " games.";
.... }
I'd like to "fix" this and have followed guidance from the question Convert Data.RVar.RVar [Char] to [Char]
So I add the appropriate imports for StdRandom and runRVar and write the following lines
results <- runRVar (run numGames) StdRandom
putStrLn $ show results
But I'm tripping over myself trying to find instances of MonadRandom IO for my usage:
src/Main.hs:13:21: error:
• No instance for (MonadRandom IO) arising from a use of ‘runRVar’
• In a stmt of a 'do' block:
results <- runRVar (run numGames) StdRandom
In the expression:
do { args <- getArgs;
let numGames = ...;
putStrLn $ "Making picks for " ++ show numGames ++ " games.";
results <- runRVar (run numGames) StdRandom;
.... }
In an equation for ‘main’:
main
= do { args <- getArgs;
let numGames = ...;
putStrLn $ "Making picks for " ++ show numGames ++ " games.";
.... }
So two questions:
Why does the original version compile and work?
What might you suggest I do to remove the warning and write this "correctly"?
The answer(s) were provided by #leftaroundabout in the comments. Posting an answer here for others new to Random in case it proves helpful
First Question
I had written the original program about 3 years ago. I had no idea how to print results from RVars. I chose to use Data.Random.Show.Unsafe. Apparently this module provides an instance of Show for RVar a. I didn't remember that.
In addition, I was implicitly exporting everything from my Cover5 module so the main module whose code is above was importing that instance.
So that's why print was working.
Second Question
Instances are available in Data.Random, which I wasn't importing.
The reason why I was getting errors but was thinking they were warnings is because I changed 2 (actually more than 2) things at once:
I restricted what was exported from Cover5 module so the Unsafe
instance of Show wasn't exported anymore
I ran the build with some arguments which I thought were just going to show more warnings than a traditional build.
I am working on http client in haskell (that's my first "non exersize" project).
There is an api which returns json with all text using unicode, something like
\u041e\u043d\u0430 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u0442 \u0432\u0430\u0441 \u0432 \u0434\u043b\u0438\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a
I want to decode this json to utf-8, to print some data from json message.
I searched for existing libraries, but find Nothing for this purpose.
So I wrote function to convert data (I am using lazy bytestrings because I got data with this type from wreq lib)
ununicode :: BL.ByteString -> BL.ByteString
ununicode s = replace s where
replace :: BL.ByteString -> BL.ByteString
replace str = case (Map.lookup (BL.take 6 str) table) of
(Just x) -> BL.append x (replace $ BL.drop 6 str)
(Nothing) -> BL.cons (BL.head str) (replace $ BL.tail str)
table = Map.fromList $ zip letters rus
rus = ["Ё", "ё", "А", "Б", "В", "Г", "Д", "Е", "Ж", "З", "И", "Й", "К", "Л", "М",
"Н", "О", "П", "Р", "С", "Т", "У", "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Ъ", "Ы",
"Ь", "Э", "Ю", "Я", "а", "б", "в", "г", "д", "е", "ж", "з", "и", "й", "к",
"л", "м", "н", "о", "п", "р", "с", "т", "у", "ф", "х", "ц", "ч", "ш", "щ",
"ъ", "ы", "ь", "э", "ю", "я"]
letters = ["\\u0401", "\\u0451", "\\u0410", "\\u0411", "\\u0412", "\\u0413",
"\\u0414", "\\u0415", "\\u0416", "\\u0417", "\\u0418", "\\u0419",
"\\u041a", "\\u041b", "\\u041c", "\\u041d", "\\u041e", "\\u041f",
"\\u0420", "\\u0421", "\\u0422", "\\u0423", "\\u0424", "\\u0425",
"\\u0426", "\\u0427", "\\u0428", "\\u0429", "\\u042a", "\\u042b",
"\\u042c", "\\u042d", "\\u042e", "\\u042f", "\\u0430", "\\u0431",
"\\u0432", "\\u0433", "\\u0434", "\\u0435", "\\u0436", "\\u0437",
"\\u0438", "\\u0439", "\\u043a", "\\u043b", "\\u043c", "\\u043d",
"\\u043e", "\\u043f", "\\u0440", "\\u0441", "\\u0442", "\\u0443",
"\\u0444", "\\u0445", "\\u0446", "\\u0447", "\\u0448", "\\u0449",
"\\u044a", "\\u044b", "\\u044c", "\\u044d", "\\u044e", "\\u044f"]
But it doesn't work as I expected. It replaces text, but instead of cyrrilic letters I got something like
345 ?C1;8:C5< 8=B5#2LN A #4=52=8:>2F0<8 8=B5#5A=KE ?#>D5AA89 8 E>118
The second problem that I can't debug my function.
When I try just call it with custom string I got error Data.ByteString.Lazy.head: empty ByteString
I gave no idea about reason why it's empty.
It work's fine during normal program execution:
umailGet env params = do
r <- apiGet env (("method", "umail.get"):params)
x <- return $ case r of
(Right a) -> a
(Left a) -> ""
return $ ununicode $ x
and than in Main
r2 <- umailGet client []
print $ r2
And the last problem is that all api can return any unicode symbol, so this solution is bad by design.
Of course function implementation seems to be bad to, so after solving the main problem, I am going to rewrite it using foldr.
UPDATED:
It seems like I had desribed problem not enough clear.
So I am sending request via wreq lib, and get a json answer. For example
{"result":"12","error":"\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u0435\u0441\u0441\u0438\u0438"}
That's not the result of haskell representetion of result, thare are real ascii symbols. I got the same text using curl or firefox. 190 bytes/190 ascii symbols.
Using this site for example http://unicode.online-toolz.com/tools/text-unicode-entities-convertor.php I can convert it to cyrrilic text {"result":"12","error":"Неверный идентификатор сессии"}
And I need to implement something like this service using haskell (or find a package where it had been already implemented), where response like this has type Lazy Bytestring.
I also tried to change types to use Text instead of ByteString (both Lazy and strict), changed first line to ununicode s = encodeUtf8 $ replace $ L.toStrict $ LE.decodeUtf8 s
And with that new implementation I am getting an error when executing my program
Data.Text.Internal.Fusion.Common.head: Empty stream. Sot it looks like I have error in my replacing function, maybe if I fix it, it also will fix the main problem.
I am not sure if you are falling in the "print unicode" trap (see here) - for en/decoding there already exists hackage: Data.Text.Encoding decodeUtf8 :: ByteString -> Text and encodeUtf8 :: Text -> ByteString should do the task.
Edit:
I have played around with text/bytestring for some time to reproduce your "\u1234" characters - well i couldn't
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Text (Text)
import qualified Data.Text.Encoding as E
import qualified Data.Text.IO as T
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as B
inputB :: ByteString
inputB = "ДЕЖЗИЙКЛМНОПРСТУФ"
inputT :: Text
inputT = "ДЕЖЗИЙКЛМНОПРСТУФ"
main :: IO ()
main = do putStr "T.putStrLn inputT: " ; T.putStrLn inputT
putStr "B.putStrLn inputB: " ; B.putStrLn inputB
putStr "print inputB: " ; print inputB
putStr "print inputT: " ; print inputT
putStr "B.putStrLn $ E.encodeUtf8 inputT: " ; B.putStrLn $ E.encodeUtf8 inputT
putStr "T.putStrLn $ E.decodeUtf8 inputB: " ; T.putStrLn $ E.decodeUtf8 inputB
putStr "print $ E.decodeUtf8 inputB: " ; print $ E.decodeUtf8 inputB
putStr "print $ E.encodeUtf8 inputT: " ; print $ E.encodeUtf8 inputT
here is the result of it:
T.putStrLn inputT: ДЕЖЗИЙКЛМНОПРСТУФ
B.putStrLn inputB:
rint inputB: "\DC4\NAK\SYN\ETB\CAN\EM\SUB\ESC\FS\GS\RS\US !\"#$"
print inputT: "\1044\1045\1046\1047\1048\1049\1050\1051\1052\1053\1054\1055\1056\1057\1058\1059\1060"
B.putStrLn $ E.encodeUtf8 inputT: ДЕЖЗИЙКЛМНОПРСТУФ
T.putStrLn $ E.decodeUtf8 inputB:
rint $ E.decodeUtf8 inputB: "\DC4\NAK\SYN\ETB\CAN\EM\SUB\ESC\FS\GS\RS\US !\"#$"
print $ E.encodeUtf8 inputT: "\208\148\208\149\208\150\208\151\208\152\208\153\208\154\208\155\208\156\208\157\208\158\208\159\208\160\208\161\208\162\208\163\208\164"
honestly I don't know why I get the "rint" lines after the bytestring printlines that yield no result.
I need to profile a large number of haskell executables, hopefully in parallel. I was able to get the clock time with measure and measTime from the Criterion library, but couldn't get measCpuTime or any GC report to work (measCpuTime returns a time that's impossibly short). The code looks like:
buildProj :: FilePath -> IO ExitCode
buildProj projDir = system $ "cd " ++ projDir ++ "; cabal sandbox init; cabal configure; cabal build"
-- Time a project
instance NFData ExitCode
where
rnf ExitSuccess = ()
rnf (ExitFailure _) = ()
benchmark :: FilePath -> Int64 -> IO Double
benchmark projDir runs = do
let runProj = "./" ++ projDir ++ "/dist/build/" ++ projDir ++ "/" ++ projDir ++ "> /dev/null"
exit <- timeout 17000000 $ system runProj -- TODO hardcode timeout
case exit of
Just ExitSuccess -> do {(m, _) <- measure (nfIO $ system runProj) runs;
return $! measTime m}
Just (ExitFailure _) -> return 100
Nothing -> return 100
In short, I'm running the executables with System.Process.system as an IO action and I've declared ExitCode as NFData in order to get nfIO to work. What have I done wrong? Are there better tools to do the task?
The file's here if you want to play with it.
I took a look at this SO question and got some ideas. First note that criterion uses cbits to enable system-dependent cpu time functions. Let's pretend you're on unix. The simplest thing to do is to directly read from /proc/PID/stat/cutime at the start and end of your runs and take the difference. Beyond that, you can actually use the c code provided in that question, link it in yourself as a foreign import, and then call that directly from your own code.