我已经阅读过Haskell中的插件,但我无法满足自己的目的(理想情况下是在生产环境中使用)。
我的插件系统目标是:
一个最小的例子可能是:
app / service~plugins接口
module SharedTypes (PluginInterface (..)) where
data PluginInterface =
PluginInterface { pluginName :: String
, runPlugin :: Int -> Int }
某些插件列表
-- ~/plugins/plugin{Nth}.?? (with N=1..)
module Plugin{Nth}( getPlugin ) where
import SharedTypes
getPlugin :: PluginInterface
getPlugin = PluginInterface "{Nth}th plugin" $ \x -> {Nth} * x
应用/服务
...
loadPlugins :: FilePath -> IO [PluginInterface]
loadPlugins = undefined
...
我认为使用动态编译链接库(将每个Plugin{Nth}
编译为共享库)可以工作(作为FFI),但
getPlugin
功能点)谢谢!
更新
按照伟大的@xnyhps回答,使用ghc 7.10
SharedTypes.hs
module SharedTypes (
PluginInterface (..)
) where
data PluginInterface =
PluginInterface { pluginName :: String
, runPlugin :: Int -> Int
}
Plugin1.hs
module Plugin1 (
getPlugin
) where
import SharedTypes
getPlugin :: PluginInterface
getPlugin = PluginInterface "Plugin1" $ \x -> 1 * x
Plugin2.hs
module Plugin2 (
getPlugin
) where
import SharedTypes
getPlugin :: PluginInterface
getPlugin = PluginInterface "Plugin2" $ \x -> 2 * x
app.hs
import SharedTypes
import System.Plugins.DynamicLoader
import System.Directory
import Data.Maybe
import Control.Applicative
import Data.List
import System.FilePath
import Control.Monad
loadPlugins :: FilePath -> IO [PluginInterface]
loadPlugins path = getDirectoryContents path >>= mapM loadPlugin . filter (".plugin" `isSuffixOf`)
where loadPlugin file = do
m <- loadModuleFromPath (combine path file) -- absolute path
(Just path) -- base of qualified name (or you'll get not found)
resolveFunctions
getPlugin <- loadFunction m "getPlugin"
return getPlugin
main = do
-- and others used by plugins
addDLL "/usr/lib/ghc-7.10.1/base_I5BErHzyOm07EBNpKBEeUv/libHSbase-4.8.0.0-I5BErHzyOm07EBNpKBEeUv-ghc7.10.1.so"
loadModuleFromPath "/srv/despierto/home/josejuan/Projects/Solveet/PluginSystem/SharedTypes.o" Nothing
plugins <- loadPlugins "/srv/despierto/home/josejuan/Projects/Solveet/PluginSystem/plugins"
forM_ plugins $ \plugin -> do
putStrLn $ "Plugin name: " ++ pluginName plugin
putStrLn $ " Run := " ++ show (runPlugin plugin 34)
编制和执行
[josejuan@centella PluginSystem]$ ghc --make -dynamic -fPIC -O3 Plugin1.hs
[1 of 2] Compiling SharedTypes ( SharedTypes.hs, SharedTypes.o )
[2 of 2] Compiling Plugin1 ( Plugin1.hs, Plugin1.o )
[josejuan@centella PluginSystem]$ ghc --make -dynamic -fPIC -O3 Plugin2.hs
[2 of 2] Compiling Plugin2 ( Plugin2.hs, Plugin2.o )
[josejuan@centella PluginSystem]$ mv Plugin1.o plugins/Plugin1.plugin
[josejuan@centella PluginSystem]$ mv Plugin2.o plugins/Plugin2.plugin
[josejuan@centella PluginSystem]$ ghc --make -dynamic -fPIC -O3 app.hs
[2 of 2] Compiling Main ( app.hs, app.o )
Linking app ...
[josejuan@centella PluginSystem]$ ./app
Plugin name: Plugin1
Run := 34
Plugin name: Plugin2
Run := 68
答案 0 :(得分:5)
有the dynamic-loader package,允许您将额外的对象文件或共享库加载到您的进程中。 (Hackage上的版本不适用于7.10,但是当前version on GitHub does。)
有了这个,你可以这样做:
import System.Plugins.DynamicLoader
import System.Directory
loadPlugins :: FilePath -> IO [PluginInterface]
loadPlugins path = do
files <- getDirectoryContents path
mapM (\plugin_path -> do
m <- loadModuleFromPath (path ++ "/" ++ plugin_path) (Just path)
resolveFunctions
plugin <- loadFunction m "getPlugin"
return plugin) files
但是,您必须记住整个过程非常不安全:如果您更改了PluginInterface
数据类型并尝试加载使用旧版本编译的插件,应用程序将崩溃。你必须希望getPlugin
函数有类型PluginInterface
,没有检查。最后,如果插件来自不受信任的源,它可以执行任何操作,即使您尝试调用的函数在Haskel中应该是纯粹的。