-- |
--
-- Module: Optics
-- Description: The main module, usually you only need to import this one.
--
-- This library makes it possible to define and use 'Lens'es, 'Traversal's,
-- 'Prism's and other /optics/, using an /abstract interface/.
--
module Optics
  (
  -- * Introduction
  -- $introduction

  -- ** What are optics?
  -- $what

  -- ** What is the abstract interface?
  -- $abstract

  -- ** Comparison with @lens@
  -- $lens_comparison

  -- ** Other resources
  -- $otherresources

  -- * Using the library
  -- $basicusage
    module Optics.Optic

  -- ** Optic kinds #optickinds#
  , module O

  -- ** Optic operators
  , module Optics.Operators

  -- * Optics utilities

  -- ** At

  -- | An 'AffineTraversal' to traverse a key in a map or an element of a
  -- sequence:
  --
  -- >>> preview (ix 1) ['a','b','c']
  -- Just 'b'
  --
  -- a 'Lens' to get, set or delete a key in a map:
  --
  -- >>> set (at 0) (Just 'b') (Map.fromList [(0, 'a')])
  -- fromList [(0,'b')]
  --
  -- and a 'Lens' to insert or remove an element of a set:
  --
  -- >>> IntSet.fromList [1,2,3,4] & contains 3 .~ False
  -- fromList [1,2,4]
  --
  , module Optics.At

  -- ** Cons

  -- | 'Prism's to match on the left or right side of a list, vector or other
  -- sequential structure:
  --
  -- >>> preview _Cons "abc"
  -- Just ('a',"bc")
  --
  -- >>> preview _Snoc "abc"
  -- Just ("ab",'c')
  --
  , module Optics.Cons

  -- ** Each

  -- | An 'IxTraversal' for each element of a (potentially monomorphic) container.
  --
  -- >>> over each (*10) (1,2,3)
  -- (10,20,30)
  --
  , module Optics.Each

  -- ** Empty

  -- | A 'Prism' for a container type that may be empty.
  --
  -- >>> isn't _Empty [1,2,3]
  -- True
  --
  , module Optics.Empty

  -- ** Re

  -- | Some optics can be reversed with 're'.  This is mainly useful to invert
  -- 'Iso's:
  --
  -- >>> let _Identity = iso runIdentity Identity
  -- >>> view (_1 % re _Identity) ('x', "yz")
  -- Identity 'x'
  --
  -- Yet we can use a 'Lens' as a 'Review' too:
  --
  -- >>> review (re _1) ('x', "yz")
  -- 'x'
  --
  -- In the following diagram, red arrows illustrate how 're' transforms optics.
  -- The 'ReversedLens' and 'ReversedPrism' optic kinds are backwards versions
  -- of 'Lens' and 'Prism' respectively, and are present so that @'re' . 're'@
  -- does not change the optic kind.
  --
  -- <<diagrams/reoptics.png Reversed Optics>>
  --
  , module Optics.Re

  -- ** ReadOnly

  -- | Defines 'getting', which turns a read-write optic into its read-only
  -- counterpart.
  , module Optics.ReadOnly

  -- ** Mapping

  -- | Defines 'mapping' through 'Functor's
  , module Optics.Mapping

  -- ** 'Setter' utilities for working in 'Control.Monad.State.MonadState'.
  , module Optics.State

  -- ** View

  -- | A generalized view function 'gview', which returns a single result (like
  -- 'view') if the optic is a 'Getter', a 'Maybe' result (like 'preview') if
  -- the optic is an 'AffineFold', or a monoidal summary of results (like
  -- 'foldOf') if the optic is a 'Fold'.  In addition, it works for any
  -- 'Control.Monad.Reader.MonadReader', not just @(->)@.
  --
  -- >>> gview _1 ('x','y')
  -- 'x'
  --
  -- >>> gview _Left (Left 'x')
  -- Just 'x'
  --
  -- >>> gview folded ["a", "b"]
  -- "ab"
  --
  -- >>> runReaderT (gview _1) ('x','y') :: IO Char
  -- 'x'
  --
  -- This module is experimental.  Using the more type-restricted variants is
  -- encouraged where possible.
  --
  , module Optics.View

  -- ** Zoom

  -- | A class to 'zoom' in, changing the 'Control.Monad.State.State' supplied
  -- by many different monad transformers, potentially quite deep in a monad
  -- transformer stack.
  --
  -- >>> flip execState ('a','b') $ zoom _1 $ equality .= 'c'
  -- ('c','b')
  --
  , module Optics.Zoom

  -- * Indexed optics
  -- $indexed
  , module Optics.Indexed

  -- * Generation of optics
  -- ** ...with Template Haskell
  , module Optics.TH
  -- ** ...with @OverloadedLabels@
  , module Optics.Label

  -- * Optics for concrete base types
  , module P

  -- * Cheat sheet
  -- $cheatsheet
  )
  where

-- Core optics functionality

-- for some reason haddock reverses the list...

import Optics.Optic

import Optics.Traversal                      as O
import Optics.Setter                         as O
import Optics.Review                         as O
import Optics.ReversedPrism                  as O
import Optics.Prism                          as O
import Optics.ReversedLens                   as O
import Optics.Lens                           as O
import Optics.IxTraversal                    as O
import Optics.IxSetter                       as O
import Optics.IxFold                         as O
import Optics.IxAffineTraversal              as O
import Optics.IxAffineFold                   as O
import Optics.IxGetter                       as O
import Optics.IxLens                         as O
import Optics.Iso                            as O
import Optics.Getter                         as O
import Optics.Fold                           as O
import Optics.AffineTraversal                as O
import Optics.AffineFold                     as O

-- Optics utilities
import Optics.At
import Optics.Cons
import Optics.Each
import Optics.Empty
import Optics.Indexed
import Optics.Mapping
import Optics.Operators
import Optics.Re
import Optics.ReadOnly
import Optics.State
import Optics.View
import Optics.Zoom

-- Overloaded labels support
import Optics.Label

-- Template Haskell support
import Optics.TH

-- Optics for concrete base types

import Data.Tuple.Optics                     as P
import Data.Maybe.Optics                     as P
import Data.Either.Optics                    as P

-- $introduction
--
-- Read on for a general introduction to the notion of optics, or if you are
-- familiar with them already, you may wish to jump ahead to the "What is the
-- abstract interface?"  section below in "Optics#abstract".

-- $what
--
-- An optic is a first-class, composable /notion of substructure/.  As a highly
-- abstract concept, the idea can be approached by considering several examples
-- of optics and understanding their common features. What are the possible
-- relationships between some "outer" type @S@ and some "inner" type @A@?
--
-- (For simplicity we will initially ignore the possibility of type-changing
-- update operations, which change @A@ to some other type @B@ and hence change
-- @S@ to some other type @T@.  These are fully supported by the library, at the
-- cost of some extra type parameters.)
--
-- === "Optics.Iso": isomorphisms
--
-- First, @S@ and @A@ may be /isomorphic/, i.e. there exist mutually inverse
-- functions to convert @S -> A@ and @A -> S@.  This is a somewhat trivial
-- notion of substructure: @A@ is just another way to represent "all of @S@".
--
-- An @'Iso'' S A@ is an isomorphism between @S@ and @A@, with the conversion
-- functions given by 'view' and 'review'. For example, given
--
-- @
-- newtype Age = Age Int
-- @
--
-- there is an isomorphism between the newtype and its representation:
--
-- @
--        'coerced' :: 'Iso'' Age 'Int'
-- 'view'   'coerced' :: Age -> 'Int'
-- 'review' 'coerced' :: 'Int' -> Age
-- @
--
-- === "Optics.Lens": generalised fields
--
-- If @S@ is a simple product type (i.e. it has a single constructor with one or
-- more fields), @A@ may be a single field of @S@.  More generally, @A@ may be
-- "part of @S@" in the sense that @S@ is isomorphic to the pair @(A,C)@ for
-- some type @C@ representing the other fields.  In this case, there is a
-- /projection/ function @S -> A@ for getting the value of the field, but the
-- /update/ function (setting the value of the field) requires the "rest of @S@"
-- and so has type @A -> S -> S@.
--
-- A @'Lens'' S A@ captures the structure of @A@ being a field of @S@, with the
-- projection function given by 'view' and the update function by 'set'.  For
-- example, for the pair type @(X,Y)@ there are lenses for each component:
--
-- @
--      '_1' :: 'Lens'' (X,Y) X
--      '_2' :: 'Lens'' (X,Y) Y
-- 'view' '_1' :: (X,Y) -> X
-- 'set'  '_2' :: Y -> (X,Y) -> (X,Y)
-- @
--
-- (Note that the update function could arguably have the more precise type @A
-- -> C -> S@, since we do not expect the result of setting a field to depend on
-- the previous value of the field.  However, making @C@ explicit turns out to
-- be awkward, so instead we impose /laws/ to require that the result of setting
-- the field depends only on @C@, and, more generally, that the lens behaves as
-- we would expect.)
--
-- === "Optics.Prism": generalised constructors
--
-- If @S@ is a simple sum type (i.e. it has one or more constructors, each with
-- a single field), @A@ may be the type of the field for a single constructor of
-- @S@.  More generally, @S@ may be isomorphic to the disjoint union @Either D
-- A@ for some type @D@ representing the other constructors.  In this case,
-- projecting out @A@ from @S@ (pattern-matching on the constructor) may fail,
-- so it has type @S -> Maybe A@.  In the reverse direction we have a function
-- of type @A -> S@ representing the constructor itself.
--
-- A @'Prism'' S A@ captures the structure of @A@ being a constructor of @S@,
-- with the partial projection function given by 'preview' and the constructor
-- function given by 'review'.  For example, for the type @'Either' X Y@ there
-- is a prism for each constructor:
--
-- @
--         '_Left'  :: 'Prism'' ('Either' X Y) X
--         '_Right' :: 'Prism'' ('Either' X Y) Y
-- 'preview' '_Left'  :: 'Either' X Y -> 'Maybe' X
-- 'review'  '_Right' :: Y -> 'Either' X Y
-- @
--
-- === "Optics.Traversal": multiple substructures
--
-- Alternatively, @S@ may "contain" the substructure @A@ a variable number of
-- times.  In this case, the projection function extracts the (possibly zero or
-- many) elements so has type @S -> [A]@, while the update function may take
-- different values for different elements so has type @(A -> A) -> S -> S@
-- (though in fact more general formulations are possible).
--
-- A @'Traversal'' S A@ captures the structure of @A@ being contained in @S@
-- perhaps multiple times, with the list of values given by 'toListOf' and the
-- update function given by 'over' .  For example, for the type @Maybe X@ there
-- is a traversal that may return zero or one element:
--
-- @
--          'traversed' :: 'Traversal'' ('Maybe' X) X
-- 'toListOf' 'traversed' :: 'Maybe' X -> [X]
-- 'over'     'traversed' :: (X -> X) -> 'Maybe' X -> 'Maybe' X
-- @
--
-- (In fact, traversals of at most one element are known as /affine/ traversals,
-- see "Optics.AffineTraversal".)
--
--
-- === In general
--
-- So far we have seen four different kinds of optic or "notions of
-- substructure", and many more are possible.  Observe the important properties
-- they have in common:
--
-- * There are subtyping relationships between different optic kinds.  Any
--   isomorphism is trivially a lens and a prism (with no other fields or
--   constructors, respectively).  Any lens is a traversal (where the list of
--   elements is always a singleton list), and any prism is also a traversal
--   (where there will be zero or one element depending on whether the
--   constructor matches).  This was implicit in the fact that we
--   used the same operators in multiple cases: 'view' gives the projection
--   function of both an isomorphism and a lens, but cannot be applied to a
--   traversal.
--
-- * Optics can be composed. If @S@ is isomorphic to @U@ and @U@ is isomorphic
--   to @A@ then @S@ is isomorphic to @A@, and similarly for other optic kinds.
--
-- * Composition and subtyping interact: a lens and a prism can be composed, by
--   first thinking of them as traverals using the subtyping relationship.  That
--   is, if @S@ has a field @U@, and @U@ has a constructor @A@, then @S@
--   contains zero or one @A@s that we can pick out with a traversal (but in
--   general there is neither a lens from @S@ to @A@ nor a prism).
--
-- * Each optic kind can be described by certain operations it enables. For
--   example lenses support projection and update, while prisms support partial
--   projection and construction.
--
-- * Optics are subject to laws, which are necessary for the operations to make
--   sense.
--
-- The point of the @optics@ library is to capture this common pattern.


-- $abstract #abstract#
--
-- A key principle behind this library is the belief that optics are useful as
-- an abstract concept, and that the purpose of types is to capture abstract
-- concepts and make them useful.  The programmer using optics should be able to
-- think in terms of the abstract interface, rather than the details of the
-- implementation, and implementation choices should (as far as possible) not
-- dictate the interface.
--
-- Each optic kind is identified by a "tag type" (such as 'A_Lens'), which is an
-- empty data type.  The type of the actual optics (such as 'Lens') is obtained
-- by applying the 'Optic' newtype wrapper to the tag type.
--
-- @
-- type 'Lens'  s t a b = 'Optic'  'A_Lens' 'NoIx' s t a b
-- type 'Lens'' s   a   = 'Optic'' 'A_Lens' 'NoIx' s   a
-- @
--
-- 'NoIx' as the second parameter to 'Optic' indicates that the optic is not
-- indexed.  See the "Indexed optics" section below in "Optics#indexed" for
-- further discussion of indexed optics.
--
-- The details of the internal implementation of 'Optic' are hidden behind an
-- abstraction boundary, so that the library can be used without needing to
-- think about the particular implementation choices.
--
--
-- === Specification of optics interfaces
--
-- Each different kind of optic is documented in a separate module describing
-- its abstract interface, in a standard format with at least /formation/,
-- /introduction/, /elimination/, and /well-formedness/ sections.  See "Optic
-- kinds" below in "Optics#optickinds" for a list of these modules.
--
-- * The __formation__ sections contain type definitions. For example
--   "Optics.Lens" defines:
--
--     @
--     -- Type synonym for a type-modifying lens.
--     type 'Lens' s t a b = 'Optic' 'A_Lens' 'NoIx' s t a b
--     @
--
-- * The __introduction__ sections describe the canonical way to construct each
--   particular optic. Continuing with a 'Lens' example:
--
--     @
--     -- Build a lens from a getter and a setter.
--     'lens' :: (s -> a) -> (s -> b -> t) :: 'Lens' s t a b
--     @
--
-- * Correspondingly, the __elimination__ sections show how you can destruct the
--   optic into the pieces from which it was constructed.
--
--     @
--     -- A 'Lens' is a 'Setter' and a 'Getter', therefore you can specialise types to obtain
--     'view' :: 'Lens' s t a b -> s -> a
--     'set'  :: 'Lens' s t a b -> b -> s -> t
--     @
--
-- * The __computation__ rules tie introduction and elimination forms
--   together. These rules are automatically fulfilled by the library (for
--   well-formed optics).
--
--     @
--     'view' ('lens' f g)   s ≡ f s
--     'set'  ('lens' f g) a s ≡ g s a
--     @
--
-- * The __well-formedness__ sections describe the laws that each optic should
--   obey.  As far as possible, all optics provided by the library are
--   well-formed, but in some cases this depends on invariants that cannot be
--   expressed in types.  Ill-formed optics /might/ behave differently from what
--   the computation rules specify.
--
--     For example, a 'Lens' should obey three laws, known as /GetPut/, /PutGet/
--     and /PutPut/.  See the "Optics.Lens" module for their definitions.  The
--     user of the 'lens' introduction form must ensure that these laws are
--     satisfied.
--
-- * Some optic kinds have __additional introduction forms__,
--   __additional elimination forms__ or __combinators__ sections, which give
--   alternative ways to create and use optics of that kind.  In principle these
--   are expressible in terms of the canonical introduction and elimination
--   rules.
--
-- * The __subtyping__ section gives the "tag type" (such as 'A_Lens'), which in
--   particular is accompanied by 'Is' instances that define the subtyping
--   relationship discussed in the following section.
--
--
-- === Subtyping
--
-- There is a subtyping relationship between optics, implemented using
-- typeclasses.  The 'Is' typeclass captures the property that one optic kind
-- can be used as another, and the 'castOptic' function can be used to
-- explicitly cast between optic kinds.  'Is' forms a partial order, represented
-- in the graph below.  For example, a lens can be used as a traversal, so there
-- are arrows from 'Lens' to 'Traversal' (via 'AffineTraversal') and there is an
-- instance of @'Is' 'A_Lens' 'A_Traversal'@.
--
-- Introduction forms (constructors) return a concrete optic kind, while
-- elimination forms (destructors) are generally polymorphic in the optic kind
-- they accept.  This means that it is not normally necessary to explicitly cast
-- between optic kinds.  For example, we have
--
-- @
-- 'view' :: 'Is' k 'A_Getter' => 'Optic'' k is s a -> s -> a
-- @
--
-- so 'view' can be used with isomorphisms or lenses, as these can be converted
-- to a 'Getter'.
--
-- If an explicit cast is needed, you can use 'castOptic'. This arises when you
-- use optics of different kinds in a context that requires them to have the
-- same type. For example @['folded', 'traversed']@ gives a type error (since
-- 'A_Traversal' is not 'A_Fold') but @['folded', 'castOptic' 'traversed']@
-- works.
--
-- The optic kind module (e.g. "Optics.Lens") does not list all ways to
-- construct or use particular the optic kind.  For example, since a 'Lens' is
-- also a 'Traversal', a 'Fold' etc, so you can use 'traverseOf', 'preview' and
-- many other combinators with lenses.
--
--
-- ==== Subtype hierarchy
--
-- This graph gives an overview of the optic kinds and their subtype
-- relationships:
--
-- <<diagrams/optics.png Optics hierarchy>>
--
-- In addition to the optic kinds included in the diagram, there are also
-- indexed variants such as 'IxLens', 'IxGetter', 'IxAffineTraversal',
-- 'IxTraversal', 'IxAffineFold', 'IxFold' and 'IxSetter'.  These are explained
-- in more detail in the "Indexed optics" section below in "Optics#indexed".
--
--
-- === Composition
--
-- Since /optics are not functions/, they cannot be composed with the ('.')
-- operator. Instead there is a separate composition operator ('%'). The
-- composition operator returns the common supertype of its arguments, or
-- generates a type error if the composition does not make sense.
--
-- The optic kind resulting from a composition is the least upper bound (join)
-- of the optic kinds being composed, if it exists.  The 'Join' type family
-- computes the least upper bound given two optic kind tags.  For example the
-- 'Join' of a 'Lens' and a 'Prism' is an 'AffineTraversal'.
--
-- >>> :kind! Join A_Lens A_Prism
-- Join A_Lens A_Prism :: OpticKind
-- = An_AffineTraversal
--
-- The join does not exist for some pairs of optic kinds, which means that they
-- cannot be composed.  For example there is no optic kind above both 'Setter'
-- and 'Fold':
--
-- >>> :kind! Join A_Setter A_Fold
-- Join A_Setter A_Fold :: OpticKind
-- = (TypeError ...)
--
-- >>> :t mapped % folded
-- ...
-- ...A_Setter cannot be composed with A_Fold
-- ...
--
-- The ('Control.Category..') operator from "Control.Category" cannot be used to
-- compose optics either, because it would not support type-changing optics or
-- composing optics of different kinds.

-- $lens_comparison
--
-- The @lens@ package is the best known Haskell library for optics, and
-- established many of the foundations on which the @optics@ package builds (not
-- least in quite a bit of code having been directly ported).  It defines optics
-- based on the /van Laarhoven/ representation, where each optic kind is
-- introduced as a /transparent/ type synonym for a complex polymorphic type,
-- for example:
--
-- @
-- type Lens s t a b = forall f. 'Functor' f => (a -> f b) -> s -> f t
-- @
--
-- In contrast, @optics@ tries to preserve an abstraction boundary between the
-- interface of optics and their implementation.  Optic kinds are expressed
-- directly in the types, as 'Optic' is an /opaque/ newtype:
--
-- @
-- type 'Lens' s t a b = 'Optic' 'A_Lens' 'NoIx' s t a b
-- @
--
-- The choice of representation of 'Optic' is then an implementation detail, not
-- essential for understanding the library.  (In fact, @optics@ uses the
-- /profunctor/ representation rather than the /van Laarhoven/ representation;
-- this affects the optic kinds and operations that can be conveniently
-- supported, but not the essence of the design.)
--
-- Our design choice to use /opaque/ rather than /transparent/ abstractions
-- leads to various consequences, both positive and negative, which are explored
-- in the following subsections.
--
-- == Advantages of the opaque design
--
-- Since the interface is deliberately chosen rather than to some extent
-- determined by the implementation, we are free to choose a more restricted
-- interface where doing so leads to conceptual simplicity.  For example, in
-- @lens@, the 'view' function can be used with a 'Fold' provided the result
-- type has a 'Monoid' instance, and the multiple targets of the 'Fold' will be
-- combined monoidally.  This behaviour can be confusing, so in @optics@ a
-- 'Fold' cannot be silently used as a 'Getter', and we prefer to have 'view'
-- work on 'Getter's and define a separate 'foldOf' operator for use on
-- 'Fold's. (But the 'gview' function is available for users who may prefer
-- otherwise.)
--
-- In general, opaque abstractions lead to better results from type inference
-- (the optic kind is preserved in the inferred type):
--
--     >>> :t traversed % to not
--     traversed % to not
--       :: Traversable t => Optic A_Fold '[] (t Bool) (t Bool) Bool Bool
--
-- Error messages are domain-specific:
--
--     >>> set (to fst)
--     ...
--     ...A_Getter cannot be used as A_Setter
--     ...
--
-- Composing incompatible optics yields a sensible error:
--
--     >>> sets map % to not
--     ...
--     ...A_Setter cannot be composed with A_Getter
--     ...
--
-- Since 'Optic' is a rank-1 type, it is easy to store optics in a
-- datastructure:
--
-- >>> :t [folded, backwards_ folded]
-- [folded, backwards_ folded] :: Foldable f => [Fold (f a) a]
--
-- It is possible to define aliases for optics without the monomorphism
-- restriction spoiling the fun:
--
-- >>> let { myoptic = _1; p = ('x','y') } in (view myoptic p, set myoptic 'c' p)
-- ('x',('c','y'))
--
-- Finally, having an abstract interface gives more freedom of choice in the
-- internal implementation.  If there is a compelling reason to switch to an
-- alternative representation, one can in principle do so without changing the
-- interface.
--
--
-- == Disadvantages of the opaque design
--
-- Since 'Optic' is a newtype, other libraries that wish to define optics must
-- depend upon its definition.  In contrast, with a transparent representation,
-- and since the van Laarhoven representations of lenses and traversals depend
-- only on definitions from @base@, it is possible for libraries to define them
-- without any extra library dependencies (although this does not hold for more
-- advanced optic kinds such as prisms or indexed optics).  To address this, the
-- present library is split into a package @optics-core@, which has a minimal
-- dependency footprint intended for use in libraries, and the
-- \"batteries-included\" @optics@ package for use in applications.
--
-- It is something of an amazing fact that the composition operator for
-- transparent optics is just function composition.  Moreover, since Haskell
-- uses ('.')  for function composition, @lens@ is able to support a pseudo-OOP
-- syntax.  In contrast, @optics@ must use a different composition operator
-- ('%').  'Optic' does not quite form a 'Control.Category.Category', thanks to
-- type-changing optics.
--
-- Rather than emerging naturally from the definitions, opportunities for
-- polymorphism have to be identified in advance and explicitly introduced using
-- type classes.  Similarly, the set of optic kinds and the subtyping
-- relationships between them must be fixed in advance, and cannot be added to
-- in downstream libraries.  Thus in a sense the opaque approach is more
-- restrictive than the transparent one. There are cases in @lens@ where the
-- types work out nicely and permit abstraction-breaking-but-convenient
-- shortcuts, such as applying a 'Traversal' as a 'traverse'-like function,
-- whereas @optics@ requires a call to 'traverseOf'.
--
--
-- == More specific differences
--
-- The sections above set out the major conceptual differences from the @lens@
-- package, and their advantages and disadvantages.  Some more specific design
-- differences, which may be useful for comparison or porting code between the
-- libraries.  This list is no doubt incomplete.
--
-- * The composition operator is ('%') rather than ('.') and is defined as
--   @infixl 9@ instead of @infixr 9@.
--
-- * Fewer operators are provided, and some of them are not exported from the
--   main "Optics" module. Import "Optics.State.Operators" if you want them.
--
-- * The 'view' function and corresponding ('Optics.Operators.^.') operator work
--   only for 'Getter's and have a more restricted type. The equivalent for
--   'Fold's is 'foldOf', and you can use 'preview' for
--   'AffineFold's. Alternatively you can use 'gview' which is more compatible
--   with @view@ from @lens@, but it uses a type class to choose between 'view',
--   'preview' and 'foldOf'.
--
-- * Indexed optics are rather different, as described in the "Indexed optics"
--   section below in "Optics#indexed".  All ordinary optics are
--   "index-preserving", so there is no separate notion of an index-preserving
--   optic.
--
-- * 'Each' provides indexed traversals.
--
-- * @firstOf@ from @lens@ is replaced by 'headOf'.
--
-- * @concatOf@ from @lens@ is omitted in favour of the more general 'foldOf'.
--
-- * 'set'' is a strict version of 'set', not 'set' for type-preserving optics.
--
-- * Numbered lenses for accessing fields of tuples positionally are provided
--   only up to '_9', rather than @_19@.
--
-- * There are four variants of @backwards@ for (indexed) 'Traversal's and
--   'Fold's: 'backwards', 'backwards_', 'ibackwards' and 'ibackwards_'.
--
-- * There is no @Traversal1@ and @Fold1@.
--
-- * There are affine variants of (indexed) traversals and folds
--   ('AffineTraversal', 'AffineFold', 'IxAffineTraversal' and 'IxAffineFold').
--   An affine optic targets at most one value.  Composing a 'Lens' with a
--   'Prism' produces an 'AffineTraversal', so for example @'matching' ('_1' '%'
--   '_Left')@ is well-typed.
--
-- * Functions 'ifiltered' and 'indices' are defined as optic combinators due to
--   restrictions of internal representation.
--
-- * We can't use 'traverse' as an optic directly.  Instead there is a
--   'Traversal' called 'traversed'.  Similarly 'traverseOf' must be used to
--   apply a 'Traversal', rather than simply using it as a function.
--
-- * The 're' combinator produces a different optic kind depending on the kind
--   of the input 'Iso', for example 'Review' reverses to 'Getter' while a
--   reversed 'Iso' is still an 'Iso'.  Thus there is no separate @from@
--   combinator for reversing 'Iso's.
--
-- * 'singular' ('isingular' for indexed optics) doesn't produce a partial lens
--   that might fail with a runtime error, but an affine traversal.
--
-- * '<>' cannot be used to combine 'Fold's, so 'summing' should be used
--   instead.


-- $otherresources
--
-- * <https://skillsmatter.com/skillscasts/10692-through-a-glass-abstractly-lenses-and-the-power-of-abstraction Through a Glass, Abstractly: Lenses and the Power of Abstraction> a talk on the principles behind this library with <https://github.com/well-typed/optics/raw/master/Talk.pdf accompanying slides> by Adam Gundry (but note that the design details of @optics@ have changed substantially since this talk was given)
--
-- * <https://skillsmatter.com/skillscasts/12360-profunctors-and-data-accessors Profunctors and Data Accessors> a talk on basics of profunctors and how they relate to data accessors such as lenses, prisms and traversals by Andrzej Rybczak
--
-- * <https://www.cs.ox.ac.uk/people/jeremy.gibbons/publications/poptics.pdf Profunctor Optics: Modular Data Accessors> a paper by Matthew Pickering, Jeremy Gibbons and Nicolas Wu
--
-- * <https://oleg.fi/gists/posts/2017-04-18-glassery.html Glassery> and <https://oleg.fi/gists/posts/2017-04-26-indexed-poptics.html Indexed Profunctor optics>, blog posts by Oleg Grenrus
--
-- * The <https://hackage.haskell.org/package/lens lens> package by Edward Kmett and contributors


-- $basicusage
--
-- To get started, you can just add @optics@ as a dependency to your @.cabal@
-- file, and then:
--
-- @
-- import "Optics"
-- @
--
-- If you are writing a library for which it is important to keep the dependency
-- footprint minimal, you may wish to depend upon @optics-core@ instead (and
-- perhaps @optics-extra@ or @optics-th@), and then:
--
-- @
-- import "Optics.Core"
-- @

-- $indexed #indexed#
--
-- The @optics@ library also provides indexed optics, which provide
-- an additional /index/ value in mappings:
--
-- @
-- 'over'  :: 'Setter'     s t a b -> (a -> b)      -> s -> t
-- 'iover' :: 'IxSetter' i s t a b -> (i -> a -> b) -> s -> t
-- @
--
-- Note that there aren't any laws about indices.
-- Especially in compositions the same index may occur multiple times.
--
-- The machinery builds on indexed variants of 'Functor', 'Foldable', and 'Traversable' classes:
-- 'FunctorWithIndex', 'FoldableWithIndex' and 'TraversableWithIndex' respectively.
-- There are instances for types in the boot libraries.
--
-- @
-- class ('FoldableWithIndex' i t, 'Traversable' t)
--   => 'TraversableWithIndex' i t | t -> i where
--     'itraverse' :: 'Applicative' f => (i -> a -> f b) -> t a -> f (t b)
-- @
--
-- Indexed optics /can/ be used as regular ones, i.e. indexed optics
-- gracefully downgrade to regular ones.
--
-- >>> toListOf ifolded "foo"
-- "foo"
--
-- >>> itoListOf ifolded "foo"
-- [(0,'f'),(1,'o'),(2,'o')]
--
-- But there is also a combinator 'noIx' to explicitly erase indices:
--
-- >>> :t (ifolded % simple)
-- (ifolded % simple)
--   :: FoldableWithIndex i f => Optic A_Fold '[i] (f b) (f b) b b
--
-- >>> :t noIx (ifolded % simple)
-- noIx (ifolded % simple)
--   :: FoldableWithIndex i f => Optic A_Fold NoIx (f b) (f b) b b
--
-- @
-- λ> :t noIx (ifolded % ifolded)
-- noIx (ifolded % ifolded)
--   :: (FoldableWithIndex i1 f1, FoldableWithIndex i2 f2) =>
--      Optic A_Fold NoIx (f1 (f2 b)) (f1 (f2 b)) b b
-- @
--
-- As the example above illustrates, regular and indexed optics have the same
-- tag in the first parameter of 'Optic', in this case 'A_Fold'.  Regular optics
-- simply don't have any indices.  The provided type aliases 'IxLens',
-- 'IxGetter', 'IxAffineTraversal', 'IxAffineFold', 'IxTraversal', 'IxFold' and
-- 'IxSetter' are variants with a single index. In general, the second parameter
-- of the 'Optic' newtype is a type-level list of indices, which will typically
-- be 'NoIx' (the empty index list) or @('WithIx' i)@ (a singleton list).
--
-- When two optics are composed with ('%'), the index lists are concatenated.
-- Thus composing an unindexed optic with an indexed optic preserves the
-- indices, or composing two indexed optics retains both indices:
--
-- @
-- λ> :t (ifolded % ifolded)
-- (ifolded % ifolded)
--   :: (FoldableWithIndex i1 f1, FoldableWithIndex i2 f2) =>
--      Optic A_Fold '[i1, i2] (f1 (f2 b)) (f1 (f2 b)) b b
-- @
--
-- In order to use such an optic, it is necessary to flatten the indices into a
-- single index using 'icompose' or a similar function:
--
-- @
-- λ> :t icompose (,) (ifolded % ifolded)
-- icompose (,) (ifolded % ifolded)
--   :: (FoldableWithIndex i1 f1, FoldableWithIndex i2 f2) =>
--      Optic A_Fold (WithIx (i1, i2)) (f1 (f2 b)) (f1 (f2 b)) b b
-- @
--
-- For example:
--
-- >>> itoListOf (icompose (,) (ifolded % ifolded)) [['a','b'], ['c', 'd']]
-- [((0,0),'a'),((0,1),'b'),((1,0),'c'),((1,1),'d')]
--
-- Alternatively, you can use one of the ('<%') or ('%>') operators to compose
-- indexed optics and pick the index to retain, or the ('<%>') operator to
-- retain a pair of indices:
--
-- >>> itoListOf (ifolded <% ifolded) [['a','b'], ['c', 'd']]
-- [(0,'a'),(0,'b'),(1,'c'),(1,'d')]
--
-- >>> itoListOf (ifolded %> ifolded) [['a','b'], ['c', 'd']]
-- [(0,'a'),(1,'b'),(0,'c'),(1,'d')]
--
-- >>> itoListOf (ifolded <%> ifolded) [['a','b'], ['c', 'd']]
-- [((0,0),'a'),((0,1),'b'),((1,0),'c'),((1,1),'d')]
--
-- In the diagram below, the optics hierarchy is amended with these (singly) indexed variants (in blue).
-- Orange arrows mean
-- "can be used as one, assuming it's composed with any optic below the
-- orange arrow first". For example. '_1' is not an indexed fold, but
-- @'itraversed' % '_1'@ is, because it's an indexed traversal, so it's
-- also an indexed fold.
--
-- >>> let fst' = _1 :: Lens (a, c) (b, c) a b
-- >>> :t fst' % itraversed
-- fst' % itraversed
--   :: TraversableWithIndex i f =>
--      Optic A_Traversal '[i] (f a, c) (f b, c) a b
--
-- <<diagrams/indexedoptics.png Indexed Optics>>

-- $cheatsheet
--
-- The following table summarizes the key optic kinds and their combinators.
-- It is based on a [similar table for the lens
-- package](https://github.com/ekmett/lens/wiki/operators).
--
-- A 'Lens' can be used as a 'Getter', 'Setter', 'Fold' and 'Traversal'.
--
-- +-------------------+---------------+---------------------------------------------------+
-- |Combinator         |Indexed        |Notes                                              |
-- +===================+===============+===================================================+
-- |                                                                                       |
-- +-------------------+---------------+---------------------------------------------------+
-- |__Getters__                                                                            |
-- +-------------------+---------------+---------------------------------------------------+
-- |'to'               |'ito'          |Build a 'Getter' / 'IxGetter' from a plain         |
-- |                   |               |function.                                          |
-- +-------------------+---------------+---------------------------------------------------+
-- |'view' / '^.'      |'iview'        |View a single target.                              |
-- +-------------------+---------------+---------------------------------------------------+
-- |'views'            |'iviews'       |View after applying a function.                    |
-- +-------------------+---------------+---------------------------------------------------+
-- |                                                                                       |
-- +-------------------+---------------+---------------------------------------------------+
-- |__Setters__                                                                            |
-- +-------------------+---------------+---------------------------------------------------+
-- |'sets'             |'isets'        |Build a 'Setter' / 'IxSetter' from an update       |
-- |                   |               |function.                                          |
-- +-------------------+---------------+---------------------------------------------------+
-- |'mapped'           |'imapped'      |Build a 'Setter' from the 'Functor' class, or an   |
-- |                   |               |'IxSetter' from 'FunctorWithIndex'.                |
-- +-------------------+---------------+---------------------------------------------------+
-- |'set' / '.~'       |'iset'         |Replace target(s) with value.                      |
-- +-------------------+---------------+---------------------------------------------------+
-- |'over' / '%~'      |'iover'        |Modify target(s) by applying a function.           |
-- +-------------------+---------------+---------------------------------------------------+
-- |                                                                                       |
-- +-------------------+---------------+---------------------------------------------------+
-- |__Folds__          |                                                                   |
-- +-------------------+---------------+---------------------------------------------------+
-- |'folded'           | 'ifolded'     |Build a 'Fold' from the 'Foldable' class, or an    |
-- |                   |               |'IxFold' from 'FoldableWithIndex'.                 |
-- +-------------------+---------------+---------------------------------------------------+
-- |'toListOf' / '^..' |'itoListOf'    |Return a list of the target(s).                    |
-- +-------------------+---------------+---------------------------------------------------+
-- |                                                                                       |
-- +-------------------+---------------+---------------------------------------------------+
-- |__AffineFolds__                                                                        |
-- +-------------------+---------------+---------------------------------------------------+
-- | 'afolding'        |'iafolding'    |Build an 'AffineFold' / 'IxAffineFold' from a      |
-- |                   |               |partial function.                                  |
-- +-------------------+---------------+---------------------------------------------------+
-- |'preview' / '^?'   |'ipreview'     |Match the target or return 'Nothing'.              |
-- +-------------------+---------------+---------------------------------------------------+
-- |'previews'         |'ipreviews'    |Preview after applying a function.                 |
-- +-------------------+---------------+---------------------------------------------------+
-- |                                                                                       |
-- +-------------------+---------------+---------------------------------------------------+
-- |__Traversals__                                                                         |
-- +-------------------+---------------+---------------------------------------------------+
-- |'traversed'        |'itraversed'   |Build a 'Traversal' from the 'Traversable' class,  |
-- |                   |               |or an 'IxTraversal' from 'TraversableWithIndex'.   |
-- +-------------------+---------------+---------------------------------------------------+
-- |'traverseOf'       |'itraverseOf'  |Update target(s) with an 'Applicative'.            |
-- +-------------------+---------------+---------------------------------------------------+
-- |                                                                                       |
-- +-------------------+---------------+---------------------------------------------------+
-- |__Prisms__                                                                             |
-- +-------------------+---------------+---------------------------------------------------+
-- |'prism'            |               |Build a 'Prism' from a constructor and matcher.    |
-- +-------------------+---------------+---------------------------------------------------+
-- |'review' / '#'     |               |Use a 'Prism' to construct the sum type.           |
-- +-------------------+---------------+---------------------------------------------------+
--
-- For setting/modifying using a 'Setter', a variety of combinators are available
-- in "Optics.State" and "Optics.State.Operators".  The latter are not exported
-- by the main "Optics" module, so must be imported explicitly.
--
-- +--------------+-----------------+-------------------------------------------+------------------------------+-------------------------------+-------------------------------------------+
-- | Lazy         | Strict          | Stateful                                  | Stateful returning new value | Stateful returning old value  | Notes                                     |
-- +==============+=================+===========================================+==============================+===============================+===========================================+
-- |'set' / '.~'  | 'set'' / '!~'   | 'assign' / 'Optics.State.Operators..='    | 'Optics.State.Operators.<.=' | 'Optics.State.Operators.<<.=' | Replace target(s) with value.             |
-- +--------------+-----------------+-------------------------------------------+------------------------------+-------------------------------+-------------------------------------------+
-- |'over' / '%~' | 'over'' / '%!~' | 'modifying' / 'Optics.State.Operators.%=' | 'Optics.State.Operators.<%=' | 'Optics.State.Operators.<<%=' | Modify target(s) by applying a function.  |
-- +--------------+-----------------+-------------------------------------------+------------------------------+-------------------------------+-------------------------------------------+
-- | '?~'         | '?!~'           | 'Optics.State.Operators.?='               | 'Optics.State.Operators.<?=' | 'Optics.State.Operators.<<?=' | Replace target(s) with 'Just' a value.    |
-- +--------------+-----------------+-------------------------------------------+------------------------------+-------------------------------+-------------------------------------------+

-- $setup
-- >>> import Control.Monad.Reader
-- >>> import Control.Monad.State
-- >>> import Data.Functor.Identity
-- >>> import qualified Data.IntSet as IntSet
-- >>> import qualified Data.Map as Map
-- >>> import Optics.State.Operators