解释起来有点棘手,所以为了避免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的遍历,与出站类型相匹配)值)。如果这看起来很神奇,那基本上就是......
所以获取和设置实际上工作正常IFF已经是列表中正确类型的值,我的问题是我如何制作此Traversal的版本,以便如果值为正确的类型在列表中,它将替换它(并按预期工作),但如果遍历为SET并且列表中没有匹配元素,则会向列表添加值。我知道Traversal不应该改变他们正在遍历的元素数量,所以这可能需要在列表本身上成为一个棱镜或镜头?
我知道这真的令人困惑,所以请提出澄清问题:)
至于我已经看过的内容,我正在考虑prefixed和_Cons这样做的可行方法,但我不太确定把它连接起来。也许我完全咆哮着错误的树。
我可能会以某种方式在遍历的末尾添加一个默认值,但我不想要Monoid或Default,所以我无法从中冒出一些元素(我只想做它设置时。
我也开始讨论这是否真的是存储这种状态的正确方法,但它是迄今为止我发现的最优雅的解决方案(尽管我知道)运行时的类型转换是次优的)。我已经查看了Vault类型,但是当我尝试它时,传递密钥并没有真正起作用(我想它有类似的类型转换性能问题)。
干杯!谢谢你的阅读。
答案 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
相似。因此,您只需获取/设置扩展程序即可。
但是有一个限制,所有扩展必须是不同的类型。