对可能存在的列表元素

时间:2016-12-10 00:15:23

标签: haskell traversal lens

今天"使用镜头进行功能编程的冒险"我们的英雄试图在列表元素上定义一个可能存在或不存在的棱镜。

解释起来有点棘手,所以为了避免X,Y问题,我会给出真正的用例。

我在Haskell中编写了一个名为Rasa的文本编辑器,整个想法是它具有极高的可扩展性,这意味着大多数功能都是作为扩展编写的。由于它是一个核心原则,扩展还依赖于其他扩展,因此我需要一种集中存储其状态的方法,以便所有依赖于扩展的扩展可以访问其当前的扩展状态'。当然,编辑器的核心并不知道这些状态的类型,因此目前我存储了Dynamic值的列表。当扩展存储它转换为动态的状态时,可以稍后通过像这样的棱镜提取它:

data Store = Store
  { _event :: [Event]
  , _editor :: E.Editor
  , _extState :: [Dynamic]
  }

ext :: Typeable a => Traversal' Store a
ext = extState.traverse._Dynamic

所以现在ext是一个多态遍历,它基本上只对匹配'有问题的类型(如果您设置为它,它将替换相同类型的值,如果您从中获取,则它将作为Dynamics的遍历,与出站类型相匹配)值)。如果这看起来很神奇,那基本上就是......

顺便说一下,我很乐意随时在列表中准确提供一份给定Extension状态对象的副本。

所以获取和设置实际上工作正常IFF已经是列表中正确类型的值,我的问题是我如何制作此Traversal的版本,以便如果值为正确的类型在列表中,它将替换它(并按预期工作),但如果遍历为SET并且列表中没有匹配元素,则会向列表添加值。我知道Traversal不应该改变他们正在遍历的元素数量,所以这可能需要在列表本身上成为一个棱镜或镜头?

我知道这真的令人困惑,所以请提出澄清问题:)

至于我已经看过的内容,我正在考虑prefixed_Cons这样做的可行方法,但我不太确定把它连接起来。也许我完全咆哮着错误的树。

我可能会以某种方式在遍历的末尾添加一个默认值,但我不想要Monoid或Default,所以我无法从中冒出一些元素(我只想做它设置时。

我也开始讨论这是否真的是存储这种状态的正确方法,但它是迄今为止我发现的最优雅的解决方案(尽管我知道)运行时的类型转换是次优的)。我已经查看了Vault类型,但是当我尝试它时,传递密钥并没有真正起作用(我想它有类似的类型转换性能问题)。

干杯!谢谢你的阅读。

1 个答案:

答案 0 :(得分:0)

我认为扩展名列表不适合您。我会在_extState :: Map TypeRep Ext添加data Ext = forall a. Ext a之类的内容。然后我会加镜头:

ext :: forall a . Typeable a => Lens' Store (Maybe a)
ext = _extState . at (typeRep (Proxy :: Proxy a)) . mapping coerce
  where
    coerce = iso (\(Ext x) -> unsafeCoerce x) Ext

此镜头与at相似。因此,您只需获取/设置扩展程序即可。

但是有一个限制,所有扩展必须是不同的类型。