packages feed

fused-effects-optics-0.2.1.0: src/Control/Effect/Optics.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

module Control.Effect.Optics
  ( -- * Reader operations
    Control.Effect.Optics.view,
    Control.Effect.Optics.views,
    locally,

    -- * State operations
    use,
    uses,
    preuse,
    assign,
    modifying,

    -- * Infix operators
    (.=),
    (?=),
    (%=),
    (<~),
  )
where

import Control.Effect.Reader as Reader
import Control.Effect.State as State
import Optics.Core

-- | View the target of a 'Lens', 'Iso', or 'Getter' in the current context.
--
-- Because functions implement 'Reader.Reader', you can use this wherever
-- you would use the @view@ function in @optics@, as well as the @gview@
-- operation in @optics-extra@.
view ::
  forall r a m sig k is.
  ( Is k A_Getter,
    Has (Reader.Reader r) sig m
  ) =>
  Optic' k is r a ->
  m a
view l = Reader.asks (Optics.Core.view l)
{-# INLINE view #-}

-- | Apply a function to the target of a 'Lens', 'Iso', or 'Getter' in the current context.
views ::
  forall r a b m sig k is.
  ( Is k A_Getter,
    Has (Reader.Reader r) sig m
  ) =>
  Optic' k is r a ->
  (a -> b) ->
  m b
views l f = Reader.asks (f . Optics.Core.view l)
{-# INLINE views #-}

-- | Given a monadic argument, evaluate it in a context modified by applying
-- the provided function to the target of the provided 'Setter', 'Lens', or 'Traversal'.
locally ::
  ( Is k A_Setter,
    Has (Reader.Reader r) sig m
  ) =>
  Optic k is r r a b ->
  (a -> b) ->
  m c ->
  m c
locally l f = Reader.local (over l f)

-- | Use the target of a 'Lens', 'Iso', or 'Getter' in the current state.
use ::
  forall s a m sig k is.
  ( Is k A_Getter,
    Has (State.State s) sig m
  ) =>
  Optic' k is s a ->
  m a
use l = State.gets (Optics.Core.view l)
{-# INLINE use #-}

-- | Apply a function to the target of a 'Lens', 'Iso', or 'Getter' in the current state.
uses ::
  forall s a b m sig k is.
  ( Is k A_Getter,
    Has (State.State s) sig m
  ) =>
  Optic' k is s a ->
  (a -> b) ->
  m b
uses l f = State.gets (f . Optics.Core.view l)
{-# INLINE uses #-}

-- | Use the target of a 'AffineTraversal' or 'AffineFold' in the current state.
preuse ::
  forall s a m sig k is.
  ( Is k An_AffineFold,
    Has (State.State s) sig m
  ) =>
  Optic' k is s a ->
  m (Maybe a)
preuse l = State.gets (preview l)
{-# INLINE preuse #-}

-- | Replace the target(s) of an Optic in our monadic state with a new value, irrespective of the old.
-- The action and the optic operation are applied strictly.
--
-- This is aprefix form of '.='.
assign ::
  forall s a b m sig k is.
  ( Is k A_Setter,
    Has (State.State s) sig m
  ) =>
  Optic k is s s a b ->
  b ->
  m ()
assign l x = State.modify (set' l x)
{-# INLINE assign #-}

-- | Map over the target(s) of an 'Optic' in our monadic state.
-- The action and the optic operation are applied strictly.
modifying ::
  ( Is k A_Setter,
    Has (State.State s) sig m
  ) =>
  Optic k is s s a b ->
  (a -> b) ->
  m ()
modifying l x = State.modify (over' l x)
{-# INLINE modifying #-}

-- * Operators

infix 4 .=

infix 4 ?=

infix 4 %=

-- | Replace the target(s) of an Optic in our monadic state with a new value, irrespective of the old.
-- The action and the optic operation are applied strictly.
--
-- This is an infix form of 'assign'.
(.=) ::
  forall s a b m sig k is.
  ( Is k A_Setter,
    Has (State.State s) sig m
  ) =>
  Optic k is s s a b ->
  b ->
  m ()
(.=) = assign
{-# INLINE (.=) #-}

-- | Replace the target(s) of an Optic in our monadic state with 'Just' a new value, irrespective of the old.
-- The action and the optic operation are applied strictly.
(?=) ::
  forall s a b m sig k is.
  ( Is k A_Setter,
    Has (State.State s) sig m
  ) =>
  Optic k is s s a (Maybe b) ->
  b ->
  m ()
l ?= a = State.modify (set l (Just a))
{-# INLINE (?=) #-}

-- | Map over the target(s) of an 'Optic' in our monadic state.
-- The action and the optic operation are applied strictly.
(%=) ::
  ( Is k A_Setter,
    Has (State.State s) sig m
  ) =>
  Optic k is s s a b ->
  (a -> b) ->
  m ()
(%=) = modifying
{-# INLINE (%=) #-}

-- | Run the provided monadic action and assign it to the target of a 'Setter'.
(<~) :: (Is k A_Setter, Has (State s) sig m) => Optic k is s s a b -> m b -> m ()
l <~ mb = mb >>= assign l