【问题标题】:How can I Handle user plugins in my types?如何处理我的类型中的用户插件?
【发布时间】:2017-04-03 13:30:27
【问题描述】:

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

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

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

我怎么能写出这样模糊的东西:

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

当 PluginState 不是具体类型时?

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

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

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

【问题讨论】:

  • 这方面的出发点是查看 GHC 如何处理插件以及 Yi 如何处理配置。我认为两者都采用部分重新编译方法......
  • 听起来像是hackage.haskell.org/package/vault的案例

标签: haskell types typeclass records


【解决方案1】:

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

也许您可以使用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 = ...

在这种情况下,Plugin 可以作为 Contravariant 的实例,也可能是 Divisible 的实例。

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

data Plugin es e = ...

那么也许我们可以找到一种方法来“缩放”给定插件,使其在比定义它的那个更一般的状态下工作。

【讨论】:

  • 应用时使用起来有点尴尬(我必须解压缩转换函数,应用它,然后重新打包状态和转换函数备份到要返回的新插件中),但放在一边从那里它就像一个魅力!谢谢!
  • @Chris Penner 不过,打包解包可以由handleEvent 之类的单个函数处理。此外,Plugin 类型应该有严格的字段以避免空间泄漏。
猜你喜欢
  • 2010-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多