Open mikesol opened 3 years ago
I don't think I've seen optics used in this way before, do you have any example usages? I'm wondering if they relate at all to https://hackage.haskell.org/package/lens-action
I don't think I've seen optics used in this way before, do you have any example usages?
The hackage package looks interesting, and the signatures seem similar.
The example comes from my current sketches for the next iteration of https://github.com/mikesol/purescript-audio-behaviors. In it, the previous audio graph g0
(ie the one 20 milliseconds before) is passed to a function that creates the next audio graph. This is done so that the parts of g0
that don't change aren't rebuilt, which greatly speeds up rendering.
g0
is a monad where the actual audio graph is stashed in the monad and the terms a user can get out of the monad (ie an individual sine wave) are just pointers to nodes in the graph. That means that, as we traverse down the graph to get an individual element (ie an individual sine wave oscillator), we are actually getting pointers. These pointers are uninteresting: they are only useful insofar as they can be used to manipulate or query the graph. But it is important that we can traverse down the graph to get the pointer to any node.
To do this, I wound up writing a bunch of functions that produced stuff like Lens (f s) (f t) (f a) (f b)
, where f
is the monad containing the graph and s t a b
are various pointers in the graph (ie s
and t
would be a loudspeaker and a
and b
would be a single sine wave in the graph). My first step was looking around pursuit to see if these functions already existed, but they do not, which leads me to believe that they're either (a) too specialized; (b) a bad idea; or (c) valuable. If it is (c), then perhaps others would use them as well, and as implementing them is a pain (it requires understand profunctor optics, which took me months), I thought it may be worth it to contribute them to this repo instead of making my own.
I see that the hackage package is separate from the main lens one, so maybe these functions are too exotic to be in the profunctor-lenses
repo, in which case I can make a new project and we can merge the functions here if/when they get more traction/mindshare. Let me know!
Another example from our code base at Meeshkan:
remove :: Int -> Script8Base -> Script8Base
remove sIndex script = widthAdjustedSIndex
where
items' = preview items script
withModifiedCount = set count (maybe 0 (add (-1) <<< A.length) items') script
withRemovedCommand = over items (\x -> fromMaybe x (A.deleteAt sIndex x)) withModifiedCount
widthAdjustedSIndex = over items (A.mapWithIndex (\i a -> a { sIndex = i })) withRemovedCommand
Here, the fromMaybe
is a hack if sIndex
is out of the boundaries of array x
. What it should do is bubble up an error message to the top of the lens. Monadic lenses would allow for this. The signature would be ie remove :: forall e. e -> Int -> Script8Base -> Either e Script8Base
.
Maybe a silly question, as I don't fully understand what you are doing, but
Maybe then you might find some solution in looking at creating optic using functions too? That is, could you create a lens using functions to achieve what you are wanting instead? In other words, come up with a profunctor to instantiate optics in a way to achieve what you are wanting.
For example, traverseOf
instantiates the profunctor p
to Star f
. That is p a b = Star f a b = a -> f b
. This is the classic monadic action term, and it means the optic is instantiated to
Optic p s t a b
= p a b -> p s t
= Star f a b -> Star s t
= (a -> f b) -> (s -> f t)
which is very much looking like the sort of thing you were thinking about when s = v a
and t = v b
for some container (vessel) v
= (a -> f b) -> (v a -> f (v b))
This is also the classic container traversal definition for some applicative f
.
So, if f
is your monad (which is applicative), and v a
is your container, this will, given a value to action mapping a -> f b
, return an action for a container of results f (v b)
obtained by mapping the container values a
to actions f b
using your a -> f b
, sequencing those actions in order, and collecting the results in a new container v b
.
This is a draft PR to create utilities for working with monads in lenses.
It currently covers
_1M
,_2M
,_LeftM
and_RightM
. I'd like to dotraversed
andpropM
as well, after which I think it'll be in good shape for review.propM
seems manageable buttraversedM
, with signaturetraversedM :: forall t a b m. Traversable t => Traversal (t a) (m (t b)) a (m b)
, seems quite daunting. Essentially you'd need to "apply traverse twice" but I'm not sure if that's possible and what that function would look like. If anyone would be interested in writingtraversedM
that'd be helpful!Checklist: