
alexanderforemny:
My general idea is to have the main application listening to the network socket and then calling all the plugins on each incoming message. Therefore I maintain a list of plugin states in the main application's state and on each incoming message I call a function which modifies the plugin's state.
Like lambdabot!
There's a PluginClass class which contains definitions of functions for each plugin which they all share. Simplyfied it's like this:
type PL = StateT PluginConfig
class PluginClass a where identifier :: a -> String rawMessage :: (MonadIO m) => a -> Message -> PL m ()
Like lambdabot, kind of.
So plugins can be identified uniquely using the identifier function and they can respond to messages using the rawMessage function. This function is executed in the PL monad, which is essentially a StateT monad for updating the plugin's state trough put and maybe accessing a few data fields from the underlying Bot monad in which the main application is operating.
Then again I want to be able to query a plugin's state from a different plugin. For instance I'll have a plugin which keeps track of the channels the bot has joined collecting user information, the topic, etc. Another plugin could then query the "chan info" plugin and get all the users in a certain channel through a queryPlugin function which takes a plugin and looks that plugin up in the main application's plugin state list for the right state and then calls a function on it. The plugin and the corresponding functions would be exported by the plugin's module.
queryPlugin :: (PluginClass a) => a -> (a -> b) -> PL m b queryPlugin pl f = do plugins <- getGlobalPlugins -- ideally (PluginClass a) => [a] let pluginNames = map identifier plugins targetName = identifier pl [(_, target)] = filter ((==) targetName . fst) (zip pluginNames plugin) return (f target)
But here I am facing either one or the other problem, depending on the "solution."
1) I somehow have to store all the plugin states in the main application. Since plugins are essentially their states, they are quite arbitrary. I either cannot use a list for that or I have to use existential types which would make that possible.
Existential types are used in lambdabot for this. I'd probably use an associated type to connect the plugin to its state type now, too.
2) Using an existential plugin type would restrict the functions I am able to call on the plugin to those which are supported by the PluginClass. This would render queryPlugin unusable since the functions a plugin exports for query the state are arbitrary.
You might be able to design around this. Lambdabot manages ok with existentially typed interfaces. -- Don