[Haskell-cafe] Writing an IRC bot, problems with plugins
Don Stewart
dons at galois.com
Wed Nov 5 15:26:23 EST 2008
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
More information about the Haskell-Cafe
mailing list