问题
Here's a more specific variant of this question: Mutate only focus of Store Comonad?, for the benefit of not asking more than one question at once.
Are there any lenses compatible with Control.Lens which allow me to interact with the focus of a comonad (the value from extract
) or with the index/value of the Store Comonad (pos
)?
It seems like lenses may be of some use here, but I have been unable to find anything which fits; any help would be appreciated, thanks!
回答1:
Comonad
doesn't give you any way to write back to the comonad's focus, so you can't write a general Lens
for extract
. But it is very easy to turn any old function into a Getter:
extracted :: Comonad w => Getter (w a) a
extracted = to extract
Of course many comonads do allow you to write into the focus - Store
, as you mentioned, but also (including but not limited to) Env
, Traced
and Identity
.
-- 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)
Given ComonadWritable
it's easy to construct a Lens
for a comonad's focus.
focus :: ComonadWritable w => Lens' (w a) a
focus = lens extract (\w x -> write (const x) w)
One note on efficiency: StoreT
and TracedT
's write
implementations build a chain of functions with equality checks on the way down, so extract
is O(n) in the number of write
calls. Since you mentioned you're using a Representable
comonad w
, you could implement some clever strategy of batching up edits and reifying them into an actual w
every so often. Or you could store edits in a Map
(strengthening the Eq
constraint to Ord
) and delegate to the underlying w
when it turns out an element hasn't been edited. I'll leave that part to you.
来源:https://stackoverflow.com/questions/49585262/mutate-only-focus-of-store-comonad