可能存在的列表元素的棱镜

Prism over maybe-existent list element

今天 "adventures in functional programming with lenses" 我们的主人公试图在可能存在或不存在的列表元素上定义棱镜。

解释起来有点棘手,所以为了避免 X、Y 问题,我将充分展示实际用例。

我正在 Haskell 中编写一个名为 Rasa, the whole idea is that it's extremely extensible, and that means most functionality is written as extensions. Since it's a core principle, extensions ALSO depend on other extensions, so I needed a way to store their state centrally such that all extensions depending on an extension could access its current 'extension state'. Of course the types of these states is not known to the core of the editor, so at the moment I'm storing a list of Dynamic 值的文本编辑器。当扩展存储它转换为 Dynamic 的状态时,稍后可以通过像这样的棱镜提取它:

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

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

所以现在 ext 是一个多态遍历,它基本上只对 'match' 相关类型的动态进行操作(如果你设置它,它将替换相同类型的值,如果你 'get' 从它,它充当与出站值类型匹配的 Dynamics 的遍历)。如果这看起来很神奇,那基本上就是...

顺便说一句,我希望在任何时候列表中都有给定扩展的状态对象的 1 个副本。

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

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

至于我已经看过的东西,我正在寻找 prefixed and _Cons 可能的方法来做到这一点,但我不太确定如何连接它。也许我完全找错了树。

我也许可以通过某种方式在遍历的末尾添加一个默认值,但我不想要求 Monoid 或 Default 所以我无法从任何地方召唤出元素(而且我只想在设置)。

我也乐于讨论这是否真的是存储此状态的正确方法,但这是我迄今为止找到的最优雅的解决方案(尽管我知道类型转换 运行 时间是次优的)。我研究了 Vault 类型,但是当我尝试它时传递密钥并没有真正起作用(我想它有类似的类型转换性能问题)。

干杯!感谢阅读。

我认为扩展列表不是适合您的解决方案。我会在 data Ext = forall a. Ext a 中添加类似 _extState :: Map TypeRep Ext 的内容。然后我会添加镜头:

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。所以你可以简单地 get/set 你的扩展。

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