如何处理我的类型中的用户插件?

How can I Handle user plugins in my types?

我正在 Haskell 中编写一个模块化和可扩展的文本编辑器,我想以这种方式实现插件:插件的作者提供了一个看起来像这样的函数:

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

随着每个事件的发生,插件可以使用当前的编辑器状态和它自己的状态的自定义块来计算新的编辑器状态和新的插件状态。当然,每个插件的插件状态都会有不同的类型,所以我一直在思考如何以一般方式将其集成到我的系统中。

我怎么能这样写得含糊不清:

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

当 PluginState 不是具体类型时?

TLDR;如何以可访问的方式存储具有非具体类型的值映射,而无需将每个插件的状态类型烘焙到我的全局状态中?我可以在添加新插件时重新编译编辑器。

谢谢!我真的坚持这个:/

如果您需要任何说明,请直接提问!

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.

也许你可以使用 existential type 来隐藏插件状态,比如

{-# 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')

现在每个插件都是同一类型,但不同的插件值可以有不同类型的内部状态:

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

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

(我从 foldl 包中的 Fold 类型中获得灵感,它以类似的方式使用存在主义。)

您现在可以获得插件列表:

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

设计的一个可能演变是将内部状态限制为某些类型类的实例:

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

我怀疑可以为 Plugin 类型定义一个 Monoid 实例。

此外,我们可以考虑在它接受的事件类型上显式参数化 Plugin,例如

data Plugin e = ...

在那种情况下,插件也可以成为 Contravariant and perhaps Divisible 的一个实例。

如果我们疯狂地对编辑器状态进行参数化

data Plugin es e = ...

那么也许我们可以找到一种方法让给定的插件 "zoom" 在比其定义的状态更一般的状态下工作。