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

时间:2016-11-19 22:02:24

标签: haskell types typeclass records

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

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

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

我怎么能像这样模糊地写一些东西:

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

当PluginState不是具体类型时?

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

谢谢!我真的被困在这个:/

如果您需要任何澄清,请询问!

1 个答案:

答案 0 :(得分:2)

  

当然,每个插件的插件状态都会有不同的类型,所以我不知道如何以一般方式将它集成到我的系统中。

也许您可以使用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) 
    }

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

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

data Plugin e = ...

在这种情况下,插件可以成为ContravariantDivisible的实例。

如果我们在编辑状态下疯狂并参数化

data Plugin es e = ...
那么也许我们可以找到一种方法来缩放"一个给定的插件工作在一个比它定义的更普遍的状态。