How can I Handle user plugins in my types?

被刻印的时光 ゝ 提交于 2019-11-30 19:35:57

问题


I'm writing a modular and extensible text editor in Haskell and I'd like to implement plugins in such a way: The writer of the plugin provides a single function that looks something like this:

handleEvent :: (PluginState, EditorState) -> Event -> (PluginState, EditorState)

As each event occurs the plugin can use the current editor state and customized chunk of its own state to calculate a new editor state and new plugin state. Of course each Plugin is going to have a different Type for the Plugin state, so I'm getting stuck on how I can integrate this into my system in a general way.

How can I write something vaguely like this:

type Plugin = (PluginState, EditorState) -> Event -> (PluginState, EditorState)
data MyEditor = MyEditor EditorState [Plugin] [PluginState]

When PluginState isn't a concrete type?

TLDR; How can I store a map of values with non-concrete types in an accessible way without baking in every plugin's state-type into my global state? I'm okay with re-compiling the editor when a new plugin is added.

Thanks! I'm really stuck on this one :/

If you need any clarification please just ask!


回答1:


Of course each Plugin is going to have a different Type for the Plugin state, so I'm getting stuck on how I can integrate this into my system in a general way.

Perhaps you could use an existential type to hide the plugin state, something like

{-# LANGUAGE ExistentialQuantification #-}

data Plugin = forall ps. Plugin { 
       currentState :: ps
    ,  transition :: ps -> EditorState -> Event -> (ps, EditorState) 
    }

handleEvent :: Plugin -> EditorState -> Event -> (Plugin,EditorState)
handleEvent (Plugin ps t) es e =
    let (ps',es') = t ps es e
    in  (Plugin ps' t,es')

Now each plugin is of the same type, and yet different plugin values can have internal states of different types:

charPlugin :: Plugin
charPlugin = Plugin 'a' (\ps es e -> (succ ps,es))

intPlugin :: Plugin
intPlugin = Plugin (1::Int) (\ps es e -> (succ ps,es))

(I took inspiration from the Fold type from the foldl package, which uses existentials in a similar way.)

You can now have a list of plugins:

plugins :: [Plugin]
plugins = [charPlugin,intPlugin]

A possible evolution of the design would be to constrain the internal states to be instances of some typeclass:

data Plugin = forall ps. Show ps => Plugin { 
       currentState :: ps
    ,  transition :: ps -> EditorState -> Event -> (ps, EditorState) 
    }

I suspect a Monoid instance could be defined for the Plugin type.

Also, we could think of explicitly parameterizing Plugin on the type of events it accepts, like

data Plugin e = ...

In that case Plugin could be made an instance of Contravariant and perhaps Divisible as well.

And if we go wild and parameterize on the editor state

data Plugin es e = ...

then perhaps we could find a way to "zoom" a given plugin to work in a more general state than the one for which it was defined.



来源:https://stackoverflow.com/questions/40698396/how-can-i-handle-user-plugins-in-my-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!