{-# LANGUAGE FlexibleContexts, RankNTypes #-}
-- | Provides combinators for the lens-based manipulation of state and
-- context types provided by the fused-effects library, similar to
-- those provided for mtl-based monad transformers.
module Control.Effect.Lens
( -- * Reader accessors
Control.Effect.Lens.view
, views
-- * State getters/setters
, use
, uses
, assign
, modifying
-- * Infix operators
, (.=)
, (?=)
, (%=)
, (<~)
-- * Mathematical operators
, (+=)
, (-=)
, (*=)
, (//=)
) where
import Control.Algebra
import Control.Effect.Reader as Reader
import Control.Effect.State as State
import Lens.Micro as Lens
import Lens.Micro.Extras as Lens
-- | View the value pointed to by a @Getter@, 'Lens', 'Traversal', or
-- @Fold@ corresponding to the 'Reader' context of the given monadic
-- carrier.
view :: forall r a sig m . (Has (Reader.Reader r) sig m) => Getting a r a -> m a
view l = Reader.asks (Lens.view l)
{-# INLINE view #-}
-- | View a function of the value pointed to by a @Getter@ or 'Lens',
-- or the result of folding over all the results of a @Fold@ or
-- 'Traversal', when applied to the 'Reader' context of the given
-- monadic carrier.
--
-- This is slightly more general in lens itself, but should suffice for our purposes.
views :: forall s a b sig m . (Has (Reader.Reader s) sig m) => Getting a s a -> (a -> b) -> m b
views l f = fmap f (Reader.asks (Lens.view l))
{-# INLINE views #-}
-- | Extract the target of a 'Lens' or @Getter@, or use a summary of a
-- @Fold@ or 'Traversal' that points to a monoidal value.
use :: forall s a sig m . (Has (State.State s) sig m) => Getting a s a -> m a
use l = State.gets (Lens.view l)
{-# INLINE use #-}
-- | Use a function of the target of a 'Lens' or @Getter@ in the
-- current state, or use a summary of a @Fold@ or 'Traversal' that
-- points to a monoidal value.
uses :: forall s a b f sig . (Has (State.State s) sig f) => Getting a s a -> (a -> b) -> f b
uses l f = fmap f (State.gets (Lens.view l))
{-# INLINE uses #-}
-- | Replace the target of a 'Lens' (or all the targets of a @Setter@
-- or 'Traversal') within the current monadic state, irrespective of
-- the old value.
--
-- This is a prefix version of '.='.
assign :: forall s a b sig m . (Has (State.State s) sig m) => ASetter s s a b -> b -> m ()
assign l b = State.modify (Lens.set l b)
{-# INLINE assign #-}
-- | Map over the target of a 'Lens', or all of the targets of a @Setter@
-- or 'Traversal', in the current monadic state.
--
-- This is a prefix version of '%='.
modifying :: forall s a b sig m . (Has (State.State s) sig m) => ASetter s s a b -> (a -> b) -> m ()
modifying l f = State.modify (Lens.over l f)
{-# INLINE modifying #-}
infix 4 .=, %=, ?=, +=, -=, *=, //=
infixr 2 <~
-- | Replace the target of a 'Lens' (or all the targets of a @Setter@
-- or 'Traversal') within the current monadic state, irrespective of
-- the old value.
--
-- This is an infix version of 'assign'.
(.=) :: forall s a b sig m . (Has (State.State s) sig m) => ASetter s s a b -> b -> m ()
(.=) = assign
{-# INLINE (.=) #-}
-- | Replace the target of a Lens or all of the targets of a @Setter@ or
-- 'Traversal' in our monadic state with Just a new value, irrespective
-- of the old.
(?=) :: forall s a b sig m . (Has (State.State s) sig m) => ASetter s s a (Maybe b) -> b -> m ()
setter ?= item = setter .= Just item
{-# INLINE (?=) #-}
-- | Run a monadic action, and set all of the targets of a 'Lens', @Setter@
-- or 'Traversal' to its result.
(<~) :: forall s a b sig m . (Has (State s) sig m) => ASetter s s a b -> m b -> m ()
setter <~ act = act >>= assign setter
{-# INLINE (<~) #-}
-- | Map over the target of a 'Lens', or all of the targets of a @Setter@
-- or 'Traversal', in the current monadic state.
--
-- This is an infix version of 'modifying'.
(%=) :: forall s a b sig m . (Has (State.State s) sig m) => ASetter s s a b -> (a -> b) -> m ()
(%=) = modifying
{-# INLINE (%=) #-}
-- | Modify the target(s) of a 'Lens', @Iso@, @Setter@ or 'Traversal' by adding a value.
(+=) :: forall s a sig m . (Has (State.State s) sig m, Num a) => ASetter' s a -> a -> m ()
l += v = State.modify (l +~ v)
{-# INLINE (+=) #-}
-- | Modify the target(s) of a 'Lens', @Iso@, @Setter@ or 'Traversal' by subtracting a value.
(-=) :: forall s a sig m . (Has (State.State s) sig m, Num a) => ASetter' s a -> a -> m ()
l -= v = State.modify (l -~ v)
{-# INLINE (-=) #-}
-- | Modify the target(s) of a 'Lens', @Iso@, @Setter@ or 'Traversal' by subtracting a value.
(*=) :: forall s a sig m . (Has (State.State s) sig m, Num a) => ASetter' s a -> a -> m ()
l *= v = modifying l (* v)
{-# INLINE (*=) #-}
-- | Modify the target(s) of a 'Lens', @Iso@, @Setter@ or 'Traversal' by dividing a value.
(//=) :: forall s a sig m . (Has (State.State s) sig m, Fractional a) => ASetter' s a -> a -> m ()
l //= v = modifying l (/ v)
{-# INLINE (//=) #-}