-- | -- Module : Lens.Explicit -- Copyright : (c) Justus SagemΓΌller 2017 -- License : GPL v3 -- -- Maintainer : (@) sagemueller $ geo.uni-koeln.de -- Stability : experimental -- Portability : portable -- {-# LANGUAGE Rank2Types #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE UnicodeSyntax #-} module Lens.Explicit ( -- * Lenses and other optics -- ** Getters to, Getter, AGetter, (^.) -- ** Setters , sets, Setter, ASetter, (%~), (.~), Setter' -- ** Lenses , lens, Lens, ALens, (%%~), Lens' -- ** Prisms , prism, Prism, APrism, matching, Prism' -- ** Reviews , unto, Review, AReview, re -- ** Isomorphisms , iso, Iso, AnIso, from, under, Iso' -- ** Folds , folded, Fold, AFold, foldMapOf -- ** Traversals , traversed, Traversal, ATraversal, traverseOf, Traversal' -- * Composition -- $composInfo , Cat.id, (Cat..), (&) ) where import qualified Lens.Explicit.Core as Π– import Lens.Explicit.Core (OpticC(..)) import Prelude hiding (id, (.)) import Control.Category as Cat import Data.Function hiding (id, (.)) infixl 8 ^. (^.) :: 𝑠 -> AGetter 𝑠 π‘Ž -> π‘Ž s ^. Π–.Equality = s s ^. OpticC (Π–.Getter f) = f s to :: (𝑠 -> π‘Ž) -> Getter 𝑠 π‘Ž to = OpticC . Π–.to -- | Getters are basically just functions: accessors which can read a field (type @π‘Ž@) -- of some data structure (type @𝑠@), but not write back anything to the structure. type Getter 𝑠 π‘Ž = Π–.Getter 𝑠 𝑠 π‘Ž π‘Ž -- | A getter that may also have additional capabilities, e.g. a 'Lens'. type AGetter 𝑠 π‘Ž = Π–.AGetter 𝑠 π‘Ž infixr 4 %~, .~ (%~) :: ASetter 𝑠 𝑑 π‘Ž 𝑏 -> (π‘Ž -> 𝑏) -> 𝑠 -> 𝑑 Π–.Equality %~ m = m OpticC (Π–.Setter f) %~ m = f m (.~) :: ASetter 𝑠 𝑑 π‘Ž 𝑏 -> 𝑏 -> 𝑠 -> 𝑑 a .~ b = a %~ const b sets :: ((π‘Ž -> 𝑏) -> 𝑠 -> 𝑑) -> Setter 𝑠 𝑑 π‘Ž 𝑏 sets = OpticC . Π–.sets -- | Setters are accessors that can write/manipulate a field (type @π‘Ž@) -- of a data structure (type @𝑠@), but not retrieve any results. -- -- The manipulation might result in a type @𝑏@ for the field different from -- the original @π‘Ž@, in that case, the data structure will likewise change -- change its type from @𝑠@ to @𝑑@. type Setter 𝑠 𝑑 π‘Ž 𝑏 = Π–.Setter 𝑠 𝑑 π‘Ž 𝑏 -- | A setter that may also have additional capabilities, e.g. a 'Lens'. type ASetter 𝑠 𝑑 π‘Ž 𝑏 = Π–.ASetter 𝑠 𝑑 π‘Ž 𝑏 type Setter' 𝑠 π‘Ž = Setter 𝑠 𝑠 π‘Ž π‘Ž infixr 4 %%~ (%%~) :: Functor 𝑓 => ALens 𝑠 𝑑 π‘Ž 𝑏 -> (π‘Ž -> 𝑓 𝑏) -> 𝑠 -> 𝑓 𝑑 (%%~) Π–.Equality Ο„ s = Ο„ s (%%~) (OpticC (Π–.Lens f Ο†)) Ο„ s = fmap (Ο† s) . Ο„ $ f s lens :: (𝑠 -> π‘Ž) -> (𝑠 -> 𝑏 -> 𝑑) -> Lens 𝑠 𝑑 π‘Ž 𝑏 lens f g = OpticC $ Π–.lens f g -- | Lenses combine the capabilities of 'Getter' and 'Setter' – they have β€œread and -- write permission”, i.e. you can use them with the '^.' as well as '.~' and '%~' -- operators. -- -- This is the standard type of record-field accessor. type Lens 𝑠 𝑑 π‘Ž 𝑏 = Π–.Lens 𝑠 𝑑 π‘Ž 𝑏 -- | A lens that may also have additional capabilities, e.g. an 'Iso'. type ALens 𝑠 𝑑 π‘Ž 𝑏 = Π–.ALens 𝑠 𝑑 π‘Ž 𝑏 type Lens' 𝑠 π‘Ž = Lens 𝑠 𝑠 π‘Ž π‘Ž prism :: (𝑏 -> 𝑑) -> (𝑠 -> Either 𝑑 π‘Ž) -> Prism 𝑠 𝑑 π‘Ž 𝑏 prism f g = OpticC $ Π–.prism f g matching :: APrism 𝑠 𝑑 π‘Ž 𝑏 -> 𝑠 -> Either 𝑑 π‘Ž matching Π–.Equality = Right matching (OpticC (Π–.Prism _ f)) = f -- | Prisms are the categorical dual of lenses: whilst a lens /focuses/ in on a field -- of a record structure (i.e. of a product type), a prism /distinguishes/ constructors -- of an alternative (i.e. of a sum type). type Prism 𝑠 𝑑 π‘Ž 𝑏 = Π–.Prism 𝑠 𝑑 π‘Ž 𝑏 -- | A prism that may also have additional capabilities, e.g. an 'Iso'. type APrism 𝑠 𝑑 π‘Ž 𝑏 = Π–.APrism 𝑠 𝑑 π‘Ž 𝑏 type Prism' 𝑠 π‘Ž = Prism 𝑠 𝑠 π‘Ž π‘Ž unto :: (𝑏 -> 𝑑) -> Review 𝑑 𝑏 unto = OpticC . Π–.unto re :: Π–.FromGetter c => AReview 𝑑 𝑏 -> Π–.Optic c 𝑑 𝑑 𝑏 𝑏 re Π–.Equality = Π–.Equality re (OpticC (Π–.Review f)) = OpticC $ Π–.to f -- | Reviews are basically like constructors in languages without pattern matching: -- /prisms without read permission/. Because such a constructor is just a function, -- and getters are functions too, you can also consider a review as a β€œreverse 'Getter'”. type Review 𝑑 𝑏 = Π–.Review 𝑑 𝑑 𝑏 𝑏 -- | A review that may also have additional capabilities, e.g. a 'Prism'. type AReview 𝑑 𝑏 = Π–.AReview 𝑑 𝑏 under :: AnIso 𝑠 𝑑 π‘Ž 𝑏 -> (𝑑 -> 𝑠) -> 𝑏 -> π‘Ž under Π–.Equality g = g under (OpticC (Π–.Iso f Ο†)) g = f . g . Ο† from :: AnIso 𝑠 𝑑 π‘Ž 𝑏 -> Iso 𝑏 π‘Ž 𝑑 𝑠 from Π–.Equality = Π–.Equality from (OpticC (Π–.Iso f Ο†)) = iso Ο† f iso :: (𝑠 -> π‘Ž) -> (𝑏 -> 𝑑) -> Iso 𝑠 𝑑 π‘Ž 𝑏 iso f g = OpticC $ Π–.iso f g -- | Isomorphisms are 1-1 mappings. This can be seen as a 'Lens' which focuses on -- a field that contains the entire information of the data structure, or as a -- prism that distinguishes the only constructor available. type Iso 𝑠 𝑑 π‘Ž 𝑏 = Π–.Iso 𝑠 𝑑 π‘Ž 𝑏 -- | An isomorphism that could also have additional capabilities. (This is somewhat -- theoretical, since isomorphism is already the most powerful relation we describe.) type AnIso 𝑠 𝑑 π‘Ž 𝑏 = Π–.AnIso 𝑠 𝑑 π‘Ž 𝑏 type Iso' 𝑠 π‘Ž = Iso 𝑠 𝑠 π‘Ž π‘Ž traverseOf :: Applicative 𝑓 => ATraversal 𝑠 𝑑 π‘Ž 𝑏 -> (π‘Ž -> 𝑓 𝑏) -> 𝑠 -> 𝑓 𝑑 traverseOf Π–.Equality = id traverseOf (OpticC (Π–.Traversal y)) = y traversed :: (βˆ€ 𝑓 . Applicative 𝑓 => (π‘Ž -> 𝑓 𝑏) -> 𝑠 -> 𝑓 𝑑) -> Traversal 𝑠 𝑑 π‘Ž 𝑏 traversed f = OpticC (Π–.traversed f) -- | Traversals can 'Fold' over the fields of a data structure, and additionally -- reconstruct the structure with modified fields. type Traversal 𝑠 𝑑 π‘Ž 𝑏 = Π–.Traversal 𝑠 𝑑 π‘Ž 𝑏 -- | A traversal that may also have additional capabilities, e.g. a 'Lens' or 'Prism'. type ATraversal 𝑠 𝑑 π‘Ž 𝑏 = Π–.ATraversal 𝑠 𝑑 π‘Ž 𝑏 type Traversal' 𝑠 π‘Ž = Traversal 𝑠 𝑠 π‘Ž π‘Ž foldMapOf :: Monoid π‘Ÿ => AFold 𝑠 π‘Ž -> (π‘Ž -> π‘Ÿ) -> 𝑠 -> π‘Ÿ foldMapOf Π–.Equality = id foldMapOf (OpticC (Π–.Fold y)) = y folded :: Foldable 𝑓 => Fold (𝑓 π‘Ž) π‘Ž folded = OpticC $ Π–.folded -- | Folds access fields that may occur multiple times in the data structure, -- or not at all, such as the elements of a list. Like 'Getter', they don't -- have β€œwrite permission”. type Fold 𝑠 π‘Ž = Π–.Fold 𝑠 𝑠 π‘Ž 𝑠 -- | A fold that may also have additional capabilities, e.g. a 'Getter' or 'Traversal'. type AFold 𝑠 π‘Ž = Π–.AFold 𝑠 𝑠 π‘Ž 𝑠 -- $composInfo -- Optics compose β€œOO style”, from left to right. For example, given -- -- @ -- data Foo = Foo Int String -- foostr :: 'Lens'' Foo String -- data Bar = Bar Foo Bool -- barfoo :: 'Lens'' Bar Foo -- -- hideout :: bar -- hideout = Bar (Foo 7 "I'm here!") True -- @ -- -- you can use -- -- @ -- hideout '^.' barfoo'.'foostr -- @ -- -- to look up the @"I'm here!"@ string. -- -- Optics of different power can directly be composed with each other, for instance, -- in the example above it would have also been sufficient if -- -- @ -- barfoo :: 'Getter' Bar Foo -- @