Closed gittywithexcitement closed 8 years ago
How do you imagine it would work? Currently you can use zoom
from lens
, see this comment.
I see. That comment covers the case where the callee function has a mtl MonadState constraint. As far as I know, it does not help with:
The simplest use case is when the callee function uses 1 Ether state transformer. I could use mtl state transformer instead, but I prefer uniformity in my code over a mix of Ether and mtl state transformers.
My example use case is a caller function that uses 2 (implicitly or explicitly) tagged Ether state transformers, and a callee that uses one of the states untouched, and a lens applied to the other state.
import Control.Lens.Type (Lens')
import qualified Control.Monad.Ether.State.Strict as EtherE
import qualified Control.Monad.Ether.Implicit.State.Strict as EtherI
caller :: (EtherI.MonadState (Sum Int) m, EtherI.MonadState Text m)
=> m Int
caller = zoomE (Proxy:: Proxy (Sum Int)) (Proxy:: Proxy Int) _Wrapped' callee
callee :: (EtherI.MonadState (Int) m, EtherI.MonadState Text m)
=> m Int
callee = EtherI.get
Here's what I came up with:
zoomE :: EtherE.MonadState tagBig stateBig m
=> proxy tagBig
-> proxy tagSmall
-> Lens' stateBig stateSmall
-> EtherE.StateT tagSmall stateSmall m b
-> m b
zoomE tagBig_ tagSmall_ lens st = do
sBig0 <- EtherE.get tagBig_
let sSmall0 = view lens sBig0
(a, sSmall1) <- EtherE.runStateT tagSmall_ st sSmall0
let sBig1 = set lens sSmall1 sBig0
EtherE.put tagBig_ sBig1
pure a
zoomI :: EtherI.MonadState stateBig m
=> Lens' stateBig stateSmall
-> EtherI.StateT stateSmall m b
-> m b
zoomI lens st = do
sBig0 <- EtherI.get
let sSmall0 = view lens sBig0
(a, sSmall1) <- EtherI.runStateT st sSmall0
let sBig1 = set lens sSmall1 sBig0
EtherI.put sBig1
pure a
Let me know what you think. If this looks reasonable to you I can make a PR and do edits.
Your solution is specific to StateT
. I need to explore the design space. I definitely don't want to incur a dependency on lens
for this package, but I'm afraid a good zoom
implementation would require it.
I have a couple of ideas, though.
I've developed a solution that works nicely using reflection
. I have no intention to include it in ether
but it surely can go into a separate package ether-zoom
. Ping me if you're still interested.
I am interested, @int-index. Did you ever make ether-zoom
?
No, but here's the general idea:
You can redirect instance search using DispatchT
in the style of tagAttach
and tagReplace
. Declare a type like data K_TagZoom t z = TagZoom t z
and use the z
parameter to reify a lens: Reifies z (Lens' stateOuter stateInner)
. Use this lens to define the appropriate MonadState
instance:
instance ( MonadState tag stateOuter m
, Reifies z (Lens' stateOuter stateInner)
) => MonadState tag stateInner (DispatchTagZoomT tag z m) where
get = ... -- use the lens here
put = ... -- use the lens here
state = ... -- use the lens here
I'll probably package this up when I'm done with ether-flatten
, but no promises. PRs welcome.
I realized that there are some non-trivial pitfalls and implemented it myself, see #26. Tell me what you think.
What about adding a zoom function for implicitly and explicitly tagged transformers?