这是该问题的一个更具体的变体:Mutate only focus of Store Comonad?,其好处是不会一次提出多个问题。

是否有任何与Control.Lens兼容的镜头可以让我与同名对象的焦点(extract的值)或与Comonad商店的索引/值(pos)进行交互?

似乎镜头在这里可能有用,但我一直找不到合适的东西。任何帮助,将不胜感激,谢谢!

最佳答案

Comonad不能给您任何方式写回社区的焦点,因此您不能为Lens编写通用的extract。但是it is very easy to turn any old function into a Getter

extracted :: Comonad w => Getter (w a) a
extracted = to extract


当然,许多comonads确实允许您将焦点写入焦点-如您所提到的Store,但也可以(包括但不限于)EnvTracedIdentity

-- I suspect some of these laws are redundant:
--   write id = id
--   write f . write g = write (f . g)
--   extract . write f = f . extract
--   duplicate . write f = write (write f) . duplicate
--   write (const (extract w)) w = w
class Comonad w => ComonadWritable w where
    write :: (a -> a) -> w a -> w a

instance ComonadWritable Identity where
    write f (Identity x) = Identity (f x)

-- law-abiding "up to Eq"
instance (Eq s, ComonadWritable w) => ComonadWritable (StoreT s w) where
    write f (StoreT w s) = StoreT (write g w) s
        where
            g k s'
                | s' == s = f (k s')
                | otherwise = k s'

-- law-abiding "up to Eq"
instance (Eq m, Monoid m, ComonadWritable w) => ComonadWritable (TracedT m w) where
    write f (TracedT w) = TracedT (write g w)
        where
            g k m
                | m == mempty = f (k m)
                | otherwise = k m

instance ComonadWritable w => ComonadWritable (EnvT e w) where
    write f (EnvT e w) = EnvT e (write f w)


给定ComonadWritable,可以很容易地构造一个Lens以使其成为焦点。

focus :: ComonadWritable w => Lens' (w a) a
focus = lens extract (\w x -> write (const x) w)


关于效率的一个注释:StoreTTracedTwrite实现构建了一系列函数,并在向下进行相等检查,因此extract的调用次数为O(n)。既然您提到您使用的是write comonad Representable,则可以实施一些巧妙的策略来批量整理编辑并将它们经常化为实际的w。或者,您可以将编辑存储在w中(将Map约束加强为Eq),并在发现尚未编辑的元素时委托给基础Ord。我会把那部分留给你。

09-18 19:16