profunctor-optics-0.0.0.3: src/Data/Profunctor/Optic/Zoom.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Profunctor.Optic.Zoom where
import Control.Monad.Reader as Reader
import Control.Monad.State as State
import Control.Monad.Trans.State.Lazy as Lazy
import Control.Monad.Trans.State.Strict as Strict
import Control.Monad.Trans.RWS.Lazy as Lazy
import Control.Monad.Trans.RWS.Strict as Strict
import Control.Monad.Trans.Identity
import Data.Profunctor.Optic.Import
import Data.Profunctor.Optic.Types
-- $setup
-- >>> :set -XNoOverloadedStrings
-- >>> :set -XTypeApplications
-- >>> :set -XFlexibleContexts
-- >>> :set -XFlexibleInstances
-- >>> :set -XRankNTypes
-- >>> import Control.Monad.State
-- >>> :load Data.Profunctor.Optic
infixr 2 `zoom`
type family Zoomed (m :: * -> *) :: * -> * -> *
type instance Zoomed (IdentityT m) = Zoomed m
type instance Zoomed (ReaderT e m) = Zoomed m
type instance Zoomed (Strict.StateT s z) = StateTRep z
type instance Zoomed (Lazy.StateT s z) = StateTRep z
type instance Zoomed (Strict.RWST r w s z) = RWSTRep w z
type instance Zoomed (Lazy.RWST r w s z) = RWSTRep w z
class (MonadState s m, MonadState t n) => Zoom m n s t | m -> s, n -> t, m t -> n, n s -> m where
-- | Run a monadic action in a larger 'State' than it was defined in.
--
-- Used to lift actions into a 'State' 'Monad' with a larger 'State' type.
--
-- >>> flip evalState (1,"foo") $ zoom first' $ use id
-- 1
--
-- >>> flip evalState [Right "foo", Right "bar"] $ zoom traversed $ use right'
-- "foobar"
--
-- >>> flip execState (1,"foo") $ zoom first' $ id .= 2
-- (2,"foo")
--
-- >>> flip execState [(1,"foo"), (2,"foo")] $ zoom traversed $ second' ..= (<>"bar")
-- [(1,"foobar"),(2,"foobar")]
--
-- >>> flip execState [Left "foo", Right "bar"] $ zoom traversed $ right' ..= (<>"baz")
-- [Left "foo",Right "barbaz"]
--
-- >>> flip evalState ("foo","bar") $ zoom both (use id)
-- "foobar"
--
zoom :: Optic' (Star (Zoomed m c)) t s -> m c -> n c
instance Zoom m n s t => Zoom (IdentityT m) (IdentityT n) s t where
zoom l (IdentityT m) = IdentityT (zoom l m)
{-# INLINE zoom #-}
instance Zoom m n s t => Zoom (ReaderT e m) (ReaderT e n) s t where
zoom l (ReaderT m) = ReaderT (zoom l . m)
{-# INLINE zoom #-}
instance Monad z => Zoom (Strict.StateT s z) (Strict.StateT t z) s t where
zoom o m = Strict.StateT $ unStateTRep #. (runStar #. o .# Star) (StateTRep #. (Strict.runStateT m))
{-# INLINE zoom #-}
instance Monad z => Zoom (Lazy.StateT s z) (Lazy.StateT t z) s t where
zoom o m = Lazy.StateT $ unStateTRep #. (runStar #. o .# Star) (StateTRep #. (Lazy.runStateT m))
{-# INLINE zoom #-}
instance (Monoid w, Monad z) => Zoom (Strict.RWST r w s z) (Strict.RWST r w t z) s t where
zoom o m = Strict.RWST $ \r -> unRWSTRep #. (runStar #. o .# Star) (RWSTRep #. (Strict.runRWST m r))
{-# INLINE zoom #-}
instance (Monoid w, Monad z) => Zoom (Lazy.RWST r w s z) (Lazy.RWST r w t z) s t where
zoom o m = Lazy.RWST $ \r -> unRWSTRep #. (runStar #. o .# Star) (RWSTRep #. (Lazy.runRWST m r))
{-# INLINE zoom #-}
----------------------------------------------------------------------
-- StateTRep
----------------------------------------------------------------------
newtype StateTRep m s a = StateTRep { unStateTRep :: m (s, a) }
instance Monad m => Functor (StateTRep m s) where
fmap f (StateTRep m) = StateTRep $ do
(s, a) <- m
return (s, f a)
{-# INLINE fmap #-}
instance (Monad m, Semigroup s) => Apply (StateTRep m s) where
StateTRep mf <.> StateTRep ma = StateTRep $ do
(s, f) <- mf
(s', a) <- ma
return (s <> s', f a)
{-# INLINE (<.>) #-}
instance (Monad m, Monoid s) => Applicative (StateTRep m s) where
pure a = StateTRep (return (mempty, a))
{-# INLINE pure #-}
StateTRep mf <*> StateTRep ma = StateTRep $ do
(s, f) <- mf
(s', a) <- ma
return (mappend s s', f a)
{-# INLINE (<*>) #-}
------------------------------------------------------------------------------
-- RWSTRep
------------------------------------------------------------------------------
newtype RWSTRep w m s a = RWSTRep { unRWSTRep :: m (s, a, w) }
instance Monad m => Functor (RWSTRep w m s) where
fmap f (RWSTRep m) = RWSTRep $ do
(s, a, w) <- m
return (s, f a, w)
{-# INLINE fmap #-}
instance (Monad m, Semigroup s, Semigroup w) => Apply (RWSTRep w m s) where
RWSTRep mf <.> RWSTRep ma = RWSTRep $ do
(s, f, w) <- mf
(s', a, w') <- ma
return (s <> s', f a, w <> w')
{-# INLINE (<.>) #-}
instance (Monad m, Monoid s, Monoid w) => Applicative (RWSTRep w m s) where
pure a = RWSTRep (return (mempty, a, mempty))
{-# INLINE pure #-}
RWSTRep mf <*> RWSTRep ma = RWSTRep $ do
(s, f, w) <- mf
(s', a, w') <- ma
return (mappend s s', f a, mappend w w')
{-# INLINE (<*>) #-}