Я читал о плагинах в Haskell, но я не могу получить удовлетворительный способ для своих целей (в идеале для использования в производственной среде).Из системы плагина Haskell
Мои цели системы плагинов являются:
- производственная среда должна быть из коробки (все скомпилированных).
- для загрузки плагинов включен сброс приложения/службы, но в идеале он будет загружать и обновлять плагины на лету.
Один минимальный пример может быть:
приложение/сервис ~ плагинов интерфейс
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
App/услуги
...
loadPlugins :: FilePath -> IO [PluginInterface]
loadPlugins = undefined
...
Я думаю, что с помощью динамической компиляции подключаемой библиотеки (сводной каждый Plugin{Nth}
в общей библиотеке) МОГЛА произведение (как FFI), но
- Как перечислить и загружать каждую общую библиотеку во время выполнения? (получить каждый
getPlugin
функция точка) - Существует какой-то лучший способ? (Например, некоторые «магия» процесс до запуска приложения/службы)
Спасибо!
UPDATE
Полный ход пример
После большого @xnyhps ответ, полный работает пример использования ghc 7.10
SharedTypes.hs
module SharedTypes (
PluginInterface (..)
) where
data PluginInterface =
PluginInterface { pluginName :: String
, runPlugin :: Int -> Int
}
Plugin1. вс
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
приложение.вс
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)
Составление и исполнение
[[email protected] 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)
[[email protected] PluginSystem]$ ghc --make -dynamic -fPIC -O3 Plugin2.hs
[2 of 2] Compiling Plugin2 (Plugin2.hs, Plugin2.o)
[[email protected] PluginSystem]$ mv Plugin1.o plugins/Plugin1.plugin
[[email protected] PluginSystem]$ mv Plugin2.o plugins/Plugin2.plugin
[[email protected] PluginSystem]$ ghc --make -dynamic -fPIC -O3 app.hs
[2 of 2] Compiling Main (app.hs, app.o)
Linking app ...
[[email protected] PluginSystem]$ ./app
Plugin name: Plugin1
Run := 34
Plugin name: Plugin2
Run := 68
Спасибо! Я думаю, это прекрасно! * «Тем не менее» * да, конечно, для обеспечения совместимости ('getVersion') и ретросопротивления можно было бы сделать много дополнительных вещей (' getPlugin :: Maybe (a -> a) ',' getPluginV2 :: Maybe (a -> a - > a) и т. д.). – josejuan