我如何处理我的类型的用户插件? [英] How can I Handle user plugins in my types?

查看:87
本文介绍了我如何处理我的类型的用户插件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Haskell中编写了一个模块化和可扩展的文本编辑器,我想以这种方式实现插件:插件的编写者提供了一个如下所示的单个函数:

  handleEvent ::(PluginState,EditorState) - >事件 - > (PluginState,EditorState)

当每个事件发生时,插件可以使用当前编辑器状态和自定义块它自己的状态来计算一个新的编辑状态和新的插件状态。当然,每个插件都会为插件状态设置不同的类型,所以我会陷入困境,不知道如何以通用的方式将其集成到我的系统中。



我该如何写这样的东西:

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

当PluginState不是具体的类型吗?



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



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

解决方案


当然,每个插件将为插件状态设置一个不同的类型,所以我一直在困扰我如何将它集成到我的系统中

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

  { - #LANGUAGE ExistentialQuantification# - } 

data Plugin = forall ps。插件{
currentState :: ps
,transition :: ps - > EditorState - >事件 - > (ps,EditorState)
}

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

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

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

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

(我从 折叠 类型 >包,它以类似的方式使用存在。)



您现在可以拥有一个插件列表:

插件:: [插件] 
插件= [charPlugin,intPlugin]

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

  data Plugin = forall ps。显示ps =>插件{
currentState :: ps
,transition :: ps - > EditorState - >事件 - > (ps,EditorState)
}






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



另外,我们可以考虑明确地参数化 Plugin 它接受的事件类型,比如

  data插件e = ... 

在这种情况下,Plugin可以成为 Contravariant ,也许 Divisible 也是如此。



在编辑器状态下狂放并参数化

  data插件es e = ... 

那么也许我们可以找到一种方法来放大一个给定的插件工作在一个比它所定义的更普遍的状态。


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!

解决方案

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.

这篇关于我如何处理我的类型的用户插件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆