optics-core-0.3: src/Optics/AffineFold.hs
-- |
-- Module: Optics.AffineFold
-- Description: A 'Optics.Fold.Fold' that contains at most one element.
--
-- An 'AffineFold' is a 'Optics.Fold.Fold' that contains at most one
-- element, or a 'Optics.Getter.Getter' where the function may be
-- partial.
--
module Optics.AffineFold
(
-- * Formation
AffineFold
-- * Introduction
, afolding
-- * Elimination
, preview
, previews
-- * Computation
-- |
--
-- @
-- 'preview' ('afolding' f) ≡ f
-- @
-- * Additional introduction forms
, afoldVL
, filtered
-- * Additional elimination forms
, atraverseOf_
, isn't
-- * Monoid structure
-- | 'AffineFold' admits a monoid structure where 'afailing' combines folds
-- (returning a result from the second fold only if the first returns none)
-- and the identity element is 'Optics.IxAffineTraversal.ignored' (which
-- returns no results).
--
-- /Note:/ There is no 'Optics.Fold.summing' equivalent that returns an
-- 'AffineFold', because it would not need to return more than one result.
--
-- There is no 'Semigroup' or 'Monoid' instance for 'AffineFold', because
-- there is not a unique choice of monoid to use that works for all optics,
-- and the ('<>') operator could not be used to combine optics of different
-- kinds.
, afailing
-- * Subtyping
, An_AffineFold
-- | <<diagrams/AffineFold.png AffineFold in the optics hierarchy>>
) where
import Data.Maybe
import Data.Profunctor.Indexed
import Optics.Internal.Bi
import Optics.Internal.Optic
-- | Type synonym for an affine fold.
type AffineFold s a = Optic' An_AffineFold NoIx s a
-- | Obtain an 'AffineFold' by lifting 'traverse_' like function.
--
-- @
-- 'afoldVL' '.' 'atraverseOf_' ≡ 'id'
-- 'atraverseOf_' '.' 'afoldVL' ≡ 'id'
-- @
--
-- @since 0.3
afoldVL
:: (forall f. Functor f => (forall r. r -> f r) -> (a -> f u) -> s -> f v)
-> AffineFold s a
afoldVL f = Optic (rphantom . visit f . rphantom)
{-# INLINE afoldVL #-}
-- | Retrieve the value targeted by an 'AffineFold'.
--
-- >>> let _Right = prism Right $ either (Left . Left) Right
--
-- >>> preview _Right (Right 'x')
-- Just 'x'
--
-- >>> preview _Right (Left 'y')
-- Nothing
--
preview :: Is k An_AffineFold => Optic' k is s a -> s -> Maybe a
preview o = previews o id
{-# INLINE preview #-}
-- | Retrieve a function of the value targeted by an 'AffineFold'.
previews :: Is k An_AffineFold => Optic' k is s a -> (a -> r) -> s -> Maybe r
previews o = \f -> runForgetM $
getOptic (castOptic @An_AffineFold o) $ ForgetM (Just . f)
{-# INLINE previews #-}
-- | Traverse over the target of an 'AffineFold', computing a 'Functor'-based
-- answer, but unlike 'Optics.AffineTraversal.atraverseOf' do not construct a
-- new structure.
--
-- @since 0.3
atraverseOf_
:: (Is k An_AffineFold, Functor f)
=> Optic' k is s a
-> (forall r. r -> f r) -> (a -> f u) -> s -> f ()
atraverseOf_ o point f s = case preview o s of
Just a -> () <$ f a
Nothing -> point ()
-- | Create an 'AffineFold' from a partial function.
--
-- >>> preview (afolding listToMaybe) "foo"
-- Just 'f'
--
afolding :: (s -> Maybe a) -> AffineFold s a
afolding f = Optic (contrabimap (\s -> maybe (Left s) Right (f s)) Left . right')
{-# INLINE afolding #-}
-- | Filter result(s) of a fold that don't satisfy a predicate.
filtered :: (a -> Bool) -> AffineFold a a
filtered p = afoldVL (\point f a -> if p a then f a else point a)
{-# INLINE filtered #-}
-- | Try the first 'AffineFold'. If it returns no entry, try the second one.
--
-- >>> preview (ix 1 % re _Left `afailing` ix 2 % re _Right) [0,1,2,3]
-- Just (Left 1)
--
-- >>> preview (ix 42 % re _Left `afailing` ix 2 % re _Right) [0,1,2,3]
-- Just (Right 2)
--
afailing
:: (Is k An_AffineFold, Is l An_AffineFold)
=> Optic' k is s a
-> Optic' l js s a
-> AffineFold s a
afailing a b = afolding $ \s -> maybe (preview b s) Just (preview a s)
infixl 3 `afailing` -- Same as (<|>)
{-# INLINE afailing #-}
-- | Check to see if this 'AffineFold' doesn't match.
--
-- >>> isn't _Just Nothing
-- True
--
isn't :: Is k An_AffineFold => Optic' k is s a -> s -> Bool
isn't k s = not (isJust (preview k s))
{-# INLINE isn't #-}
-- $setup
-- >>> import Optics.Core