optics-core-0.4.2: src/Optics/Setter.hs
-- |
-- Module: Optics.Setter
-- Description: Applies an update to all contained values.
--
-- A @'Setter' S T A B@ has the ability to lift a function of type
-- @A -> B@ 'over' a function of type @S -> T@, applying the function
-- to update all the @A@s contained in @S@. This can be used to 'set'
-- all the @A@s to a single value (by lifting a constant function).
--
-- This can be seen as a generalisation of 'fmap', where the type @S@
-- does not need to be a type constructor with @A@ as its last
-- parameter.
--
module Optics.Setter
(
-- * Formation
Setter
, Setter'
-- * Introduction
, sets
-- * Elimination
, over
-- * Computation
-- |
--
-- @
-- 'over' ('sets' f) ≡ f
-- @
-- * Well-formedness
-- |
--
-- * __PutPut__: Setting twice is the same as setting once:
--
-- @
-- 'Optics.Setter.set' l v' ('Optics.Setter.set' l v s) ≡ 'Optics.Setter.set' l v' s
-- @
--
-- * __Functoriality__: 'Setter's must preserve identities and composition:
--
-- @
-- 'over' s 'id' ≡ 'id'
-- 'over' s f '.' 'over' s g ≡ 'over' s (f '.' g)
-- @
-- * Additional introduction forms
-- | See also 'Data.Set.Optics.setmapped', which changes the elements of a 'Data.Set.Set'.
, mapped
-- * Additional elimination forms
, set
, set'
, over'
, rewriteOf
, transformOf
-- * Subtyping
, A_Setter
-- | <<diagrams/Setter.png Setter in the optics hierarchy>>
) where
import Data.Profunctor.Indexed
import Optics.Internal.Optic
import Optics.Internal.Setter
import Optics.Internal.Utils
-- | Type synonym for a type-modifying setter.
type Setter s t a b = Optic A_Setter NoIx s t a b
-- | Type synonym for a type-preserving setter.
type Setter' s a = Optic' A_Setter NoIx s a
-- | Apply a setter as a modifier.
over
:: Is k A_Setter
=> Optic k is s t a b
-> (a -> b) -> s -> t
over o = \f -> runFunArrow $ getOptic (castOptic @A_Setter o) (FunArrow f)
{-# INLINE over #-}
-- | Apply a setter as a modifier, strictly.
--
-- TODO DOC: what exactly is the strictness property?
--
-- Example:
--
-- @
-- f :: Int -> (Int, a) -> (Int, a)
-- f k acc
-- | k > 0 = f (k - 1) $ 'over'' 'Data.Tuple.Optics._1' (+1) acc
-- | otherwise = acc
-- @
--
-- runs in constant space, but would result in a space leak if used with 'over'.
--
-- Note that replacing '$' with '$!' or 'Data.Tuple.Optics._1' with
-- 'Data.Tuple.Optics._1'' (which amount to the same thing) doesn't help when
-- 'over' is used, because the first coordinate of a pair is never forced.
--
over'
:: Is k A_Setter
=> Optic k is s t a b
-> (a -> b) -> s -> t
over' o = \f ->
let star = getOptic (castOptic @A_Setter o) $ Star (wrapIdentity' . f)
in unwrapIdentity' . runStar star
{-# INLINE over' #-}
-- | Apply a setter.
--
-- @
-- 'set' o v ≡ 'over' o ('const' v)
-- @
--
-- >>> set _1 'x' ('y', 'z')
-- ('x','z')
--
set
:: Is k A_Setter
=> Optic k is s t a b
-> b -> s -> t
set o = over o . const
{-# INLINE set #-}
-- | Apply a setter, strictly.
--
-- TODO DOC: what exactly is the strictness property?
--
set'
:: Is k A_Setter
=> Optic k is s t a b
-> b -> s -> t
set' o = over' o . const
{-# INLINE set' #-}
-- | Build a setter from a function to modify the element(s), which must respect
-- the well-formedness laws.
sets
:: ((a -> b) -> s -> t)
-> Setter s t a b
sets f = Optic (roam f)
{-# INLINE sets #-}
-- | Create a 'Setter' for a 'Functor'.
--
-- @
-- 'over' 'mapped' ≡ 'fmap'
-- @
--
mapped :: Functor f => Setter (f a) (f b) a b
mapped = Optic mapped__
{-# INLINE mapped #-}
-- | Rewrite by applying a rule everywhere you can. Ensures that the rule cannot
-- be applied anywhere in the result:
--
-- @
-- propRewriteOf l r x = 'all' ('Data.Just.isNothing' '.' r) ('universeOf' l ('rewriteOf' l r x))
-- @
--
-- Usually 'transformOf' is more appropriate, but 'rewriteOf' can give better
-- compositionality. Given two single transformations @f@ and @g@, you can
-- construct @\\a -> f a '<|>' g a@ which performs both rewrites until a fixed
-- point.
--
-- @since 0.4.1
rewriteOf :: Is k A_Setter => Optic k is a b a b -> (b -> Maybe a) -> a -> b
rewriteOf o f = go
where
go = transformOf o $ \x -> maybe x go (f x)
{-# INLINE rewriteOf #-}
-- | Transform every element by recursively applying a given 'Setter' in a
-- bottom-up manner.
--
-- @since 0.4.1
transformOf :: Is k A_Setter => Optic k is a b a b -> (b -> b) -> a -> b
transformOf o f = go
where
go = f . over o go
{-# INLINE transformOf #-}
-- $setup
-- >>> import Optics.Core