diff --git a/profunctor-optics/profunctor-optics.cabal b/profunctor-optics/profunctor-optics.cabal index 946d856..c42c02f 100644 --- a/profunctor-optics/profunctor-optics.cabal +++ b/profunctor-optics/profunctor-optics.cabal @@ -36,13 +36,13 @@ source-repository head library exposed-modules: Data.Tuple.Optic - Data.Either.Optic - + Data.Profunctor.Rep.Foldl Data.Profunctor.Rep.Foldl1 Data.Profunctor.Optic Data.Profunctor.Optic.Types + Data.Profunctor.Optic.Pattern Data.Profunctor.Optic.Property Data.Profunctor.Optic.Carrier Data.Profunctor.Optic.Combinator @@ -83,20 +83,20 @@ library ghc-options: -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates build-depends: - base >= 4.9 && < 5.0 - , adjunctions >= 4.4 && < 5.0 - , coapplicative >= 0.0.1 && < 0.2 - , distributive >= 0.3 && < 1 - , lawz >= 0.1.1 && < 0.2 - , foldl >= 1.4.5 && < 1.5 - , mtl >= 2.0.1 && < 2.3 - , newtype-generics >= 0.5.3 && < 0.6 - , profunctors >= 5.4 && < 6 - , rings >= 0.1.3 && < 0.1.4 - , semigroupoids >= 5 && < 6 - , tagged >= 0.4.4 && < 1 - , transformers >= 0.5 && < 0.6 - + base >= 4.10 && < 5.0 + , adjunctions >= 4.4 + , coapplicative >= 0.0.1 + , distributive >= 0.3 + , lawz >= 0.1.1 + , mtl >= 2.0.1 + , mono-traversable >= 1.0.13.0 + , newtype-generics >= 0.5.3 + , profunctors >= 5.4 + , semigroupoids >= 5 + , strict >= 0.3.2 + , tagged >= 0.4.4 + , transformers >= 0.5 + , these-skinny >= 0.7.4 executable doctest main-is: doctest.hs @@ -107,7 +107,8 @@ executable doctest build-depends: base - , doctest >= 0.8 + , bytestring + , doctest >= 0.8 , mtl , profunctor-optics diff --git a/profunctor-optics/src/Data/Either/Optic.hs b/profunctor-optics/src/Data/Either/Optic.hs deleted file mode 100644 index 2cf2e88..0000000 --- a/profunctor-optics/src/Data/Either/Optic.hs +++ /dev/null @@ -1,27 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE QuantifiedConstraints #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE TypeOperators #-} -{-# LANGUAGE TypeFamilies #-} -module Data.Either.Optic ( - coswapped - , coassociated - , left - , right -) where - -import Data.Profunctor.Optic.Import -import Data.Profunctor.Optic.Iso -import Data.Profunctor.Optic.Prism - --- | 'Prism' into the `Left` constructor of `Either`. --- -left :: Prism (a + c) (b + c) a b -left = left' - --- | 'Prism' into the `Right` constructor of `Either`. --- -right :: Prism (c + a) (c + b) a b -right = right' diff --git a/profunctor-optics/src/Data/Profunctor/Optic.hs b/profunctor-optics/src/Data/Profunctor/Optic.hs index 6b9a871..ff7d84d 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic.hs @@ -5,10 +5,14 @@ {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE DeriveFunctor #-} module Data.Profunctor.Optic ( module Types , module Carrier , module Combinator + , module Pattern + , module Property , module Iso , module Prism , module Lens @@ -24,6 +28,8 @@ module Data.Profunctor.Optic ( import Data.Profunctor.Optic.Types as Types import Data.Profunctor.Optic.Carrier as Carrier import Data.Profunctor.Optic.Combinator as Combinator +import Data.Profunctor.Optic.Pattern as Pattern +import Data.Profunctor.Optic.Property as Property import Data.Profunctor.Optic.Iso as Iso import Data.Profunctor.Optic.Prism as Prism import Data.Profunctor.Optic.Lens as Lens diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Carrier.hs b/profunctor-optics/src/Data/Profunctor/Optic/Carrier.hs index d4a6972..5c6f007 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Carrier.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Carrier.hs @@ -7,61 +7,96 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DeriveGeneric #-} module Data.Profunctor.Optic.Carrier ( - -- * Carrier types + -- * Iso carrier AIso , AIso' + -- * Prism carriers , APrism , APrism' - , ACoprism - , ACoprism' + -- * Lens carriers , ALens - , ALens' , AColens + , AIxlens + , ACxlens + , ALens' , AColens' - , AGrate - , AGrate' + , AIxlens' + , ACxlens' + -- * Traversal carriers , ATraversal0 - , ATraversal0' - , ATraversal - , ATraversal' , ACotraversal0 - , ACotraversal0' + , ATraversal , ACotraversal + , AIxtraversal0 + , AIxtraversal + , ACxtraversal + , ATraversal0' + , ACotraversal0' + , ATraversal' , ACotraversal' + , AIxtraversal0' + , AIxtraversal' + , ACxtraversal' + -- * Fold carriers , AFold0 , AFold , ACofold + , AIxfold0 + , AIxfold + , ACxfold + -- * Machine carriers , AFoldl - , AFoldl' , AFoldl1 + , ACxfoldl + , ACxfoldl1 + , AFoldl' , AFoldl1' + , ACxfoldl' + , ACxfoldl1' + -- * Setter carriers , ASetter - , ASetter' , AResetter + , AIxsetter + , ARxsetter + , ASetter' , AResetter' + , AIxsetter' + , ARxsetter' + -- * View carriers , AView , AReview + , AIxview + , ARxview -- * Carrier operators , withIso , withPrism - , withCoprism , withLens + , withIxlens , withColens - , withGrate + , withCxlens , withAffine + , withAffine' , withCoaffine -- * Carrier profunctors , IsoRep(..) , PrismRep(..) - , CoprismRep(..) , LensRep(..) + , IxlensRep(..) , ColensRep(..) - , GrateRep(..) + , CxlensRep(..) , AffineRep(..) , CoaffineRep(..) , Star(..) , Costar(..) , Tagged(..) + -- * Paired + , Paired(..) + , paired + , fromTambara + -- * Split + , Split(..) + , split + , fromTambaraSum -- * Index , Index(..) , vals @@ -69,6 +104,9 @@ module Data.Profunctor.Optic.Carrier ( -- * Coindex , Coindex(..) , trivial + , noindex + , coindex + , (<<<<) -- * Conjoin , Conjoin(..) ) where @@ -79,11 +117,12 @@ import Data.Profunctor.Types as Export (Star(..), Costar(..)) import Data.Bifunctor as B import Data.Function import Data.Monoid(Alt(..)) +import Data.Profunctor.Choice +import Data.Profunctor.Strong import Data.Profunctor.Optic.Types import Data.Profunctor.Optic.Import import Data.Profunctor.Rep (unfirstCorep) import GHC.Generics (Generic) - import qualified Control.Arrow as A import qualified Control.Category as C import qualified Data.Profunctor.Rep.Foldl as L @@ -101,75 +140,145 @@ import qualified Data.Profunctor.Rep.Foldl1 as L1 -- >>> :load Data.Profunctor.Optic --------------------------------------------------------------------- --- Carriers +-- Iso carriers --------------------------------------------------------------------- type AIso s t a b = Optic (IsoRep a b) s t a b type AIso' s a = AIso s s a a +--------------------------------------------------------------------- +-- Prism carriers +--------------------------------------------------------------------- + type APrism s t a b = Optic (PrismRep a b) s t a b type APrism' s a = APrism s s a a -type ACoprism s t a b = Optic (CoprismRep a b) s t a b - -type ACoprism' s a = ACoprism s s a a +--------------------------------------------------------------------- +-- Lens carriers +--------------------------------------------------------------------- type ALens s t a b = Optic (LensRep a b) s t a b +type AColens s t a b = Optic (ColensRep a b) s t a b + +type AIxlens k s t a b = Ixoptic (IxlensRep k a b) k s t a b + +type ACxlens k s t a b = Cxoptic (CxlensRep k a b) k s t a b + type ALens' s a = ALens s s a a -type AColens s t a b = Optic (ColensRep a b) s t a b +type AColens' s a = AColens s s a a -type AColens' s a = AColens s s a a +type AIxlens' k s a = AIxlens k s s a a -type AGrate s t a b = Optic (GrateRep a b) s t a b +type ACxlens' k s a = ACxlens k s s a a -type AGrate' s a = AGrate s s a a +--------------------------------------------------------------------- +-- Traversal carriers +--------------------------------------------------------------------- type ATraversal0 s t a b = Optic (AffineRep a b) s t a b -type ATraversal0' s a = ATraversal0 s s a a +type ACotraversal0 s t a b = Optic (CoaffineRep a b) s t a b type ATraversal f s t a b = Optic (Star f) s t a b -type ATraversal' f s a = ATraversal f s s a a +type ACotraversal f s t a b = Optic (Costar f) s t a b -type ACotraversal0 s t a b = Optic (CoaffineRep a b) s t a b +type AIxtraversal0 k s t a b = Ixoptic (AffineRep a b) k s t a b + +type AIxtraversal f k s t a b = Ixoptic (Star f) k s t a b + +type ACxtraversal f k s t a b = Cxoptic (Costar f) k s t a b + +type ATraversal0' s a = ATraversal0 s s a a type ACotraversal0' s a = ACotraversal0 s s a a -type ACotraversal f s t a b = Optic (Costar f) s t a b +type ATraversal' f s a = ATraversal f s s a a type ACotraversal' f s a = ACotraversal f s s a a +type AIxtraversal0' k s a = AIxtraversal0 k s s a a + +type AIxtraversal' f k s a = AIxtraversal f k s s a a + +type ACxtraversal' f k t b = ACxtraversal f k t t b b + +--------------------------------------------------------------------- +-- Fold carriers +--------------------------------------------------------------------- + type AFold0 r s a = AFold ((Alt Maybe r)) s a type AFold r s a = ATraversal' (Const r) s a type ACofold r t b = ACotraversal' (Const r) t b -type AFoldl s t a b = Optic L.Fold s t a b +type AIxfold0 r k s a = AIxfold (Alt Maybe r) k s a + +type AIxfold r k s a = AIxtraversal' (Const r) k s a -type AFoldl' s a = AFoldl s s a a +type ACxfold r k t b = ACxtraversal' (Const r) k t b + +--------------------------------------------------------------------- +-- Machine carriers +--------------------------------------------------------------------- + +type AFoldl s t a b = Optic L.Foldl s t a b type AFoldl1 s t a b = Optic L1.Foldl1 s t a b -type AFoldl1' s a = AFoldl1 s s a a +type ACxfoldl k s t a b = Cxoptic L.Foldl k s t a b + +type ACxfoldl1 k s t a b = Cxoptic L1.Foldl1 k s t a b + +type AFoldl' t b = AFoldl t t b b + +type AFoldl1' t b = AFoldl1 t t b b + +type ACxfoldl' k t b = ACxfoldl k t t b b + +type ACxfoldl1' k t b = ACxfoldl1 k t t b b + +--------------------------------------------------------------------- +-- Setter carriers +--------------------------------------------------------------------- + +type ASetter s t a b = ATraversal Identity s t a b + +type AResetter s t a b = ACotraversal Identity s t a b + +type AIxsetter k s t a b = AIxtraversal Identity k s t a b -type ASetter f s t a b = ATraversal f s t a b +type ARxsetter k s t a b = ACxtraversal Identity k s t a b -type ASetter' f s a = ASetter f s s a a +type ASetter' s a = ASetter s s a a -type AResetter f s t a b = ACotraversal f s t a b +type AResetter' s a = AResetter s s a a -type AResetter' f s a = AResetter f s s a a +type AIxsetter' k s a = AIxsetter k s s a a -type AView r s a = ATraversal' (Const r) s a +type ARxsetter' k t b = ARxsetter k t t b b + +--------------------------------------------------------------------- +-- View carriers +--------------------------------------------------------------------- + +type AView r s a = AFold r s a type AReview t b = Optic' Tagged t b +type AIxview k s a = AIxfold (Maybe k, a) k s a + +type ARxview k t b = Cxoptic' Tagged k t b + +--------------------------------------------------------------------- +-- Carrier operators +--------------------------------------------------------------------- + -- | Extract the two functions that characterize an 'Iso'. -- withIso :: AIso s t a b -> ((s -> a) -> (b -> t) -> r) -> r @@ -182,27 +291,31 @@ withPrism :: APrism s t a b -> ((s -> t + a) -> (b -> t) -> r) -> r withPrism o f = case o (PrismRep Right id) of PrismRep g h -> f g h {-# INLINE withPrism #-} --- | Extract the two functions that characterize a 'Coprism'. --- -withCoprism :: ACoprism s t a b -> ((s -> a) -> (b -> a + t) -> r) -> r -withCoprism o f = case o (CoprismRep id Right) of CoprismRep g h -> f g h - -- | Extract the two functions that characterize a 'Lens'. -- withLens :: ALens s t a b -> ((s -> a) -> (s -> b -> t) -> r) -> r withLens o f = case o (LensRep id (flip const)) of LensRep x y -> f x y {-# INLINE withLens #-} --- | Extract the two functions that characterize a 'Colens'. +-- | Extract the two functions that characterize a 'Ixlens'. -- -withColens :: AColens s t a b -> ((b -> s -> a) -> (b -> t) -> r) -> r -withColens l f = case l (ColensRep (flip const) id) of ColensRep x y -> f x y +-- @since 0.0.3 +withIxlens :: Monoid k => AIxlens k s t a b -> ((s -> (k , a)) -> (s -> b -> t) -> r) -> r +withIxlens o f = case o (IxlensRep id $ flip const) of IxlensRep x y -> f (x . (mempty,)) (\s b -> y (mempty, s) b) +{-# INLINE withIxlens #-} --- | Extract the function that characterizes a 'Grate'. +-- | Extract the function that characterizes a 'Colens'. -- -withGrate :: AGrate s t a b -> ((((s -> a) -> b) -> t) -> r) -> r -withGrate o f = case o (GrateRep $ \k -> k id) of GrateRep sabt -> f sabt -{-# INLINE withGrate #-} +withColens :: AColens s t a b -> ((((s -> a) -> b) -> t) -> r) -> r +withColens o f = case o (ColensRep $ \k -> k id) of ColensRep sabt -> f sabt +{-# INLINE withColens #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +withCxlens :: Monoid k => ACxlens k s t a b -> ((((s -> a) -> k -> b) -> t) -> r) -> r +withCxlens o f = case o (CxlensRep ($ id)) of CxlensRep saibt -> f $ flip saibt mempty +{-# INLINE withCxlens #-} -- | TODO: Document -- @@ -210,6 +323,13 @@ withAffine :: ATraversal0 s t a b -> ((s -> t + a) -> (s -> b -> t) -> r) -> r withAffine o k = case o (AffineRep Right $ const id) of AffineRep x y -> k x y {-# INLINE withAffine #-} +-- | TODO: Document +-- +-- @since 0.0.3 +withAffine' :: ATraversal0 s s a b -> ((s -> Maybe a) -> (s -> b -> s) -> r) -> r +withAffine' o k = case o (AffineRep Right $ const id) of AffineRep x y -> k (either (const Nothing) Just . x) y +{-# INLINE withAffine' #-} + -- | TODO: Document -- withCoaffine :: ACotraversal0 s t a b -> ((((s -> t + a) -> b) -> t) -> r) -> r @@ -262,23 +382,6 @@ instance Choice (PrismRep a b) where right' (PrismRep sta bt) = PrismRep (either (Left . Left) (first Right . sta)) (Right . bt) {-# INLINE right' #-} -data CoprismRep a b s t = CoprismRep (s -> a) (b -> a + t) - -instance Functor (CoprismRep a b s) where - fmap f (CoprismRep sa bat) = CoprismRep sa (second f . bat) - {-# INLINE fmap #-} - -instance Profunctor (CoprismRep a b) where - lmap f (CoprismRep sa bat) = CoprismRep (sa . f) bat - {-# INLINE lmap #-} - - rmap = fmap - {-# INLINE rmap #-} - -instance Cochoice (CoprismRep a b) where - unleft (CoprismRep sca batc) = CoprismRep (sca . Left) (forgetr $ either (eassocl . batc) Right) - {-# INLINE unleft #-} - --------------------------------------------------------------------- -- LensRep --------------------------------------------------------------------- @@ -305,46 +408,60 @@ instance Representable (LensRep a b) where tabulate f = LensRep (\s -> info (f s)) (\s -> vals (f s)) + --------------------------------------------------------------------- --- ColensRep +-- IxlensRep --------------------------------------------------------------------- -data ColensRep a b s t = ColensRep (b -> s -> a) (b -> t) +data IxlensRep k a b s t = IxlensRep (s -> (k , a)) (s -> b -> t) -instance Profunctor (ColensRep a b) where - dimap f g (ColensRep bsa bt) = ColensRep (\b s -> bsa b (f s)) (g . bt) +instance Profunctor (IxlensRep k a b) where + dimap f g (IxlensRep sia sbt) = IxlensRep (sia . f) (\s -> g . sbt (f s)) -{- -instance Costrong (ColensRep a b) where - unfirst (ColensRep baca bbc) = ColensRep (curry baa) (forget2 $ bbc . fst) - where baa = uncurry baca . shuffle . (\(a,b) -> (a,bbc b)) . swap --TODO: B.second bbc - shuffle (x,(y,z)) = (y,(x,z)) --} +instance Strong (IxlensRep k a b) where + first' (IxlensRep sia sbt) = + IxlensRep (\(a, _) -> sia a) (\(s, c) b -> (sbt s b, c)) + + second' (IxlensRep sia sbt) = + IxlensRep (\(_, a) -> sia a) (\(c, s) b -> (c, sbt s b)) --------------------------------------------------------------------- --- GrateRep +-- ColensRep --------------------------------------------------------------------- --- | The 'GrateRep' profunctor precisely characterizes 'Grate'. +-- | The 'ColensRep' profunctor precisely characterizes 'Colens'. -- -newtype GrateRep a b s t = GrateRep { unGrateRep :: ((s -> a) -> b) -> t } +newtype ColensRep a b s t = ColensRep { unColensRep :: ((s -> a) -> b) -> t } -instance Profunctor (GrateRep a b) where - dimap f g (GrateRep z) = GrateRep $ \d -> g (z $ \k -> d (k . f)) +instance Profunctor (ColensRep a b) where + dimap f g (ColensRep z) = ColensRep $ \d -> g (z $ \k -> d (k . f)) -instance Closed (GrateRep a b) where - closed (GrateRep sabt) = GrateRep $ \xsab x -> sabt $ \sa -> xsab $ \xs -> sa (xs x) +instance Closed (ColensRep a b) where + closed (ColensRep sabt) = ColensRep $ \xsab x -> sabt $ \sa -> xsab $ \xs -> sa (xs x) -instance Costrong (GrateRep a b) where +instance Costrong (ColensRep a b) where unfirst = unfirstCorep -instance Cosieve (GrateRep a b) (Coindex b a) where - cosieve (GrateRep f) (Coindex g) = f g +instance Cosieve (ColensRep a b) (Coindex b a) where + cosieve (ColensRep f) (Coindex g) = f g -instance Corepresentable (GrateRep a b) where - type Corep (GrateRep a b) = Coindex b a +instance Corepresentable (ColensRep a b) where + type Corep (ColensRep a b) = Coindex b a - cotabulate f = GrateRep $ f . Coindex + cotabulate f = ColensRep $ f . Coindex + +--------------------------------------------------------------------- +-- CxlensRep +--------------------------------------------------------------------- + +-- @since 0.0.3 +newtype CxlensRep k a b s t = CxlensRep { unCxlensRep :: ((s -> a) -> k -> b) -> t } + +instance Profunctor (CxlensRep k a b) where + dimap f g (CxlensRep z) = CxlensRep $ \d -> g (z $ \k -> d (k . f)) + +instance Closed (CxlensRep k a b) where + closed (CxlensRep sabt) = CxlensRep $ \xsab x -> sabt $ \sa -> xsab $ \xs -> sa (xs x) --------------------------------------------------------------------- -- AffineRep @@ -413,6 +530,67 @@ instance Choice (CoaffineRep a b) where left' (CoaffineRep stabt) = CoaffineRep $ \f -> Left $ stabt $ \sta -> f $ eassocl . fmap eswap . eassocr . first sta + +--------------------------------------------------------------------- +-- 'Paired' +--------------------------------------------------------------------- + +newtype Paired p c d a b = Paired { runPaired :: p (c , a) (d , b) } + +fromTambara :: Profunctor p => Tambara p a b -> Paired p d d a b +fromTambara = Paired . dimap swap swap . runTambara + +instance Profunctor p => Profunctor (Paired p c d) where + dimap f g (Paired pab) = Paired $ dimap (fmap f) (fmap g) pab + +instance Strong p => Strong (Paired p c d) where + second' (Paired pab) = Paired . dimap shuffle shuffle . second' $ pab + where + shuffle (x,(y,z)) = (y,(x,z)) + +-- ^ @ +-- paired :: Iso s t a b -> Iso s' t' a' b' -> Iso (s, s') (t, t') (a, a') (b, b') +-- paired :: Lens s t a b -> Lens s' t' a' b' -> Lens (s, s') (t, t') (a, a') (b, b') +-- @ +-- +paired + :: Profunctor p + => Optic (Paired p s2 t2) s1 t1 a1 b1 + -> Optic (Paired p a1 b1) s2 t2 a2 b2 + -> Optic p (s1 , s2) (t1 , t2) (a1 , a2) (b1 , b2) +paired x y = + dimap swap swap . runPaired . x . Paired . dimap swap swap . runPaired . y . Paired + +--------------------------------------------------------------------- +-- 'Split' +--------------------------------------------------------------------- + +newtype Split p c d a b = Split { runSplit :: p (Either c a) (Either d b) } + +fromTambaraSum :: Profunctor p => TambaraSum p a b -> Split p d d a b +fromTambaraSum = Split . dimap eswap eswap . runTambaraSum + +instance Profunctor p => Profunctor (Split p c d) where + dimap f g (Split pab) = Split $ dimap (fmap f) (fmap g) pab + +instance Choice p => Choice (Split p c d) where + right' (Split pab) = Split . dimap shuffle shuffle . right' $ pab + where + shuffle = Right . Left ||| (Left ||| Right . Right) + +-- ^ @ +-- split :: Iso s t a b -> Iso s' t' a' b' -> Iso (Either s s') (Either t t') (Either a a') (Either b b') +-- split :: Prism s t a b -> Prism s' t' a' b' -> Lens (Either s s') (Either t t') (Either a a') (Either b b') +-- split :: View s t a b -> View s' t' a' b' -> Review (Either s s') (Either t t') (Either a a') (Either b b') +-- @ +split + :: Profunctor p + => Optic (Split p s2 t2) s1 t1 a1 b1 + -> Optic (Split p a1 b1) s2 t2 a2 b2 + -> Optic p (s1 + s2) (t1 + t2) (a1 + a2) (b1 + b2) +split x y = + dimap eswap eswap . runSplit . x . Split . dimap eswap eswap . runSplit . y . Split + --------------------------------------------------------------------- -- Index --------------------------------------------------------------------- @@ -458,11 +636,11 @@ instance a ~ b => Coapplicative (Index a b) where -- Coindex --------------------------------------------------------------------- --- | An indexed continuation that characterizes a 'Data.Profunctor.Optic.Lens.Grate' +-- | An indexed continuation that characterizes a 'Data.Profunctor.Optic.Lens.Colens' -- -- @'Coindex' a b s ≡ forall f. 'Functor' f => (f a -> b) -> f s@, -- --- See also 'Data.Profunctor.Optic.Lens.cloneGrateVl'. +-- See also 'Data.Profunctor.Optic.Lens.cloneColensVl'. -- -- 'Coindex' can also be used to compose indexed maps, folds, or traversals directly. -- @@ -482,6 +660,10 @@ instance Functor (Coindex a b) where instance a ~ b => Apply (Coindex a b) where (Coindex slab) <.> (Coindex ab) = Coindex $ \la -> slab $ \sl -> ab (la . sl) +--TODO helpful to use grate ops w/ cotraverse1 +--instance a ~ b => Coapply (Coindex a b) where +-- coapply (Coindex eab) = undefined + instance a ~ b => Applicative (Coindex a b) where pure s = Coindex ($s) (<*>) = (<.>) @@ -490,7 +672,6 @@ trivial :: Coindex a b b -> a trivial (Coindex f) = f id {-# INLINE trivial #-} -{- -- | Lift a regular function into a coindexed function. -- -- For example, to traverse two layers, keeping only the first index: @@ -508,7 +689,7 @@ coindex :: Functor f => s -> (b -> a) -> Coindex (f a) (f b) s coindex s ab = Coindex $ \sfa -> fmap ab (sfa s) {-# INLINE coindex #-} -infixr 9 .#. +infixr 9 <<<< -- | Compose two coindexes. -- @@ -526,9 +707,9 @@ infixr 9 .#. -- fmap (:[]) :: Coindex a b c -> Coindex a b [c] -- @ -- -(.#.) :: Semigroup s => Coindex c b s -> Coindex b a s -> Coindex c a s -Coindex f .#. Coindex g = Coindex $ \b -> f $ \s1 -> g $ \s2 -> b (s1 <> s2) --} +-- @since 0.0.3 +(<<<<) :: Semigroup s => Coindex c b s -> Coindex b a s -> Coindex c a s +Coindex f <<<< Coindex g = Coindex $ \b -> f $ \s1 -> g $ \s2 -> b (s1 <> s2) --------------------------------------------------------------------- -- Conjoin @@ -632,7 +813,7 @@ instance A.ArrowChoice (Conjoin j) where {-# INLINE (|||) #-} instance A.ArrowApply (Conjoin j) where - app = Conjoin $ \i (f, b) -> unConjoin f i b + app = Conjoin $ \k (f, b) -> unConjoin f k b {-# INLINE app #-} instance A.ArrowLoop (Conjoin j) where diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Combinator.hs b/profunctor-optics/src/Data/Profunctor/Optic/Combinator.hs index 35e7a16..25d93d9 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Combinator.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Combinator.hs @@ -6,43 +6,62 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeFamilies #-} module Data.Profunctor.Optic.Combinator ( - -- * Operations on arbitrary profunctors - constl + -- * Constructors + parr + , coarr + , star + , costar + , unstar + , uncostar + -- * Indexed constructors + , reix + , recx + , ixsum + , cxsum + , ixany + , ixhead + , ixlast + -- * Miscellaneous optics + , ixmap + , cxmap + , constl , constr , shiftl , shiftr , coercel , coercer - -- * Operations on (co)-strong profunctors - , strong - , costrong - , choice - , cochoice - , pull - , peval - , pushl - , pushr - -- * Operations on (co)-representable profunctors - , star - , costar - , unstar - , uncostar - , sieve' - , cosieve' - , tabulate' - , cotabulate' , represent + , ixrepresent , corepresent - , pure' - , copure' - , pappend - , liftR2 + , cxrepresent + -- * Operations on representable profunctors + , (.) + , (.~) + , (..~) + , over + , (%) + , (%~) + , (%%~) + , overWithKey + , (#) + , (#~) + , (##~) + , reoverWithKey + , (*~) + , (**~) + , reps + , repsWithKey + , (/~) + , (//~) + , coreps + , corepsWithKey -- * Arrow-style combinators , (<<*>>) , (****) , (++++) , (&&&&) , (||||) + , liftR2 -- * Divisible-style combinators , divide , divide' @@ -52,147 +71,386 @@ module Data.Profunctor.Optic.Combinator ( , choose' , cochoose , cochoose' + , pappend ) where + +import Control.Monad.State hiding (join) import Data.Function -import Data.Profunctor.Closed +import Data.Profunctor.Strong import Data.Profunctor.Optic.Carrier import Data.Profunctor.Optic.Types import Data.Profunctor.Optic.Import +import qualified Data.Bifunctor as B +import qualified Data.Semigroup as S -- $setup -- >>> :set -XNoOverloadedStrings -- >>> :set -XTypeApplications -- >>> :set -XFlexibleContexts -- >>> :set -XRankNTypes +-- >>> import Data.Char -- >>> import Data.Function ((&)) +-- >>> import Data.Semigroup +-- >>> import qualified Data.Bifunctor as B +-- >>> import qualified Data.Map.Lazy as Map -- >>> :load Data.Profunctor.Optic +parr :: Traversing p => (a -> b) -> p a b +parr = tabulate . (pure .) +{-# INLINE parr #-} + +coarr :: Cotraversing p => (a -> b) -> p a b +coarr = cotabulate . (. copure) +{-# INLINE coarr #-} + +star :: Applicative f => Star f a a +star = Star pure +{-# INLINE star #-} + +costar :: Coapplicative f => Costar f a a +costar = Costar copure +{-# INLINE costar #-} + +unstar :: Coapplicative f => Star f a b -> a -> b +unstar f = copure . runStar f +{-# INLINE unstar #-} + +uncostar :: Applicative f => Costar f a b -> a -> b +uncostar f = runCostar f . pure +{-# INLINE uncostar #-} + +-- | Map over the indices of an indexed optic. +-- +-- See also 'Data.Profunctor.Optic.Iso.reixed'. +-- +-- @since 0.0.3 +reix :: Profunctor p => (k1 -> k2) -> (k2 -> k1) -> Ixoptic p k1 s t a b -> Ixoptic p k2 s t a b +reix kl lk = (. lmap (first' kl)) . (lmap (first' lk) .) +{-# INLINE reix #-} + +-- | Map over the indices of a coindexed optic. +-- +-- See also 'Data.Profunctor.Optic.Iso.recxed'. +-- +-- @since 0.0.3 +recx :: Profunctor p => (k1 -> k2) -> (k2 -> k1) -> Cxoptic p k1 s t a b -> Cxoptic p k2 s t a b +recx kl lk = (. rmap (. kl)) . (rmap (. lk) .) +{-# INLINE recx #-} + +-- | Lift a numeric index into a sum monoid. +-- +-- @since 0.0.3 +ixsum :: Profunctor p => Ixoptic p k s t a b -> Ixoptic p (Sum k) s t a b +ixsum = reix Sum getSum +{-# INLINE ixsum #-} + +-- | Lift a numeric co-index into a sum monoid. +-- +-- @since 0.0.3 +cxsum :: Profunctor p => Cxoptic p k s t a b -> Cxoptic p (Sum k) s t a b +cxsum = recx Sum getSum +{-# INLINE cxsum #-} + +ixany :: Profunctor p => Ixoptic p Bool s t a b -> Ixoptic p Any s t a b +ixany = reix Any getAny +{-# INLINE ixany #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +ixhead :: Profunctor p => Ixoptic p i s t a b -> Ixoptic p (S.First i) s t a b +ixhead = reix S.First S.getFirst + +-- | TODO: Document +-- +-- @since 0.0.3 +ixlast :: Profunctor p => Ixoptic p i s t a b -> Ixoptic p (S.Last i) s t a b +ixlast = reix S.Last S.getLast +{- + +cxjoin :: Strong p => Cx p a a b -> p a b +cxjoin = peval + +cxreturn :: Profunctor p => p a b -> Cx p k a b +cxreturn = rmap const + +cxunit :: Strong p => Cx' p :-> p +cxunit p = dimap fork apply (first' p) + +-- | 'Cx'' is freely strong. +-- +-- See . +-- +cxfirst :: Profunctor p => Cx' p a b -> Cx' p (a, c) (b, c) +cxfirst = dimap fst (B.first @(,)) + +cxpastro :: Profunctor p => Iso (Cx' p a b) (Cx' p c d) (Pastro p a b) (Pastro p c d) +cxpastro = dimap (\p -> Pastro apply p fork) (\(Pastro l m r) -> dimap (fst . r) (\y a -> l (y, (snd (r a)))) m) +-} + --------------------------------------------------------------------- -- Operations on arbitrary profunctors --------------------------------------------------------------------- -constl :: Profunctor p => b -> p b c -> p a c +-- | TODO: Document +-- +-- @since 0.0.3 +ixmap :: Profunctor p => (s -> a) -> (b -> t) -> Ixoptic p k s t a b +ixmap sa bt = dimap (fmap sa) bt +{-# INLINE ixmap #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +cxmap :: Profunctor p => (s -> a) -> (b -> t) -> Cxoptic p k s t a b +cxmap sa bt = dimap sa (fmap bt) +{-# INLINE cxmap #-} + +constl :: Profunctor p => b -> Optic p a c b c constl = lmap . const {-# INLINE constl #-} -constr :: Profunctor p => c -> p a b -> p a c +constr :: Profunctor p => c -> Optic p a c a b constr = rmap . const {-# INLINE constr #-} -shiftl :: Profunctor p => p (a + b) c -> p b (c + d) +shiftl :: Profunctor p => Optic p b (c + d) (a + b) c shiftl = dimap Right Left {-# INLINE shiftl #-} -shiftr :: Profunctor p => p b (c , d) -> p (a , b) c +shiftr :: Profunctor p => Optic p (a , b) c b (c , d) shiftr = dimap snd fst {-# INLINE shiftr #-} -coercel :: Profunctor p => CoerceL p => p a b -> p c b -coercel = first absurd . lmap absurd +coercel :: Profunctor p => CoercingL p => Optic p c b a b +coercel = B.first absurd . lmap absurd {-# INLINE coercel #-} -coercer :: Profunctor p => CoerceR p => p a b -> p a c +coercer :: Profunctor p => CoercingR p => Optic p a c a b coercer = rmap absurd . contramap absurd {-# INLINE coercer #-} ---------------------------------------------------------------------- --- Operations on (co)-strong profunctors ---------------------------------------------------------------------- - -strong :: Strong p => ((a , b) -> c) -> p a b -> p a c -strong f = dimap fork f . second' -{-# INLINE strong #-} - -costrong :: Costrong p => ((a , b) -> c) -> p c a -> p b a -costrong f = unsecond . dimap f fork -{-# INLINE costrong #-} +-- | TODO: Document +-- +represent :: Representable p => ((a -> Rep p b) -> s -> Rep p t) -> Optic p s t a b +represent f = tabulate . f . sieve +{-# INLINE represent #-} -choice :: Choice p => (c -> (a + b)) -> p b a -> p c a -choice f = dimap f join . right' -{-# INLINE choice #-} +-- | TODO: Document +-- +-- @since 0.0.3 +ixrepresent :: Representable p => ((i -> a -> Rep p b) -> s -> Rep p t) -> Ixoptic p i s t a b +ixrepresent f = represent $ \ab -> f (curry ab) . snd +{-# INLINE ixrepresent #-} -cochoice :: Cochoice p => (c -> (a + b)) -> p a c -> p a b -cochoice f = unright . dimap join f -{-# INLINE cochoice #-} +-- | TODO: Document +-- +corepresent :: Corepresentable p => ((Corep p a -> b) -> Corep p s -> t) -> Optic p s t a b +corepresent f = cotabulate . f . cosieve +{-# INLINE corepresent #-} -pull :: Strong p => p a b -> p a (a , b) -pull = lmap fork . second' -{-# INLINE pull #-} +-- | TODO: Document +-- +-- @since 0.0.3 +cxrepresent :: Corepresentable p => ((i -> Corep p a -> b) -> Corep p s -> t) -> Cxoptic p i s t a b +cxrepresent f = corepresent $ \ab -> const . f (flip ab) +{-# INLINE cxrepresent #-} -peval :: Strong p => p a (a -> b) -> p a b -peval = rmap eval . pull -{-# INLINE peval #-} +--------------------------------------------------------------------- +-- Operations on representable profunctors +--------------------------------------------------------------------- -pushl :: Closed p => Traversing1 p => p a c -> p b c -> p a (b -> c) -pushl p q = curry' $ divide id p q -{-# INLINE pushl #-} +-- | Map over an 'Optic'. +-- +-- @ +-- 'over' o 'id' ≡ 'id' +-- 'over' o f '.' 'over' o g ≡ 'over' o (f '.' g) +-- 'over' '.' 'setter' ≡ 'id' +-- 'over' '.' 'resetter' ≡ 'id' +-- @ +-- +-- >>> over fmapped (+1) (Just 1) +-- Just 2 +-- >>> over fmapped (*10) [1,2,3] +-- [10,20,30] +-- >>> over first (+1) (1,2) +-- (2,2) +-- >>> over first show (10,20) +-- ("10",20) +-- +over :: Optic (->) s t a b -> (a -> b) -> s -> t +over = id +{-# INLINE over #-} -pushr :: Closed p => Traversing1 p => p (a , b) c -> p a b -> p a c -pushr = (<<*>>) . curry' -{-# INLINE pushr #-} +infixr 4 .~, ..~ ---------------------------------------------------------------------- --- Operations on (co)-representable profunctors ---------------------------------------------------------------------- +-- | Set the focus of an 'Optic'. +-- +(.~) :: Optic (->) s t a b -> b -> s -> t +(.~) o b = o (const b) +{-# INLINE (.~) #-} -star :: Applicative f => Star f a a -star = Star pure -{-# INLINE star #-} +-- | Map over an 'Optic'. +-- +-- >>> (10,20) & first ..~ show +-- ("10",20) +-- +(..~) :: Optic (->) s t a b -> (a -> b) -> s -> t +(..~) = over +{-# INLINE (..~) #-} -costar :: Coapplicative f => Costar f a a -costar = Costar copure -{-# INLINE costar #-} +infixr 8 % -unstar :: Coapplicative f => Star f a b -> a -> b -unstar f = copure . runStar f -{-# INLINE unstar #-} +-- | Monoidally combine indices between subsequent levels of optic. +-- +-- Its precedence is one lower than that of function composition, which allows /./ to be nested in /%/. +-- +-- If you only need the final index then use /./. +-- +-- >>> listsWithKey (ix "*" traversed . ix "+" traversed) ["foo", "bar"] +-- [("",'f'),("+",'o'),("++",'o'),("",'b'),("+",'a'),("++",'r')] +-- >>> listsWithKey (ix "*" traversed % ix "+" traversed) ["foo", "bar"] +-- [("",'f'),("+",'o'),("++",'o'),("*",'b'),("*+",'a'),("*++",'r')] +-- +-- @since 0.0.3 +(%) :: Monoid i => Representable p => Ixoptic p i c1 c2 b1 b2 -> Ixoptic p i b1 b2 a1 a2 -> Ixoptic p i c1 c2 a1 a2 +f % g = ixrepresent . runCoindex $ (Coindex . repsWithKey) f <<<< (Coindex . repsWithKey) g +{-# INLINE (%) #-} +{- +f % g = represent $ \ia1a2 (ic,c1) -> + (fmap flip . flip . ixrepn) f ic c1 $ \ib b1 -> + (fmap flip . flip . ixrepn) g ib b1 $ \ia a1 -> ia1a2 (ib <> ia, a1) + where ixrepn o h = curry $ reps o $ uncurry h +-} + +infixr 4 %~, %%~, #~, ##~ + +-- | Set the focus of an indexed optic. +-- +-- /Note/: This function is different from the equivalent in the /lens/ package. +-- The /profunctor-optics/ equivalent of /%~/ from /lens/ is '..~'. +-- +-- @since 0.0.3 +(%~) :: Monoid i => Ixoptic (->) i s t a b -> (i -> b) -> s -> t +(%~) o = overWithKey o . (const .) +{-# INLINE (%~) #-} -uncostar :: Applicative f => Costar f a b -> a -> b -uncostar f = runCostar f . pure -{-# INLINE uncostar #-} +-- | Map over an indexed optic. +-- +-- @since 0.0.3 +(%%~) :: Monoid i => Ixoptic (->) i s t a b -> (i -> a -> b) -> s -> t +(%%~) = overWithKey +{-# INLINE (%%~) #-} -sieve' :: Sieve p f => p d c -> Star f d c -sieve' = Star . sieve -{-# INLINE sieve' #-} +-- | TODO: Document +-- +-- @since 0.0.3 +overWithKey :: Monoid i => Ixoptic (->) i s t a b -> (i -> a -> b) -> s -> t +overWithKey o f = (unConjoin #. corepresent o .# Conjoin) f mempty +{-# INLINE overWithKey #-} -cosieve' :: Cosieve p f => p a b -> Costar f a b -cosieve' = Costar . cosieve -{-# INLINE cosieve' #-} +infixr 8 # -tabulate' :: Representable p => Star (Rep p) a b -> p a b -tabulate' = tabulate . runStar -{-# INLINE tabulate' #-} +-- | Compose two coindexed traversals, combining indices. +-- +-- Its precedence is one lower than that of function composition, which allows /./ to be nested in /#/. +-- +-- If you only need the final index then use /./. +-- +-- >>> cofoldsWithKey (rxfrom Map.mapWithKey # rxfrom Map.mapWithKey) (\k r a -> Map.singleton k (a + r)) 1.0 $ Map.fromList [("k",Map.fromList [("l",2.0)])] +-- fromList [("k",fromList [("l",fromList [("kl",3.0)])])] +-- +-- @since 0.0.3 +(#) :: Monoid i => Corepresentable p => Cxoptic p i c1 c2 b1 b2 -> Cxoptic p i b1 b2 a1 a2 -> Cxoptic p i c1 c2 a1 a2 +f # g = cxrepresent . runCoindex $ (Coindex . corepsWithKey) f <<<< (Coindex . corepsWithKey) g +{- +f # g = corepresent $ \a1ka2 c1 kc -> + (fmap flip . flip . cxrepn) f kc c1 $ \kb b1 -> + (fmap flip . flip . cxrepn) g kb b1 $ \ka a1 -> a1ka2 a1 (kb <> ka) + where cxrepn o f = flip $ coreps o $ flip f +{-# INLINE (#) #-} +-} + +-- | Set the focus of a coindexed optic. +-- +-- @since 0.0.3 +(#~) :: Monoid i => Cxoptic (->) i s t a b -> (i -> b) -> s -> t +(#~) o = reoverWithKey o . (const .) +{-# INLINE (#~) #-} + +-- | Map over a coindexed optic. +-- +-- @since 0.0.3 +(##~) :: Monoid i => Cxoptic (->) i s t a b -> (i -> a -> b) -> s -> t +(##~) = reoverWithKey +{-# INLINE (##~) #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +reoverWithKey :: Monoid i => Cxoptic (->) i s t a b -> (i -> a -> b) -> s -> t +reoverWithKey o f = (unConjoin #. represent o .# Conjoin) f mempty +{-# INLINE reoverWithKey #-} -cotabulate' :: Corepresentable p => Costar (Corep p) a b -> p a b -cotabulate' = cotabulate . runCostar -{-# INLINE cotabulate' #-} +infixr 4 *~, **~, /~, //~ -represent :: Representable p => ((a -> Rep p b) -> s -> Rep p t) -> p a b -> p s t -represent f = tabulate . f . sieve -{-# INLINE represent #-} +-- | Set the focus of a representable optic. +-- +-- @since 0.0.3 +(*~) :: Optic (Star f) s t a b -> f b -> s -> f t +(*~) o b = o **~ (const b) +{-# INLINE (*~) #-} -corepresent :: Corepresentable p => ((Corep p a -> b) -> Corep p s -> t) -> p a b -> p s t -corepresent f = cotabulate . f . cosieve -{-# INLINE corepresent #-} +-- | Map over a representable optic. +-- +-- >>> [66,97,116,109,97,110] & traversed **~ \a -> ("na", chr a) +-- ("nananananana","Batman") +-- +-- @since 0.0.3 +(**~) :: Optic (Star f) s t a b -> (a -> f b) -> s -> f t +(**~) o = runStar #. o .# Star +{-# INLINE (**~) #-} -pure' :: Traversing p => (a -> b) -> p a b -pure' = tabulate . (pure .) -{-# INLINE pure' #-} +-- | TODO: Document +-- +reps :: Representable p => Optic p s t a b -> ((a -> Rep p b) -> s -> Rep p t) +reps o = sieve . o . tabulate +{-# INLINE reps #-} -copure' :: Cotraversing p => (a -> b) -> p a b -copure' = cotabulate . (. copure) -{-# INLINE copure' #-} +-- | TODO: Document +-- +-- @since 0.0.3 +repsWithKey :: Representable p => Monoid i => Ixoptic p i s t a b -> (i -> a -> Rep p b) -> s -> Rep p t +repsWithKey o f = curry (reps o $ uncurry f) mempty +{-# INLINE repsWithKey #-} -pappend :: Traversing1 p => p a b -> p a b -> p a b -pappend = divide fork -{-# INLINE pappend #-} +-- | Set the focus of a co-representable optic. +-- +-- @since 0.0.3 +(/~) :: Optic (Costar f) s t a b -> b -> f s -> t +(/~) o b = o //~ (const b) +{-# INLINE (/~) #-} -liftR2 :: Traversing1 p => (b -> c -> d) -> p a b -> p a c -> p a d -liftR2 f x y = tabulate $ \s -> liftF2 f (sieve x s) (sieve y s) -{-# INLINE liftR2 #-} +-- | Map over a co-representable optic. +-- +-- @since 0.0.3 +(//~) :: Optic (Costar f) s t a b -> (f a -> b) -> f s -> t +(//~) o = runCostar #. o .# Costar +{-# INLINE (//~) #-} +-- | TODO: Document +-- +coreps :: Corepresentable p => Optic p s t a b -> ((Corep p a -> b) -> Corep p s -> t) +coreps o = cosieve . o . cotabulate +{-# INLINE coreps #-} +-- | TODO: Document +-- +-- @since 0.0.3 +corepsWithKey :: Corepresentable p => Monoid i => Cxoptic p i s t a b -> (i -> Corep p a -> b) -> Corep p s -> t +corepsWithKey o f = flip (coreps o $ flip f) mempty +{-# INLINE corepsWithKey #-} --------------------------------------------------------------------- -- Arrow-style combinators @@ -200,7 +458,7 @@ liftR2 f x y = tabulate $ \s -> liftF2 f (sieve x s) (sieve y s) infixl 4 <<*>> --- | Profunctor version of '<*>'. +-- | Profunctor variant of '<*>'. -- (<<*>>) :: Traversing1 p => p a (b -> c) -> p a b -> p a c (<<*>>) = liftR2 ($) @@ -208,7 +466,7 @@ infixl 4 <<*>> infixr 3 **** --- | Profunctor version of '***'. +-- | Profunctor variant of '***'. -- (****) :: Traversing1 p => p a1 b1 -> p a2 b2 -> p (a1 , a2) (b1 , b2) p **** q = dimap fst (,) p <<*>> lmap snd q @@ -216,15 +474,15 @@ p **** q = dimap fst (,) p <<*>> lmap snd q infixr 2 ++++ --- | Profunctor version of '+++'. +-- | Profunctor variant of '+++'. -- (++++) :: Cotraversing1 p => p a1 b1 -> p a2 b2 -> p (a1 + a2) (b1 + b2) -p ++++ q = cotabulate $ bimap (cosieve p) (cosieve q) . coapply +p ++++ q = cotabulate $ B.bimap (cosieve p) (cosieve q) . coapply {-# INLINE (++++) #-} infixr 3 &&&& --- | Profunctor version of '&&&'. +-- | Profunctor variant of '&&&'. -- (&&&&) :: Traversing1 p => p a b1 -> p a b2 -> p a (b1 , b2) p &&&& q = liftR2 (,) p q @@ -232,17 +490,21 @@ p &&&& q = liftR2 (,) p q infixr 2 |||| --- | Profunctor version of '|||'. +-- | Profunctor variant of '|||'. -- (||||) :: Cotraversing1 p => p a1 b -> p a2 b -> p (a1 + a2) b p |||| q = cotabulate $ either (cosieve p) (cosieve q) . coapply {-# INLINE (||||) #-} +liftR2 :: Traversing1 p => (b -> c -> d) -> p a b -> p a c -> p a d +liftR2 f x y = tabulate $ \s -> liftF2 f (sieve x s) (sieve y s) +{-# INLINE liftR2 #-} + --------------------------------------------------------------------- -- Divisible-style combinators --------------------------------------------------------------------- --- | Profunctor version of < hackage.haskell.org/package/contravariant/docs/Data-Functor-Contravariant-Divisible.html#v:divide divide >. +-- | Profunctor variant of < hackage.haskell.org/package/contravariant/docs/Data-Functor-Contravariant-Divisible.html#v:divide divide >. -- divide :: Traversing1 p => (a -> (a1 , a2)) -> p a1 b -> p a2 b -> p a b divide f p q = dimap f fst $ p **** q @@ -260,7 +522,7 @@ codivide' :: Cotraversing1 p => p a b1 -> p a b2 -> p a (b1 + b2) codivide' = codivide id {-# INLINE codivide' #-} --- | Profunctor version of < hackage.haskell.org/package/contravariant/docs/Data-Functor-Contravariant-Divisible.html#v:choose choose >. +-- | Profunctor variant of < hackage.haskell.org/package/contravariant/docs/Data-Functor-Contravariant-Divisible.html#v:choose choose >. -- choose :: Cotraversing1 p => (a -> (a1 + a2)) -> p a1 b -> p a2 b -> p a b choose f p q = dimap f join $ p ++++ q @@ -277,3 +539,17 @@ cochoose f p q = dimap fork f $ p **** q cochoose' :: Traversing1 p => p a b1 -> p a b2 -> p a (b1, b2) cochoose' = cochoose id {-# INLINE cochoose' #-} + +pappend :: Traversing1 p => p a b -> p a b -> p a b +pappend = divide fork +{-# INLINE pappend #-} + +{- +pushl :: Closed p => Traversing1 p => p a c -> p b c -> p a (b -> c) +pushl p q = curry' $ divide id p q +{-# INLINE pushl #-} + +pushr :: Closed p => Traversing1 p => p (a , b) c -> p a b -> p a c +pushr = (<<*>>) . curry' +{-# INLINE pushr #-} +-} diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Fold.hs b/profunctor-optics/src/Data/Profunctor/Optic/Fold.hs index 3c39af0..df5fa64 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Fold.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Fold.hs @@ -10,58 +10,95 @@ module Data.Profunctor.Optic.Fold ( -- * Fold0 Fold0 , fold0 + , ixfold0 + , afold0 , failing , toFold0 , fromFold0 - , afold0 -- * Fold , Fold , Cofold , fold_ + , ofold_ + , afold + , aixfold , folding - , cofolding + , ofolding , foldVl - , cofoldVl - , afold + , ixfoldVl , acofold + , cofolding + , cofoldVl -- * Fold1 , Fold1 , Cofold1 , fold1_ , folding1 + , foldVl1 + , ixfoldVl1 , cofolding1 - , fold1Vl - , cofold1Vl + , cofoldVl1 -- * Optics , folded0 , filtered , folded + , cofolded + , ofolded , folded_ + , ofolded_ + , ixfoldedRep , folded1 , folded1_ + , afolded + , aofolded + , afolded1 + , aofolded1 , acolist , acolist1 -- * Operators , folds0 - , folds - , cofolds , (^?) , preview , previews , preuse , preuses + , folds + , cofolds + , foldsa + , cofoldsa , (^..) , lists - , colists , lists1 , foldsr - , cofoldsr , foldsl , foldsr' , foldsl' , foldsrM , foldslM , traverses_ + -- * Indexed operators + , folds0WithKey + , previewWithKey + , previewsWithKey + , foldsWithKey + , cofoldsWithKey + , (^%%) + , listsWithKey + , foldsrWithKey + , foldslWithKey + , foldsrWithKey' + , foldslWithKey' + , foldsrMWithKey + , foldslMWithKey + , traversesWithKey_ + -- * EndoM + , EndoM(..) + -- * Classes + , Strong(..) + , Choice(..) + , Closed(..) + , Representable(..) + , Corepresentable(..) ) where import Control.Applicative as A @@ -72,16 +109,17 @@ import Data.Foldable (Foldable, traverse_) import Data.List.NonEmpty (NonEmpty(..)) import Data.Maybe import Data.Monoid -import Data.Semiring as Rng +import Data.MonoTraversable (Element,MonoTraversable(..),MonoFoldable(..)) +import Data.NonNull import Data.Profunctor.Optic.Carrier import Data.Profunctor.Optic.Combinator import Data.Profunctor.Optic.Import import Data.Profunctor.Optic.Traversal import Data.Profunctor.Optic.Types import Data.Profunctor.Optic.Prism -import Data.Profunctor.Rep.Foldl (EndoM(..)) +import qualified Data.Functor.Rep as F import qualified Data.List as L -import qualified Data.List.NonEmpty as NEL +import qualified Data.List.NonEmpty as NNL import qualified Data.Profunctor.Rep.Foldl1 as M -- $setup @@ -117,7 +155,20 @@ fold0 :: (s -> Maybe a) -> Fold0 s a fold0 f = coercer . lmap (\s -> maybe (Left s) Right (f s)) . right' {-# INLINE fold0 #-} -infixl 3 `failing` +-- | Obtain an 'Ixfold0' directly. +-- +-- @since 0.0.3 +ixfold0 :: (s -> Maybe (k, a)) -> Ixfold0 k s a +ixfold0 g = ixtraversalVl0 (\point f s -> maybe (point s) (uncurry f) $ g s) . coercer +{-# INLINE ixfold0 #-} + +-- | TODO: Document +-- +afold0 :: ((a -> Maybe r) -> s -> Maybe r) -> AFold0 r s a +afold0 f = afold $ (Alt #.) #. f .# (getAlt #.) +{-# INLINE afold0 #-} + +infix 3 `failing` -- | If the first 'Fold0' has no focus then try the second one. -- @@ -144,14 +195,8 @@ fromFold0 :: AFold0 a s a -> View s (Maybe a) fromFold0 = (\f -> coercer . lmap f) . preview {-# INLINE fromFold0 #-} --- | TODO: Document --- -afold0 :: ((a -> Maybe r) -> s -> Maybe r) -> AFold0 r s a -afold0 f = afold $ (Alt #.) #. f .# (getAlt #.) -{-# INLINE afold0 #-} - --------------------------------------------------------------------- --- 'Fold' +-- Fold --------------------------------------------------------------------- -- | Obtain a 'Fold' directly. @@ -173,6 +218,33 @@ fold_ :: Foldable f => (s -> f a) -> Fold s a fold_ f = coercer . lmap f . foldVl traverse_ {-# INLINE fold_ #-} +-- | Obtain a mono 'Fold' directly. +-- +-- >>> "foobar" ^.. ofold_ tail +-- "oobar" +-- +-- @since 0.0.3 +ofold_ :: MonoFoldable a => (s -> a) -> Fold s (Element a) +ofold_ f = coercer . lmap f . foldVl otraverse_ +{-# INLINE ofold_ #-} + +-- | TODO: Document +-- +-- @ +-- 'afold' :: ((a -> r) -> s -> r) -> 'AFold' r s a +-- @ +-- +afold :: ((a -> r) -> s -> r) -> ATraversal (Const r) s t a b +afold f = atraversal $ (Const #.) #. f .# (getConst #.) +{-# INLINE afold #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +aixfold :: ((k -> a -> r) -> s -> r) -> AIxtraversal (Const r) k s t a b +aixfold f = afold $ \iar s -> f (curry iar) $ snd s +{-# INLINE aixfold #-} + -- | Obtain a 'Fold' from a 'Traversable' functor. -- -- @ @@ -184,13 +256,17 @@ folding :: Traversable f => (s -> a) -> Fold (f s) a folding f = foldVl traverse . coercer . lmap f {-# INLINE folding #-} --- | TODO: Document +-- | Obtain a 'Fold' from a 'MonoTraversable' functor. -- --- > 'cofoldVl' 'cotraverse' . 'from' f +-- @ +-- 'folding' f ≡ 'otraversed' . 'to' f +-- 'folding' f ≡ 'foldVl' 'otraverse' . 'to' f +-- @ -- -cofolding :: Distributive g => (b -> t) -> Cofold (g t) b -cofolding f = cofoldVl cotraverse . coercel . rmap f -{-# INLINE cofolding #-} +-- @since 0.0.3 +ofolding :: MonoTraversable s => (Element s -> a) -> Fold s a +ofolding f = foldVl otraverse . coercer . lmap f +{-# INLINE ofolding #-} -- | Obtain a 'Fold' from a Van Laarhoven 'Fold'. -- @@ -198,23 +274,32 @@ foldVl :: (forall f. Applicative f => (a -> f b) -> s -> f t) -> Fold s a foldVl f = coercer . traversalVl f . coercer {-# INLINE foldVl #-} --- | Obtain a 'Cofold' from a Van Laarhoven 'Cofold'. +-- | Obtain a 'Ixfold' from a Van Laarhoven 'Fold'. -- -cofoldVl :: (forall f. Coapplicative f => (f a -> b) -> f s -> t) -> Cofold t b -cofoldVl f = coercel . cotraversalVl f . coercel -{-# INLINE cofoldVl #-} +-- @since 0.0.3 +ixfoldVl :: (forall f. Applicative f => (k -> a -> f b) -> s -> f t) -> Ixfold k s a +ixfoldVl f = coercer . ixtraversalVl f . coercer +{-# INLINE ixfoldVl #-} -- | TODO: Document -- -afold :: ((a -> r) -> s -> r) -> AFold r s a -afold f = Star #. (Const #.) #. f .# (getConst #.) .# runStar -{-# INLINE afold #-} +acofold :: ((r -> b) -> r -> t) -> ACofold r t b +acofold f = acotraversal $ (.# getConst) #. f .# (.# Const) +{-# INLINE acofold #-} -- | TODO: Document -- -acofold :: ((r -> b) -> r -> t) -> ACofold r t b -acofold f = Costar #. (.# getConst) #. f .# (.# Const) .# runCostar -{-# INLINE acofold #-} +-- > 'cofoldVl' 'cotraverse' . 'from' f +-- +cofolding :: Distributive g => (b -> t) -> Cofold (g t) b +cofolding f = cofoldVl cotraverse . coercel . rmap f +{-# INLINE cofolding #-} + +-- | Obtain a 'Cofold' from a Van Laarhoven 'Cofold'. +-- +cofoldVl :: (forall f. Coapplicative f => (f a -> b) -> f s -> t) -> Cofold t b +cofoldVl f = coercel . cotraversalVl f . coercel +{-# INLINE cofoldVl #-} --------------------------------------------------------------------- -- Fold1 @@ -224,7 +309,7 @@ acofold f = Costar #. (.# getConst) #. f .# (.# Const) .# runCostar -- -- @ -- 'fold1_' ('lists1' o) ≡ o --- 'fold1_' f ≡ 'to' f . 'fold1Vl' 'traverse1_' +-- 'fold1_' f ≡ 'to' f . 'foldVl1' 'traverse1_' -- 'fold1_' f ≡ 'coercer' . 'lmap' f . 'lift' 'traverse1_' -- @ -- @@ -233,41 +318,48 @@ acofold f = Costar #. (.# getConst) #. f .# (.# Const) .# runCostar -- This can be useful to represent operations from @Data.List.NonEmpty@ and elsewhere into a 'Fold1'. -- fold1_ :: Foldable1 f => (s -> f a) -> Fold1 s a -fold1_ f = coercer . lmap f . fold1Vl traverse1_ +fold1_ f = coercer . lmap f . foldVl1 traverse1_ {-# INLINE fold1_ #-} -- | Obtain a 'Fold1' from a 'Traversable1' functor. -- -- @ -- 'folding1' f ≡ 'traversed1' . 'to' f --- 'folding1' f ≡ 'fold1Vl' 'traverse1' . 'to' f +-- 'folding1' f ≡ 'foldVl1' 'traverse1' . 'to' f -- @ -- folding1 :: Traversable1 f => (s -> a) -> Fold1 (f s) a -folding1 f = fold1Vl traverse1 . coercer . lmap f +folding1 f = foldVl1 traverse1 . coercer . lmap f {-# INLINE folding1 #-} --- | TODO: Document +-- | Obtain a 'Fold1' from a Van Laarhoven 'Fold1'. -- --- > 'cofolding1' f = 'cofold1Vl' 'cotraverse1' . 'from' f +-- See 'Data.Profunctor.Optic.Property'. -- -cofolding1 :: Distributive1 g => (b -> t) -> Cofold1 (g t) b -cofolding1 f = cofold1Vl cotraverse1 . coercel . rmap f -{-# INLINE cofolding1 #-} +foldVl1 :: (forall f. Apply f => (a -> f b) -> s -> f t) -> Fold1 s a +foldVl1 f = coercer . represent f . coercer +{-# INLINE foldVl1 #-} --- | Obtain a 'Fold1' from a Van Laarhoven 'Fold1'. +-- | Obtain a 'Ixfold' from a Van Laarhoven 'Fold'. -- --- See 'Data.Profunctor.Optic.Property'. +-- @since 0.0.3 +ixfoldVl1 :: (forall f. Apply f => (k -> a -> f b) -> s -> f t) -> Ixfold1 k s a +ixfoldVl1 f = coercer . ixtraversalVl1 f . coercer +{-# INLINE ixfoldVl1 #-} + +-- | TODO: Document -- -fold1Vl :: (forall f. Apply f => (a -> f b) -> s -> f t) -> Fold1 s a -fold1Vl f = coercer . represent f . coercer -{-# INLINE fold1Vl #-} +-- > 'cofolding1' f = 'cofoldVl1' 'cotraverse1' . 'from' f +-- +cofolding1 :: Distributive1 g => (b -> t) -> Cofold1 (g t) b +cofolding1 f = cofoldVl1 cotraverse1 . coercel . rmap f +{-# INLINE cofolding1 #-} -- | Obtain a 'Cofold1' from a Van Laarhoven 'Cofold1'. -- -cofold1Vl :: (forall f. Coapply f => (f a -> b) -> f s -> t) -> Cofold1 t b -cofold1Vl f = coercel . cotraversal1Vl f . coercel -{-# INLINE cofold1Vl #-} +cofoldVl1 :: (forall f. Coapply f => (f a -> b) -> f s -> t) -> Cofold1 t b +cofoldVl1 f = coercel . cotraversalVl1 f . coercel +{-# INLINE cofoldVl1 #-} --------------------------------------------------------------------- -- Optics @@ -288,7 +380,7 @@ folded0 = fold0 id -- [2,4,6,8,10] -- filtered :: (a -> Bool) -> Fold0 a a -filtered p = traversal0Vl (\point f a -> if p a then f a else point a) . coercer +filtered p = traversalVl0 (\point f a -> if p a then f a else point a) . coercer {-# INLINE filtered #-} -- | Obtain a 'Fold' from a 'Traversable' functor. @@ -297,6 +389,21 @@ folded :: Traversable f => Fold (f a) a folded = folding id {-# INLINE folded #-} +-- | TODO: Document +-- +-- > 'cofoldVl' 'cotraverse' . 'from' f +-- +cofolded :: Distributive g => Cofold (g b) b +cofolded = cofolding id +{-# INLINE cofolded #-} + +-- | Obtain a 'Fold' from a 'MonoTraversable' functor. +-- +-- @since 0.0.3 +ofolded :: MonoTraversable a => Fold a (Element a) +ofolded = ofolding id +{-# INLINE ofolded #-} + -- | The canonical 'Fold'. -- -- @ @@ -307,6 +414,20 @@ folded_ :: Foldable f => Fold (f a) a folded_ = fold_ id {-# INLINE folded_ #-} +-- | Obtain a 'Fold' from a 'MonoFoldable'. +-- +-- @since 0.0.3 +ofolded_ :: MonoFoldable a => Fold a (Element a) +ofolded_ = ofold_ id +{-# INLINE ofolded_ #-} + +-- | Obtain an 'Ixfold' from a 'F.Representable' functor. +-- +-- @since 0.0.3 +ixfoldedRep :: F.Representable f => Traversable f => Ixfold (F.Rep f) (f a) a +ixfoldedRep = ixfoldVl F.itraverseRep +{-# INLINE ixfoldedRep #-} + -- | Obtain a 'Fold1' from a 'Traversable1' functor. -- folded1 :: Traversable1 f => Fold1 (f a) a @@ -323,18 +444,78 @@ folded1_ :: Foldable1 f => Fold1 (f a) a folded1_ = fold1_ id {-# INLINE folded1_ #-} +-- | TODO: Document +-- +afolded :: Foldable f => Monoid r => AFold r (f a) a +afolded = afold foldMap +{-# INLINE afolded #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +aofolded :: MonoFoldable a => Monoid r => AFold r a (Element a) +aofolded = afold ofoldMap +{-# INLINE aofolded #-} + +-- | TODO: Document +-- +afolded1 :: Foldable1 f => Semigroup r => AFold r (f a) a +afolded1 = afold foldMap1 +{-# INLINE afolded1 #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +aofolded1 :: MonoFoldable a => Semigroup r => AFold r (NonNull a) (Element a) +aofolded1 = afold ofoldMap1 +{-# INLINE aofolded1 #-} + -- | Right list unfold over an optic. -- acolist :: ACofold a [b] (Maybe (b, a)) acolist = acofold L.unfoldr {-# INLINE acolist #-} +--abytestring :: ACofold a Words.ByteString (Maybe (Word8, a)) +--abytestring = acofold Words.unfoldr + -- | Right non-empty list unfold over an optic. -- acolist1 :: ACofold a (NonEmpty b) (b, Maybe a) -acolist1 = acofold NEL.unfoldr +acolist1 = acofold NNL.unfoldr {-# INLINE acolist1 #-} +{- +import qualified Data.Functor.Foldable as F + +aapo :: Corecursive t => ACofold b t (Base t (t + b)) +aapo = acofold F.apo +{-# INLINE aapo #-} + +-- | TODO: Document +-- +-- >>> import Data.Functor.Foldable (ListF(..)) +-- >>> :{ +-- let +-- fromListF :: Num a => ListF a (Sum a) -> Sum a +-- fromListF Nil = mempty +-- fromListF (Cons a r) = Sum a <> r +-- in folds acata fromListF $ [1..5] +-- :} +-- Sum {getSum = 15} +-- +acata :: Recursive s => AFold a s (Base s a) +acata = afold F.cata +{-# INLINE acata #-} + +apara :: Recursive s => AFold a s (Base s (s , a)) +apara = afold F.para +{-# INLINE apara #-} + +acataA :: Recursive s => AFold (f a) s (Base s (f a)) +acataA = afold F.cataA +{-# INLINE acataA #-} +-} --------------------------------------------------------------------- -- Operators --------------------------------------------------------------------- @@ -345,35 +526,9 @@ folds0 :: AFold0 r s a -> (a -> Maybe r) -> s -> Maybe r folds0 o = (getAlt #.) #. folds o .# (Alt #.) {-# INLINE folds0 #-} --- | Map an optic to a monoid and combine the results. --- --- @ --- 'Data.Foldable.foldMap' = 'folds' 'folded_' --- @ --- --- >>> folds both (\x -> [x, x + 1]) (1,3) --- [1,2,3,4] --- >>> folds both id (["foo"], ["bar", "baz"]) --- ["foo","bar","baz"] --- -folds :: AFold r s a -> (a -> r) -> s -> r -folds o = (getConst #.) #. traverses o .# (Const #.) -{-# INLINE folds #-} - --- | TODO: Document --- --- >>> cofolds (from succ) (*2) 3 --- 7 --- --- Compare 'Data.Profunctor.Optic.View.reviews'. --- -cofolds :: ACofold r t b -> (r -> b) -> r -> t -cofolds o = (.# Const) #. cotraverses o .# (.# getConst) -{-# INLINE cofolds #-} - -infixl 8 ^? +infix 8 ^? --- | An infix alias for 'preview''. +-- | An infk alias for 'preview''. -- -- @ -- ('^?') ≡ 'flip' 'preview'' @@ -418,7 +573,45 @@ preuses :: MonadState s m => AFold0 r s a -> (a -> r) -> m (Maybe r) preuses o f = State.gets $ previews o f {-# INLINE preuses #-} -infixl 8 ^.. +-- | Map an optic to a monoid and combine the results. +-- +-- @ +-- 'Data.Foldable.foldMap' = 'folds' 'folded_' +-- @ +-- +-- >>> folds bitraversed (\x -> [x, x + 1]) (1,3) +-- [1,2,3,4] +-- >>> folds bitraversed id (["foo"], ["bar", "baz"]) +-- ["foo","bar","baz"] +-- +folds :: ATraversal (Const r) s t a b -> (a -> r) -> s -> r +folds o = (getConst #.) #. traverses o .# (Const #.) +{-# INLINE folds #-} + +-- | TODO: Document +-- +-- >>> cofolds (from succ) (*2) 3 +-- 7 +-- +-- Compare 'Data.Profunctor.Optic.View.reviews'. +-- +cofolds :: ACotraversal (Const r) s t a b -> (r -> b) -> r -> t +cofolds o = (.# Const) #. cotraverses o .# (.# getConst) +{-# INLINE cofolds #-} + +-- | TODO: Document +-- +foldsa :: Applicative f => AFold (f a) s a -> s -> f a +foldsa = flip folds pure +{-# INLINE foldsa #-} + +-- | TODO: Document +-- +cofoldsa :: Coapplicative f => ACofold (f b) t b -> f b -> t +cofoldsa = flip cofolds copure +{-# INLINE cofoldsa #-} + +infix 8 ^.. -- | Infix alias of 'lists'. -- @@ -437,34 +630,25 @@ infixl 8 ^.. -- >>> (1,2) ^.. bitraversed -- [1,2] -- --- @ --- ('^..') :: s -> 'View' s a -> [a] --- ('^..') :: s -> 'Fold' s a -> [a] --- ('^..') :: s -> 'Lens'' s a -> [a] --- ('^..') :: s -> 'Iso'' s a -> [a] --- ('^..') :: s -> 'Traversal'' s a -> [a] --- ('^..') :: s -> 'Prism'' s a -> [a] --- ('^..') :: s -> 'Traversal0'' s a -> [a] --- @ --- (^..) :: s -> AFold (Endo [a]) s a -> [a] (^..) = flip lists ---s ^.. o = (`appEndo` []) . folds o (Endo . (:)) $ s {-# INLINE (^..) #-} --- | Collect the foci of an optic into a list. +-- | Collect the fock of an optic into a list. +-- +-- @ +-- 'lists' 'folded_' = 'Data.Foldable.toList' +-- @ -- lists :: AFold (Endo [a]) s a -> s -> [a] lists o = foldsr o (:) [] {-# INLINE lists #-} --- | TODO: Document. +-- | Extract a 'NonEmpty' of the fock of an optic. -- -colists :: ACofold b (Endo [t]) (Endo [b]) -> b -> [t] -colists o = cofoldsr o (:) [] -{-# INLINE colists #-} - --- | Extract a 'NonEmpty' of the foci of an optic. +-- @ +-- 'lists1' 'folded1_' = 'Data.Semigroup.Foldable.toNonEmpty' +-- @ -- -- >>> lists1 bitraversed1 ('h' :| "ello", 'w' :| "orld") -- ('h' :| "ello") :| ['w' :| "orld"] @@ -481,13 +665,7 @@ lists1 l = M.runNedl . folds l (M.Nedl . (:|)) foldsr :: AFold (Endo r) s a -> (a -> r -> r) -> r -> s -> r foldsr o f r = (`appEndo` r) . folds o (Endo . f) {-# INLINE foldsr #-} - --- | TODO: Document --- -cofoldsr :: ACofold r (Endo t) (Endo b) -> (r -> b -> b) -> t -> r -> t -cofoldsr o f t = (`appEndo` t) . cofolds o (Endo . f) -{-# INLINE cofoldsr #-} - + -- | Left fold over an optic. -- foldsl :: AFold ((Endo-Dual) r) s a -> (r -> a -> r) -> r -> s -> r @@ -546,14 +724,174 @@ foldslM o f r xs = foldsr o f' mempty xs `appEndoM` r where f' a e = e <> EndoM -- | Applicative fold over an optic. -- --- >>> traverses_ both putStrLn ("hello","world") --- hello --- world --- -- @ -- 'Data.Foldable.traverse_' ≡ 'traverses_' 'folded' -- @ -- +-- >>> traverses_ bitraversed putStrLn ("hello","world") +-- hello +-- world +-- traverses_ :: Applicative f => AFold (Endo (f ())) s a -> (a -> f r) -> s -> f () traverses_ p f = foldsr p (\a fu -> void (f a) *> fu) (pure ()) {-# INLINE traverses_ #-} + +--------------------------------------------------------------------- +-- Indexed operators +--------------------------------------------------------------------- + +-- | TODO: Document +-- +-- @since 0.0.3 +folds0WithKey :: Monoid k => AIxfold0 r k s a -> (k -> a -> Maybe r) -> s -> Maybe r +folds0WithKey o f = curry ((getAlt #.) #. folds o .# (Alt #.) $ uncurry f) mempty +{-# INLINE folds0WithKey #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +previewWithKey :: Monoid k => AIxfold0 (k , a) k s a -> s -> Maybe (k , a) +previewWithKey o = previewsWithKey o (,) +{-# INLINE previewWithKey #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +previewsWithKey :: Monoid k => AIxfold0 r k s a -> (k -> a -> r) -> s -> Maybe r +previewsWithKey o f = folds0WithKey o (\k -> Just . f k) +{-# INLINE previewsWithKey #-} + +-- | Map an indexed optic to a monoid and combine the results. +-- +-- @since 0.0.3 +foldsWithKey :: Monoid k => AIxfold r k s a -> (k -> a -> r) -> s -> r +foldsWithKey o f = curry (folds o $ uncurry f) mempty +{-# INLINE foldsWithKey #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +cofoldsWithKey :: Monoid k => ACxfold r k t b -> (k -> r -> b) -> r -> t +cofoldsWithKey o f = flip (cofolds o $ flip f) mempty +{-# INLINE cofoldsWithKey #-} + +-- | Collect the fock of an indexed optic into a list of index-value pairs. +-- +-- @ +-- 'lists' l ≡ 'map' 'snd' '.' 'listsWithKey' l +-- @ +-- +-- >>> listsWithKey (ix (Sum 1) traversed) ["foo","bar"] +-- [(Sum {getSum = 0},"foo"),(Sum {getSum = 1},"bar")] +-- +-- @since 0.0.3 +listsWithKey :: Monoid k => AIxfold (Endo [(k, a)]) k s a -> s -> [(k, a)] +listsWithKey o = foldsrWithKey o (\k a -> ((k,a):)) [] +{-# INLINE listsWithKey #-} + +infix 8 ^%% + +-- | Infix version of 'listsWithKey'. +-- +-- @since 0.0.3 +(^%%) :: Monoid k => s -> AIxfold (Endo [(k, a)]) k s a -> [(k, a)] +(^%%) = flip listsWithKey +{-# INLINE (^%%) #-} + +-- | Indexed right fold over an indexed optic. +-- +-- @ +-- 'foldsr' o ≡ 'foldsrWithKey' o '.' 'const' +-- 'foldrWithKey' f ≡ 'foldsrWithKey' 'ixfolded' f +-- @ +-- +-- @since 0.0.3 +foldsrWithKey :: Monoid k => AIxfold (Endo r) k s a -> (k -> a -> r -> r) -> r -> s -> r +foldsrWithKey o f r = (`appEndo` r) . foldsWithKey o (\j -> Endo . f j) +{-# INLINE foldsrWithKey #-} + +-- | Left fold over an indexed optic. +-- +-- @ +-- 'foldsl' o ≡ 'foldslWithKey' o '.' 'const' +-- 'foldlWithKey' f ≡ 'foldslWithKey' 'ixfolded' f +-- @ +-- +-- @since 0.0.3 +foldslWithKey :: Monoid k => AIxfold ((Endo-Dual) r) k s a -> (k -> r -> a -> r) -> r -> s -> r +foldslWithKey o f r = (`appEndo` r) . getDual . foldsWithKey o (\k -> Dual . Endo . flip (f k)) +{-# INLINE foldslWithKey #-} + +-- | Strict right fold over an indexed optic. +-- +-- @ +-- 'foldsr'' o ≡ 'foldsrWithKey'' o '.' 'const' +-- 'foldrWithKey'' f ≡ 'foldsrWithKey'' 'ixfolded' f +-- @ +-- +-- @since 0.0.3 +foldsrWithKey' :: Monoid k => AIxfold ((Endo-Dual) (Endo r)) k s a -> (k -> a -> r -> r) -> r -> s -> r +foldsrWithKey' o f r s = foldslWithKey o f' (Endo id) s `appEndo` r where f' k (Endo acc) x = Endo $ \ z -> acc $! f k x z +{-# INLINE foldsrWithKey' #-} + +-- | Strict left fold over an indexed optic. +-- +-- @ +-- 'foldsl'' o ≡ 'foldslWithKey'' o '.' 'const' +-- 'foldlWithKey'' f ≡ 'foldslWithKey'' 'ixfolded' f +-- @ +-- +-- @since 0.0.3 +foldslWithKey' :: Monoid k => AIxfold (Endo (Endo r)) k s a -> (k -> r -> a -> r) -> r -> s -> r +foldslWithKey' o f r s = foldsrWithKey o f' (Endo id) s `appEndo` r where f' k x (Endo acc) = Endo $ \z -> acc $! f k z x +{-# INLINE foldslWithKey' #-} + +-- | Monadic right fold over an indexed optic. +-- +-- @ +-- 'foldsrM' ≡ 'ixfoldrM' '.' 'const' +-- @ +-- +-- @since 0.0.3 +foldsrMWithKey :: Monoid k => Monad m => AIxfold ((Endo-Dual) (EndoM m r)) k s a -> (k -> a -> r -> m r) -> r -> s -> m r +foldsrMWithKey o f r xs = foldslWithKey o f' mempty xs `appEndoM` r where f' k e a = e <> EndoM (f k a) +{-# INLINE foldsrMWithKey #-} + +-- | Monadic left fold over an indexed optic. +-- +-- @ +-- 'foldslM' ≡ 'foldslMWithKey' '.' 'const' +-- @ +-- +-- @since 0.0.3 +foldslMWithKey :: Monoid k => Monad m => AIxfold (Endo (EndoM m r)) k s a -> (k -> r -> a -> m r) -> r -> s -> m r +foldslMWithKey o f r xs = foldsrWithKey o f' mempty xs `appEndoM` r where f' k a e = e <> EndoM (flip (f k) a) +{-# INLINE foldslMWithKey #-} + +-- | Applicative fold over an indexed optic. +-- +-- @ +-- 'traversesWithKey_' 'ixfolded' ≡ 'traverseWithKey_' +-- @ +-- +-- @since 0.0.3 +traversesWithKey_ :: Monoid k => Applicative f => AIxfold (Endo (f ())) k s a -> (k -> a -> f r) -> s -> f () +traversesWithKey_ p f = foldsrWithKey p (\k a fu -> void (f k a) *> fu) (pure ()) +{-# INLINE traversesWithKey_ #-} + +--------------------------------------------------------------------- +-- EndoM +--------------------------------------------------------------------- + +newtype EndoM m a = EndoM { appEndoM :: a -> m a } + +instance Monad m => Semigroup (EndoM m a) where + (EndoM f) <> (EndoM g) = EndoM (f <=< g) + {-# INLINE (<>) #-} + +instance Monad m => Monoid (EndoM m a) where + mempty = EndoM return + {-# INLINE mempty #-} + + mappend = (<>) + {-# INLINE mappend #-} diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Import.hs b/profunctor-optics/src/Data/Profunctor/Optic/Import.hs index e81649b..7900d76 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Import.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Import.hs @@ -3,10 +3,13 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE PolyKinds #-} {-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE ConstraintKinds #-} module Data.Profunctor.Optic.Import ( type (+) + , type (-) , (&) -- * Operations on (->) profunctors , rgt @@ -39,7 +42,6 @@ import Control.Applicative as Export (liftA2, Alternative(..)) import Control.Coapplicative as Export hiding (apply, branch) import Control.Category as Export hiding ((.), id) import Control.Monad as Export hiding (void, join) -import Data.Bifunctor as Export import Data.Bool as Export import Data.Distributive as Export import Data.Foldable as Export (foldr, foldr') @@ -49,11 +51,11 @@ import Data.Functor.Apply as Export import Data.Functor.Coapply as Export hiding (apply, branch) import Data.Semigroup.Foldable as Export import Data.Semigroup.Traversable as Export -import Data.Semiring as Export hiding (eval) import Data.Functor.Compose as Export import Data.Functor.Const as Export import Data.Functor.Contravariant as Export import Data.Functor.Identity as Export +import Data.Monoid as Export import Data.Profunctor.Unsafe as Export import Data.Profunctor.Types as Export import Data.Profunctor.Strong as Export (Strong(..), Costrong(..)) @@ -67,6 +69,9 @@ import Data.Void as Export import Test.Logic import Prelude as Export hiding (Num(..),subtract,sum,product,(^),foldl,foldl1) +-- | Hyphenation operator. +type (g - f) a = f (g a) + branch :: (a -> Bool) -> b -> c -> a -> b + c branch f y z x = if f x then Right z else Left y {-# INLINE branch #-} diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Iso.hs b/profunctor-optics/src/Data/Profunctor/Optic/Iso.hs index 505970b..2fcabfe 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Iso.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Iso.hs @@ -14,7 +14,7 @@ module Data.Profunctor.Optic.Iso ( , Iso' , iso , isoVl - , mapping + , fmapping , contramapping , dimapping , toYoneda @@ -31,24 +31,33 @@ module Data.Profunctor.Optic.Iso ( , generic1 , adjuncted , tabulated - , involuted - , flipped - , curried - , excised + , sieved + , cosieved , unzipped , cozipped + , pair' + , maybe' + , either' , swapped , coswapped , associated , coassociated - , sieved - , cosieved + , excised + , flipped + , involuted + , uncurried + , strict + , chunked + , unpacked + , reversed -- * Operators - , reover , op , au , aup , ala + , reover + , reixes + , recxes , withIso -- * Auxilliary Types , Re(..) @@ -61,11 +70,17 @@ import Data.Coerce import Data.Functor.Adjunction hiding (adjuncted) import Data.Maybe (fromMaybe) import Data.Profunctor.Optic.Carrier +import Data.Profunctor.Optic.Combinator import Data.Profunctor.Optic.Import import Data.Profunctor.Optic.Types import Data.Profunctor.Yoneda (Coyoneda(..), Yoneda(..)) - +import Data.Sequences (IsSequence, LazySequence(..)) +import Data.MonoTraversable (Element) import qualified Data.Functor.Rep as F +import qualified Data.Sequences as S +import qualified Data.Strict.Either as E' +import qualified Data.Strict.Maybe as M' +import qualified Data.Strict.Tuple as T' import qualified Control.Monad as M (join) import qualified GHC.Generics as G @@ -126,16 +141,12 @@ isoVl abst = iso f g -- | Lift an 'Iso' into a pair of functors. -- -mapping :: Functor f => Functor g => AIso s t a b -> Iso (f s) (g t) (f a) (g b) -mapping l = withIso l $ \sa bt -> iso (fmap sa) (fmap bt) -{-# INLINE mapping #-} +fmapping :: Functor f => Functor g => AIso s t a b -> Iso (f s) (g t) (f a) (g b) +fmapping l = withIso l $ \sa bt -> iso (fmap sa) (fmap bt) +{-# INLINE fmapping #-} -- | Lift an 'Iso' into a pair of 'Contravariant' functors. -- --- @ --- contramapping :: 'Contravariant' f => 'Iso' s t a b -> 'Iso' (f a) (f b) (f s) (f t) --- @ --- contramapping :: Contravariant f => Contravariant g => AIso s t a b -> Iso (f a) (g b) (f s) (g t) contramapping f = withIso f $ \sa bt -> iso (contramap sa) (contramap bt) {-# INLINE contramapping #-} @@ -213,7 +224,7 @@ wrapped :: Newtype s => Iso' s (O s) wrapped = dimap unpack pack {-# INLINE wrapped #-} --- | Work between newtype wrappers. +-- | An 'Iso' between newtype wrappers. -- -- >>> Const "hello" & rewrapped ..~ Prelude.length & getConst -- 5 @@ -222,13 +233,13 @@ rewrapped :: Newtype s => Newtype t => Iso s t (O s) (O t) rewrapped = withIso wrapped $ \ sa _ -> withIso wrapped $ \ _ bt -> iso sa bt {-# INLINE rewrapped #-} --- | Variant of 'rewrapped' that ignores its argument. +-- | A variant of 'rewrapped' that ignores its argument. -- rewrapped' :: Newtype s => Newtype t => (O s -> s) -> Iso s t (O s) (O t) rewrapped' _ = rewrapped {-# INLINE rewrapped' #-} --- | Obtain an 'Iso' from a 'Generic' representation. +-- | An 'Iso' between 'Generic' representations. -- -- >>> view (generic . re generic) "hello" :: String -- "hello" @@ -237,13 +248,13 @@ generic :: G.Generic a => G.Generic b => Iso a b (G.Rep a c) (G.Rep b c) generic = iso G.from G.to {-# INLINE generic #-} --- | Obtain an 'Iso' from a 'Generic1' representation. +-- | An 'Iso' between 'Generic1' representations. -- generic1 :: G.Generic1 f => G.Generic1 g => Iso (f a) (g b) (G.Rep1 f a) (G.Rep1 g b) generic1 = iso G.from1 G.to1 {-# INLINE generic1 #-} --- | Obtain an 'Iso' from a functor and its adjoint. +-- | An 'Iso' between a functor and its adjoint. -- -- Useful for converting between lens-like optics and grate-like optics: -- @@ -255,58 +266,23 @@ adjuncted :: Adjunction f u => Iso (f a -> b) (f s -> t) (a -> u b) (s -> u t) adjuncted = iso leftAdjunct rightAdjunct {-# INLINE adjuncted #-} --- | Obtain an 'Iso' from a functor and its function representation. +-- | An 'Iso' between a functor and its Yoneda representation. -- tabulated :: F.Representable f => F.Representable g => Iso (f a) (g b) (F.Rep f -> a) (F.Rep g -> b) tabulated = iso F.index F.tabulate {-# INLINE tabulated #-} --- | Obtain an 'Iso' from a function that is its own inverse. --- --- @ --- 'involuted' ≡ 'Control.Monad.join' 'iso' --- @ --- --- >>> "live" ^. involuted reverse --- "evil" --- --- >>> involuted reverse ..~ ('d':) $ "live" --- "lived" --- -involuted :: (s -> a) -> Iso s a a s -involuted = M.join iso -{-# INLINE involuted #-} - --- | Flip two arguments of a function. --- --- >>> (view flipped (,)) 1 2 --- (2,1) --- -flipped :: Iso (a -> b -> c) (d -> e -> f) (b -> a -> c) (e -> d -> f) -flipped = iso flip flip -{-# INLINE flipped #-} - --- | Curry a function. --- --- >>> (fst ^. invert curried) 3 4 --- 3 +-- | TODO: Document -- -curried :: Iso (a -> b -> c) (d -> e -> f) ((a , b) -> c) ((d , e) -> f) -curried = iso uncurry curry -{-# INLINE curried #-} +sieved :: ((a -> b) -> s -> t) -> Iso s t (Index s x x) (Index s a b) +sieved abst = iso (flip Index id) (\(Index s ab) -> abst ab s) +{-# INLINE sieved #-} --- | Excise a single value from a type. --- --- >>> review (excised "foo") "foo" --- Nothing --- >>> review (excised "foo") "foobar" --- Just "foobar" +-- | TODO: Document -- -excised :: Eq a => a -> Iso' (Maybe a) a -excised a = iso (fromMaybe a) g - where g a1 | a1 == a = Nothing - | otherwise = Just a1 -{-# INLINE excised #-} +cosieved :: ((a -> b) -> s -> t) -> Iso s t (Coindex t b a) (Coindex t x x) +cosieved abst = iso (\s -> Coindex $ \ab -> abst ab s) trivial +{-# INLINE cosieved #-} -- | A right adjoint admits an intrinsic notion of zipping. -- @@ -320,6 +296,27 @@ cozipped :: Adjunction f u => Iso ((f a) + (f b)) ((f c) + (f d)) (f (a + b)) (f cozipped = iso uncozipL cozipL {-# INLINE cozipped #-} +-- | An 'Iso' between strict & lazy variants of /(,)/. +-- +-- @since 0.0.3 +pair' :: Iso (a , b) (c , d) (T'.Pair a b) (T'.Pair c d) +pair' = iso (uncurry (T'.:!:)) (T'.fst &&& T'.snd) +{-# INLINE pair' #-} + +-- | An 'Iso' between strict & lazy variants of /Maybe/. +-- +-- @since 0.0.3 +maybe' :: Iso (Maybe a) (Maybe b) (M'.Maybe a) (M'.Maybe b) +maybe' = iso (maybe M'.Nothing M'.Just) (M'.maybe Nothing Just) +{-# INLINE maybe' #-} + +-- | An 'Iso' between strict & lazy variants of /Either/. +-- +-- @since 0.0.3 +either' :: Iso (Either a b) (Either c d) (E'.Either a b) (E'.Either c d) +either' = iso (either E'.Left E'.Right) (E'.either Left Right) +{-# INLINE either' #-} + -- | Swap sides of a product. -- swapped :: Iso (a , b) (c , d) (b , a) (d , c) @@ -332,45 +329,92 @@ coswapped :: Iso (a + b) (c + d) (b + a) (d + c) coswapped = iso eswap eswap {-# INLINE coswapped #-} --- | 'Iso' defined by left-association of nested tuples. +-- | An 'Iso' defined by left-association of nested tuples. -- associated :: Iso (a , (b , c)) (d , (e , f)) ((a , b) , c) ((d , e) , f) associated = iso assocl assocr {-# INLINE associated #-} --- | 'Iso' defined by left-association of nested tuples. +-- | An 'Iso' defined by left-association of nested tuples. -- coassociated :: Iso (a + (b + c)) (d + (e + f)) ((a + b) + c) ((d + e) + f) coassociated = iso eassocl eassocr {-# INLINE coassociated #-} --- | TODO: Document +-- | Excise a single value from a type. -- -sieved :: ((a -> b) -> s -> t) -> Iso s t (Index s x x) (Index s a b) -sieved abst = dimap (flip Index id) (\(Index s ab) -> abst ab s) -{-# INLINE sieved #-} - --- | TODO: Document +-- >>> review (excised "foo") "foo" +-- Nothing +-- >>> review (excised "foo") "foobar" +-- Just "foobar" -- -cosieved :: ((a -> b) -> s -> t) -> Iso s t (Coindex t b a) (Coindex t x x) -cosieved abst = dimap (\s -> Coindex $ \ab -> abst ab s) trivial -{-# INLINE cosieved #-} +excised :: Eq a => a -> Iso' (Maybe a) a +excised a = iso (fromMaybe a) g + where g a1 | a1 == a = Nothing + | otherwise = Just a1 +{-# INLINE excised #-} ---------------------------------------------------------------------- --- Operators ---------------------------------------------------------------------- +-- | Flip two arguments of a function. +-- +-- >>> (view flipped (,)) 1 2 +-- (2,1) +-- +flipped :: Iso (a -> b -> c) (d -> e -> f) (b -> a -> c) (e -> d -> f) +flipped = iso flip flip +{-# INLINE flipped #-} --- | Given a conversion on one side of an 'Iso', recover the other. +-- | An 'Iso' defined by a function that is its own inverse. -- -- @ --- 'reover' ≡ 'over' '.' 're' +-- 'involuted' ≡ 'Control.Monad.join' 'iso' -- @ -- --- Compare 'Data.Profunctor.Optic.Setter.over'. +-- >>> "live" ^. involuted reverse +-- "evil" -- -reover :: AIso s t a b -> (t -> s) -> b -> a -reover o = withIso o $ \sa bt ts -> sa . ts . bt -{-# INLINE reover #-} +-- >>> "live" & involuted reverse ..~ ('d':) +-- "lived" +-- +involuted :: (s -> a) -> Iso s a a s +involuted = M.join iso +{-# INLINE involuted #-} + +-- | Uncurry a function. +-- +-- >>> (fst ^. invert uncurried) 3 4 +-- 3 +-- +uncurried :: Iso (a -> b -> c) (d -> e -> f) ((a , b) -> c) ((d , e) -> f) +uncurried = iso uncurry curry +{-# INLINE uncurried #-} + +-- | An 'Iso' between strict & lazy variants of a sequence. +-- +strict :: LazySequence l s => Iso' l s +strict = iso S.toStrict S.fromStrict +{-# INLINE strict #-} + +-- | TODO: Document +-- +chunked :: LazySequence l s => Iso' l [s] +chunked = iso S.toChunks S.fromChunks +{-# INLINE chunked #-} + +-- | TODO: Document +-- +unpacked :: IsSequence s => Iso' s [Element s] +unpacked = iso S.unpack S.pack +{-# INLINE unpacked #-} + +-- | Reverse a sequence. +-- +reversed :: IsSequence s => Iso' s s +reversed = iso S.reverse S.reverse +{-# INLINE reversed #-} + +--------------------------------------------------------------------- +-- Operators +--------------------------------------------------------------------- -- | Based on /ala/ from Conor McBride's work on Epigram. -- @@ -423,3 +467,27 @@ aup o = withIso o $ \sa bt f g -> fmap bt (f (rmap sa g)) ala :: Newtype s => Newtype t => Functor f => (O s -> s) -> ((O t -> t) -> f s) -> f (O s) ala = au . rewrapped' {-# INLINE ala #-} + +-- | Given a conversion on one side of an 'Iso', recover the other. +-- +-- @ +-- 'reover' ≡ 'over' '.' 're' +-- @ +-- +-- Compare 'Data.Profunctor.Optic.Setter.over'. +-- +reover :: AIso s t a b -> (t -> s) -> b -> a +reover o = withIso o $ \sa bt ts -> sa . ts . bt +{-# INLINE reover #-} + +-- | Remap the indices of an indexed optic. +-- +reixes :: Profunctor p => AIso' k1 k2 -> Ixoptic p k1 s t a b -> Ixoptic p k2 s t a b +reixes o = withIso o reix +{-# INLINE reixes #-} + +-- | Remap the indices of a coindexed optic. +-- +recxes :: Profunctor p => AIso' k1 k2 -> Cxoptic p k1 s t a b -> Cxoptic p k2 s t a b +recxes o = withIso o recx +{-# INLINE recxes #-} diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Lens.hs b/profunctor-optics/src/Data/Profunctor/Optic/Lens.hs index f877b83..ce40106 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Lens.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Lens.hs @@ -5,6 +5,7 @@ {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeFamilies #-} +{-# OPTIONS_GHC -fno-warn-duplicate-exports #-} module Data.Profunctor.Optic.Lens ( -- * Lens Lens @@ -12,63 +13,81 @@ module Data.Profunctor.Optic.Lens ( , Colens , Colens' , lens + , ixlens , lensVl - , matching - , cloneLens - , cloneLensVl + , ixlensVl + , grate , colens + , cxlens + , grateVl , colensVl + , cxlensVl + , inside + , matching , comatching - , cloneColens - -- * Grate - , Grate - , Grate' - , grate - , grateVl , inverting - , cloneGrate - , cloneGrateVl + , cloneLens + , cloneLensVl + , cloneColens + , cloneColensVl -- * Optics + , first + , ixfirst + , cofirst + , cxfirst + , second + , ixsecond + , cosecond + , cxsecond + , closed + , cxclosed , united , voided + , contained , represented , distributed , endomorphed - , precomposed - , dotted , continued , continuedT , calledCC -- * Operators - , zipsWith0 - , zipsWith2 + , coview + , zipsWith + , ozipsWith , zipsWith3 , zipsWith4 , zipsWithF + , zipsWithKey + , intersectsMap + , differencesMap + , intersectsWithMap , toPastro , toTambara , toClosure , toEnvironment , withLens , withColens - , withGrate + , withIxlens + , withCxlens -- * Classes , Strong(..) - , Costrong(..) , Closed(..) ) where import Control.Monad.Cont +import Data.Containers (PolyMap(..), IsSet(..), MonoZip(..)) import Data.Distributive +import Data.MonoTraversable as M (Element) import Data.Monoid (Endo(..)) import Data.Profunctor.Closed +import Data.Profunctor.Rep (unfirstCorep, unsecondCorep) import Data.Profunctor.Optic.Carrier import Data.Profunctor.Optic.Import import Data.Profunctor.Optic.Iso import Data.Profunctor.Optic.Types import Data.Profunctor.Strong -import Data.Semimodule.Free import qualified Data.Functor.Rep as F +import qualified Data.Containers as C -- $setup -- >>> :set -XNoOverloadedStrings @@ -76,7 +95,7 @@ import qualified Data.Functor.Rep as F -- >>> :set -XTypeFamilies -- >>> :set -XFlexibleContexts -- >>> :set -XTupleSections --- >>> import Control.Arrow +-- >>> import Control.Arrow ((&&&)) -- >>> import Control.Monad.Reader -- >>> import Data.Int -- >>> import Data.Complex @@ -84,6 +103,10 @@ import qualified Data.Functor.Rep as F -- >>> import Data.Function ((&)) -- >>> import Data.List as L -- >>> import Data.Monoid (Endo(..)) +-- >>> import Data.Semigroup +-- >>> import qualified Data.Bifunctor as B +-- >>> import qualified Data.ByteString as B +-- >>> import qualified Data.ByteString.Char8 as C -- >>> :load Data.Profunctor.Optic --------------------------------------------------------------------- @@ -108,6 +131,27 @@ lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b lens sa sbt = dimap (id &&& sa) (uncurry sbt) . second' {-# INLINE lens #-} +-- | Obtain an indexed 'Lens' from an indexed getter and a setter. +-- +-- Compare 'lens' and 'Data.Profunctor.Optic.Traversal.itraversal'. +-- +-- /Caution/: In order for the generated optic to be well-defined, +-- you must ensure that the input functions constitute a legal +-- indexed lens: +-- +-- * @snd . sia (sbt s a) ≡ a@ +-- +-- * @sbt s (snd $ sia s) ≡ s@ +-- +-- * @sbt (sbt s a1) a2 ≡ sbt s a2@ +-- +-- See 'Data.Profunctor.Optic.Property'. +-- +-- @since 0.0.3 +ixlens :: (s -> (k , a)) -> (s -> b -> t) -> Ixlens k s t a b +ixlens ska sbt = ixlensVl $ \kab s -> sbt s <$> uncurry kab (ska s) +{-# INLINE ixlens #-} + -- | Transform a Van Laarhoven lens into a profunctor lens. -- -- Compare 'Data.Profunctor.Optic.Lens.grateVl' and 'Data.Profunctor.Optic.Traversal.traversalVl'. @@ -130,37 +174,68 @@ lensVl :: (forall f. Functor f => (a -> f b) -> s -> f t) -> Lens s t a b lensVl abst = dimap ((info &&& vals) . abst (flip Index id)) (uncurry id . swap) . first' {-# INLINE lensVl #-} --- | Obtain a 'Lens' from its free tensor representation. +-- | Transform an indexed Van Laarhoven lens into an indexed profunctor 'Lens'. -- -matching :: (s -> (c , a)) -> ((c , b) -> t) -> Lens s t a b -matching sca cbt = dimap sca cbt . second' - --- | TODO: Document +-- An 'Ixlens' is a valid 'Ixtraversal'. Compare 'Data.Profunctor.Optic.Traversal.itraversalVl'. -- -cloneLens :: ALens s t a b -> Lens s t a b -cloneLens o = withLens o lens - --- | Extract the higher order function that characterizes a 'Lens'. +-- /Caution/: In order for the generated optic to be well-defined, +-- you must ensure that the input satisfies the following properties: -- --- The lens laws can be stated in terms of 'withLens': --- --- Identity: --- --- @ --- cloneLensVl o Identity ≡ Identity --- @ +-- * @iabst (const Identity) ≡ Identity@ +-- +-- * @fmap (iabst $ const f) . (iabst $ const g) ≡ getCompose . iabst (const $ Compose . fmap f . g)@ +-- +-- More generally, a profunctor optic must be monoidal as a natural +-- transformation: -- --- Composition: +-- * @o id ≡ id@ +-- +-- * @o ('Data.Profunctor.Composition.Procompose' p q) ≡ 'Data.Profunctor.Composition.Procompose' (o p) (o q)@ +-- +-- See 'Data.Profunctor.Optic.Property'. +-- +-- @since 0.0.3 +ixlensVl :: (forall f. Functor f => (k -> a -> f b) -> s -> f t) -> Ixlens k s t a b +ixlensVl f = lensVl $ \iab -> f (curry iab) . snd +{-# INLINE ixlensVl #-} + +-- | Obtain a 'Colens' from a nested continuation. +-- +-- The resulting optic is the corepresentable counterpart to 'Lens', +-- and sits between 'Iso' and 'Setter'. +-- +-- A 'Colens' lets you lift a profunctor through any representable +-- functor (aka Naperian container). In the special case where the +-- indexing type is finitary (e.g. 'Bool') then the tabulated type is +-- isomorphic to a fied length vector (e.g. 'V2 a'). +-- +-- The identity container is representable, and representable functors +-- are closed under composition. +-- +-- See +-- section 4.6 for more background on 'Colens's, and compare to the +-- /lens-family/ . +-- +-- /Caution/: In order for the generated optic to be well-defined, +-- you must ensure that the input function satisfies the following +-- properties: +-- +-- * @sabt ($ s) ≡ s@ +-- +-- * @sabt (\k -> f (k . sabt)) ≡ sabt (\k -> f ($ k))@ +-- +-- More generally, a profunctor optic must be monoidal as a natural +-- transformation: -- --- @ --- Compose . fmap (cloneLensVl o f) . cloneLensVl o g ≡ cloneLensVl o (Compose . fmap f . g) --- @ +-- * @o id ≡ id@ +-- +-- * @o ('Data.Profunctor.Composition.Procompose' p q) ≡ 'Data.Profunctor.Composition.Procompose' (o p) (o q)@ -- -- See 'Data.Profunctor.Optic.Property'. -- -cloneLensVl :: ALens s t a b -> (forall f . Functor f => (a -> f b) -> s -> f t) -cloneLensVl o ab s = withLens o $ \sa sbt -> sbt s <$> ab (sa s) -{-# INLINE cloneLensVl #-} +grate :: (((s -> a) -> b) -> t) -> Colens s t a b +grate f = dimap (flip ($)) f . closed +{-# INLINE grate #-} -- | Obtain a 'Colens' from a getter and setter. -- @@ -187,7 +262,31 @@ cloneLensVl o ab s = withLens o $ \sa sbt -> sbt s <$> ab (sa s) -- See 'Data.Profunctor.Optic.Property'. -- colens :: (b -> s -> a) -> (b -> t) -> Colens s t a b -colens bsa bt = unsecond . dimap (uncurry bsa) (id &&& bt) +colens bsa bt = cosecond . dimap (uncurry bsa) (id &&& bt) + +-- | TODO: Document +-- +-- @since 0.0.3 +cxlens :: (((s -> a) -> k -> b) -> t) -> Cxlens k s t a b +cxlens f = cxlensVl $ \aib s -> f $ \sa -> aib (fmap sa s) +{-# INLINE cxlens #-} + +-- | Transform a Van Laarhoven grate into a profunctor grate. +-- +-- Compare 'Data.Profunctor.Optic.Lens.lensVl' & 'Data.Profunctor.Optic.Traversal.cotraversalVl'. +-- +-- /Caution/: In order for the generated family to be well-defined, +-- you must ensure that the traversal1 law holds for the input function: +-- +-- * @abst runIdentity ≡ runIdentity@ +-- +-- * @abst f . fmap (abst g) ≡ abst (f . fmap g . getCompose) . Compose@ +-- +-- See 'Data.Profunctor.Optic.Property'. +-- +grateVl :: (forall f. Functor f => (f a -> b) -> f s -> t) -> Colens s t a b +grateVl o = dimap (curry eval) ((o trivial) . Coindex) . closed +{-# INLINE grateVl #-} -- | Transform a Van Laarhoven colens into a profunctor colens. -- @@ -206,7 +305,35 @@ colens bsa bt = unsecond . dimap (uncurry bsa) (id &&& bt) -- However removing the annotation will result in a faulty optic. -- colensVl :: (forall f. Functor f => (t -> f s) -> b -> f a) -> Colens s t a b -colensVl o = unfirst . dimap (uncurry id . swap) ((info &&& vals) . o (flip Index id)) +colensVl o = cofirst . dimap (uncurry id . swap) ((info &&& vals) . o (flip Index id)) + +-- | Transform a coindexed Van Laarhoven grate into a coindexed profunctor grate. +-- +-- @since 0.0.3 +cxlensVl :: (forall f. Functor f => (f a -> k -> b) -> f s -> t) -> Cxlens k s t a b +cxlensVl f = grateVl $ \aib -> const . f aib +{-# INLINE cxlensVl #-} + +-- | Lift a 'Lens' so it can run under a function (or other corepresentable profunctor). +-- +-- @ +-- 'inside' :: 'Lens' s t a b -> 'Lens' (e -> s) (e -> t) (e -> a) (e -> b) +-- @ +-- +-- >>> (\x -> (x-1,x+1)) ^. inside first $ 5 +-- 4 +-- +inside :: Corepresentable p => ALens s t a b -> Lens (p e s) (p e t) (p e a) (p e b) +inside l = lensVl $ \f es -> o es <$> f (k es) where + k es = cotabulate $ \ e -> info $ cloneLensVl l sell (cosieve es e) + o es ea = cotabulate $ \ e -> flip vals (cosieve ea e) $ cloneLensVl l sell (cosieve es e) + sell x = Index x id +{-# INLINE inside #-} + +-- | Obtain a 'Lens' from its free tensor representation. +-- +matching :: (s -> (c , a)) -> ((c , b) -> t) -> Lens s t a b +matching sca cbt = dimap sca cbt . second' -- | Obtain a 'Colens' from its free tensor representation. -- @@ -215,104 +342,95 @@ colensVl o = unfirst . dimap (uncurry id . swap) ((info &&& vals) . o (flip Inde -- [89,55,34,21,13,8,5,3,2,1,1] -- comatching :: ((c , s) -> a) -> (b -> (c , t)) -> Colens s t a b -comatching csa bct = unsecond . dimap csa bct +comatching csa bct = cosecond . dimap csa bct -- | TODO: Document -- -cloneColens :: AColens s t a b -> Colens s t a b -cloneColens o = withColens o colens - ---------------------------------------------------------------------- --- 'Grate' ---------------------------------------------------------------------- +cloneLens :: ALens s t a b -> Lens s t a b +cloneLens o = withLens o lens --- | Obtain a 'Grate' from a nested continuation. --- --- The resulting optic is the corepresentable counterpart to 'Lens', --- and sits between 'Iso' and 'Setter'. --- --- A 'Grate' lets you lift a profunctor through any representable --- functor (aka Naperian container). In the special case where the --- indexing type is finitary (e.g. 'Bool') then the tabulated type is --- isomorphic to a fied length vector (e.g. 'V2 a'). --- --- The identity container is representable, and representable functors --- are closed under composition. --- --- See --- section 4.6 for more background on 'Grate's, and compare to the --- /lens-family/ . --- --- /Caution/: In order for the generated optic to be well-defined, --- you must ensure that the input function satisfies the following --- properties: --- --- * @sabt ($ s) ≡ s@ --- --- * @sabt (\k -> f (k . sabt)) ≡ sabt (\k -> f ($ k))@ +-- | Extract the higher order function that characterizes a 'Lens'. -- --- More generally, a profunctor optic must be monoidal as a natural --- transformation: +-- The lens laws can be stated in terms of 'withLens': -- --- * @o id ≡ id@ --- --- * @o ('Data.Profunctor.Composition.Procompose' p q) ≡ 'Data.Profunctor.Composition.Procompose' (o p) (o q)@ --- --- See 'Data.Profunctor.Optic.Property'. --- -grate :: (((s -> a) -> b) -> t) -> Grate s t a b -grate sabt = dimap (flip ($)) sabt . closed - --- | Transform a Van Laarhoven grate into a profunctor grate. --- --- Compare 'Data.Profunctor.Optic.Lens.lensVl' & 'Data.Profunctor.Optic.Traversal.cotraversalVl'. --- --- /Caution/: In order for the generated family to be well-defined, --- you must ensure that the traversal1 law holds for the input function: --- --- * @abst runIdentity ≡ runIdentity@ --- --- * @abst f . fmap (abst g) ≡ abst (f . fmap g . getCompose) . Compose@ +-- Identity: +-- +-- @ +-- cloneLensVl o Identity ≡ Identity +-- @ +-- +-- Composition: +-- +-- @ +-- Compose . fmap (cloneLensVl o f) . cloneLensVl o g ≡ cloneLensVl o (Compose . fmap f . g) +-- @ -- -- See 'Data.Profunctor.Optic.Property'. -- -grateVl :: (forall f. Functor f => (f a -> b) -> f s -> t) -> Grate s t a b -grateVl o = dimap (curry eval) ((o trivial) . Coindex) . closed +cloneLensVl :: ALens s t a b -> (forall f . Functor f => (a -> f b) -> s -> f t) +cloneLensVl o ab s = withLens o $ \sa sbt -> sbt s <$> ab (sa s) +{-# INLINE cloneLensVl #-} --- | Construct a 'Grate' from a pair of inverses. +-- | Construct a 'Colens' from a pair of inverses. -- -inverting :: (s -> a) -> (b -> t) -> Grate s t a b +inverting :: (s -> a) -> (b -> t) -> Colens s t a b inverting sa bt = grate $ \sab -> bt (sab sa) +{-# INLINE inverting #-} -- | TODO: Document -- -cloneGrate :: AGrate s t a b -> Grate s t a b -cloneGrate k = withGrate k grate +cloneColens :: AColens s t a b -> Colens s t a b +cloneColens k = withColens k grate +{-# INLINE cloneColens #-} --- | Extract the higher order function that characterizes a 'Grate'. +-- | Extract the higher order function that characterizes a 'Colens'. -- --- The grate laws can be stated in terms or 'withGrate': +-- The grate laws can be stated in terms or 'withColens': -- -- Identity: -- -- @ --- cloneGrateVl o runIdentity ≡ runIdentity +-- cloneColensVl o runIdentity ≡ runIdentity -- @ -- -- Composition: -- -- @ --- cloneGrateVl o f . fmap (cloneGrateVl o g) ≡ cloneGrateVl o (f . fmap g . getCompose) . Compose +-- cloneColensVl o f . fmap (cloneColensVl o g) ≡ cloneColensVl o (f . fmap g . getCompose) . Compose -- @ -- -cloneGrateVl :: AGrate s t a b -> (forall f . Functor f => (f a -> b) -> f s -> t) -cloneGrateVl o ab s = withGrate o $ \sabt -> sabt $ \get -> ab (fmap get s) -{-# INLINE cloneGrateVl #-} +cloneColensVl :: AColens s t a b -> (forall f . Functor f => (f a -> b) -> f s -> t) +cloneColensVl o ab s = withColens o $ \sabt -> sabt $ \sa -> ab (fmap sa s) +{-# INLINE cloneColensVl #-} --------------------------------------------------------------------- -- Optics --------------------------------------------------------------------- +-- | TODO: Document +-- +first :: Lens (a, c) (b, c) a b +first = first' +{-# INLINE first #-} + +-- | TODO: Document +-- +cofirst :: Colens a b (a, c) (b, c) +cofirst = cloneColens unfirstCorep +{-# INLINE cofirst #-} + +-- | TODO: Document +-- +second :: Lens (c, a) (c, b) a b +second = second' +{-# INLINE second #-} + +-- | TODO: Document +-- +cosecond :: Colens a b (c, a) (c, b) +cosecond = cloneColens unsecondCorep +{-# INLINE cosecond #-} + -- | There is a '()' in everything. -- -- >>> "hello" ^. united @@ -322,6 +440,7 @@ cloneGrateVl o ab s = withGrate o $ \sabt -> sabt $ \get -> ab (fmap get s) -- united :: Lens' a () united = lens (const ()) const +{-# INLINE united #-} -- | There is everything in a 'Void'. -- @@ -330,105 +449,157 @@ united = lens (const ()) const -- voided :: Lens' Void a voided = lens absurd const +{-# INLINE voided #-} + +-- | TODO: Document +-- +contained :: IsSet s => Element s -> Lens' s Bool +contained k = lens (C.member k) $ \s b -> if b then C.insertSet k s else C.deleteSet k s +{-# INLINE contained #-} --- | Obtain a 'Grate' from a 'F.Representable' functor. +-- | Obtain a 'Colens' from a 'F.Representable' functor. -- -represented :: F.Representable f => Grate (f a) (f b) a b +represented :: F.Representable f => Colens (f a) (f b) a b represented = tabulated . closed {-# INLINE represented #-} --- | Obtain a 'Grate' from a distributive functor. +-- | Obtain a 'Colens' from a distributive functor. -- -distributed :: Distributive f => Grate (f a) (f b) a b +distributed :: Distributive f => Colens (f a) (f b) a b distributed = grate (`cotraverse` id) {-# INLINE distributed #-} --- | Obtain a 'Grate' from an endomorphism. +-- | Obtain a 'Colens' from an endomorphism. -- --- >>> flip appEndo 2 $ zipsWith2 endomorphed (+) (Endo (*3)) (Endo (*4)) +-- >>> flip appEndo 2 $ zipsWith endomorphed (+) (Endo (*3)) (Endo (*4)) -- 14 -- -endomorphed :: Grate' (Endo a) a +endomorphed :: Colens' (Endo a) a endomorphed = dimap appEndo Endo . closed {-# INLINE endomorphed #-} --- | Obtain a 'Grate' from a linear map. --- -precomposed :: Grate (Lin a b1 c) (Lin a b2 c) (Vec a b1) (Vec a b2) -precomposed = dimap runLin Lin . closed . dimap Vec runVec -{-# INLINE precomposed #-} - --- | Obtain a 'Grate' from a linear functional. --- -dotted :: Grate c (Cov a c) a a -dotted = grate Cov -{-# INLINE dotted #-} - --- | Obtain a 'Grate' from a continuation. +-- | Obtain a 'Colens' from a continuation. -- -- @ --- 'zipsWith2' 'continued' :: (a -> a -> a) -> c -> c -> 'Cont' a c +-- 'zipsWith' 'continued' :: (a -> a -> a) -> c -> c -> 'Cont' a c -- @ -- -continued :: Grate c (Cont a c) a a +continued :: Colens c (Cont a c) a a continued = grate cont {-# INLINE continued #-} --- | Obtain a 'Grate' from a continuation. +-- | Obtain a 'Colens' from a continuation. -- -- @ --- 'zipsWith2' 'continued' :: (m a -> m a -> m a) -> c -> c -> 'ContT' a m c +-- 'zipsWith' 'continued' :: (m a -> m a -> m a) -> c -> c -> 'ContT' a m c -- @ -- -continuedT :: Grate c (ContT a m c) (m a) (m a) +continuedT :: Colens c (ContT a m c) (m a) (m a) continuedT = grate ContT {-# INLINE continuedT #-} -- | Lift the current continuation into the calling context. -- -- @ --- 'zipsWith2' 'calledCC' :: 'MonadCont' m => (m b -> m b -> m s) -> s -> s -> m s +-- 'zipsWith' 'calledCC' :: 'MonadCont' m => (m b -> m b -> m s) -> s -> s -> m s -- @ -- -calledCC :: MonadCont m => Grate a (m a) (m b) (m a) +calledCC :: MonadCont m => Colens a (m a) (m b) (m a) calledCC = grate callCC {-# INLINE calledCC #-} +--------------------------------------------------------------------- +-- Indexed optics +--------------------------------------------------------------------- + +-- | TODO: Document +-- +-- >>> B.first getSum <$> listsWithKey (noix traversed . ixfirst . ix (Sum 1) traversed) [("foo",1), ("bar",2)] +-- [(0,'f'),(1,'o'),(2,'o'),(0,'b'),(1,'a'),(2,'r')] +-- >>> B.first getSum <$> listsWithKey (ix (Sum 3) traversed % ixfirst % ix (Sum 1) traversed) [("foo",1), ("bar",2)] +-- [(0,'f'),(1,'o'),(2,'o'),(3,'b'),(4,'a'),(5,'r')] +-- +-- @since 0.0.3 +ixfirst :: Ixlens k (a , c) (b , c) a b +ixfirst = lmap assocl . first +{-# INLINE ixfirst #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +cxfirst :: Cxlens k a b (a , c) (b , c) +cxfirst = rmap (unfirst . uncurry . flip) . curry' +{-# INLINE cxfirst #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +ixsecond :: Ixlens k (c , a) (c , b) a b +ixsecond = lmap (\(i, (c, a)) -> (c, (i, a))) . second +{-# INLINE ixsecond #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +cxsecond :: Cxlens k a b (c , a) (c , b) +cxsecond = rmap (unsecond . uncurry) . curry' . lmap swap +{-# INLINE cxsecond #-} + +-- | TODO: Document +-- +-- >>> reoverWithKey cxclosed (,) (*2) 5 +-- ((),10) +-- +-- @since 0.0.3 +cxclosed :: Cxlens k (c -> a) (c -> b) a b +cxclosed = rmap flip . closed +{-# INLINE cxclosed #-} + --------------------------------------------------------------------- -- Operators --------------------------------------------------------------------- -- | Set all fields to the given value. -- --- This is essentially a restricted variant of 'Data.Profunctor.Optic.View.review'. +-- Compare 'Data.Profunctor.Optic.View.review'. -- -zipsWith0 :: AGrate s t a b -> b -> t -zipsWith0 o b = withGrate o $ \sabt -> sabt (const b) -{-# INLINE zipsWith0 #-} +coview :: AColens s t a b -> b -> t +coview o b = withColens o $ \sabt -> sabt (const b) +{-# INLINE coview #-} --- | Zip over a 'Grate'. +-- | Zip over a 'Colens'. -- --- @\\f -> 'zipsWith2' 'closed' ('zipsWith2' 'closed' f) ≡ 'zipsWith2' ('closed' . 'closed')@ +-- @\\f -> 'zipsWith' 'closed' ('zipsWith' 'closed' f) ≡ 'zipsWith' ('closed' . 'closed')@ -- -zipsWith2 :: AGrate s t a b -> (a -> a -> b) -> s -> s -> t -zipsWith2 o aab s1 s2 = withGrate o $ \sabt -> sabt $ \get -> aab (get s1) (get s2) -{-# INLINE zipsWith2 #-} +zipsWith :: AColens s t a b -> (a -> a -> b) -> s -> s -> t +zipsWith o f s1 s2 = withColens o $ \sabt -> sabt $ \sa -> f (sa s1) (sa s2) +{-# INLINE zipsWith #-} --- | Zip over a 'Grate' with 3 arguments. +-- | Zip over a mono 'Colens'. +-- +-- >>> ozipsWith closed (+) B.pack B.pack [1..3] +-- "\STX\EOT\ACK" -- -zipsWith3 :: AGrate s t a b -> (a -> a -> a -> b) -> (s -> s -> s -> t) -zipsWith3 o aaab s1 s2 s3 = withGrate o $ \sabt -> sabt $ \sa -> aaab (sa s1) (sa s2) (sa s3) +-- @since 0.0.3 +ozipsWith :: MonoZip a => AColens s t a a -> (Element a -> Element a -> Element a) -> s -> s -> t +ozipsWith o f s1 s2 = withColens o $ \sabt -> sabt $ \sa -> ozipWith f (sa s1) (sa s2) +{-# INLINE ozipsWith #-} + +-- | Zip over a 'Colens' with 3 arguments. +-- +zipsWith3 :: AColens s t a b -> (a -> a -> a -> b) -> (s -> s -> s -> t) +zipsWith3 o f s1 s2 s3 = withColens o $ \sabt -> sabt $ \sa -> f (sa s1) (sa s2) (sa s3) {-# INLINE zipsWith3 #-} --- | Zip over a 'Grate' with 4 arguments. +-- | Zip over a 'Colens' with 4 arguments. -- -zipsWith4 :: AGrate s t a b -> (a -> a -> a -> a -> b) -> (s -> s -> s -> s -> t) -zipsWith4 o aaaab s1 s2 s3 s4 = withGrate o $ \sabt -> sabt $ \sa -> aaaab (sa s1) (sa s2) (sa s3) (sa s4) +zipsWith4 :: AColens s t a b -> (a -> a -> a -> a -> b) -> (s -> s -> s -> s -> t) +zipsWith4 o f s1 s2 s3 s4 = withColens o $ \sabt -> sabt $ \sa -> f (sa s1) (sa s2) (sa s3) (sa s4) {-# INLINE zipsWith4 #-} --- | Extract the higher order function that characterizes a 'Grate'. +-- | Extract the higher order function that characterizes a 'Colens'. -- --- The grate laws can be stated in terms or 'withGrate': +-- The grate laws can be stated in terms or 'withColens': -- -- Identity: -- @@ -442,28 +613,58 @@ zipsWith4 o aaaab s1 s2 s3 s4 = withGrate o $ \sabt -> sabt $ \sa -> aaaab (sa s -- zipsWithF o f . fmap (zipsWithF o g) ≡ zipsWithF o (f . fmap g . getCompose) . Compose -- @ -- -zipsWithF :: Functor f => AGrate s t a b -> (f a -> b) -> f s -> t -zipsWithF = cloneGrateVl +zipsWithF :: Functor f => AColens s t a b -> (f a -> b) -> f s -> t +zipsWithF = cloneColensVl {-# INLINE zipsWithF #-} +-- | TODO: Document +-- +-- @since 0.0.3 +zipsWithKey :: Monoid k => ACxlens k s t a b -> (k -> a -> a -> b) -> s -> s -> t +zipsWithKey o f s1 s2 = withCxlens o $ \sabt -> sabt $ \sa k -> f k (sa s1) (sa s2) +{-# INLINE zipsWithKey #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +intersectsMap :: PolyMap m => AColens s t (m a) (m a) -> s -> s -> t +intersectsMap o = zipsWith o C.intersectionMap +{-# INLINE intersectsMap #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +differencesMap :: PolyMap m => AColens s t (m a) (m a) -> s -> s -> t +differencesMap o = zipsWith o C.differenceMap +{-# INLINE differencesMap #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +intersectsWithMap :: PolyMap m => AColens s t (m a) (m b) -> (a -> a -> b) -> s -> s -> t +intersectsWithMap o f s1 s2 = withColens o $ \sabt -> sabt $ \sa -> C.intersectionWithMap f (sa s1) (sa s2) +{-# INLINE intersectsWithMap #-} + -- | Use a 'Lens' to construct a 'Pastro'. -- toPastro :: ALens s t a b -> p a b -> Pastro p s t toPastro o p = withLens o $ \sa sbt -> Pastro (uncurry sbt . swap) p (\s -> (sa s, s)) +{-# INLINE toPastro #-} -- | Use a 'Lens' to construct a 'Tambara'. -- toTambara :: Strong p => ALens s t a b -> p a b -> Tambara p s t toTambara o p = withLens o $ \sa sbt -> Tambara (first' . lens sa sbt $ p) +{-# INLINE toTambara #-} --- | Use a 'Grate' to construct a 'Closure'. +-- | Use a 'Colens' to construct a 'Closure'. -- -toClosure :: Closed p => AGrate s t a b -> p a b -> Closure p s t -toClosure o p = withGrate o $ \sabt -> Closure (closed . grate sabt $ p) +toClosure :: Closed p => AColens s t a b -> p a b -> Closure p s t +toClosure o p = withColens o $ \sabt -> Closure (closed . grate sabt $ p) {-# INLINE toClosure #-} --- | Use a 'Grate' to construct an 'Environment'. +-- | Use a 'Colens' to construct an 'Environment'. -- -toEnvironment :: Closed p => AGrate s t a b -> p a b -> Environment p s t -toEnvironment o p = withGrate o $ \sabt -> Environment sabt p (curry eval) +toEnvironment :: Closed p => AColens s t a b -> p a b -> Environment p s t +toEnvironment o p = withColens o $ \sabt -> Environment sabt p (curry eval) {-# INLINE toEnvironment #-} diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Machine.hs b/profunctor-optics/src/Data/Profunctor/Optic/Machine.hs index c33f909..43e0dda 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Machine.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Machine.hs @@ -6,264 +6,635 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeFamilies #-} module Data.Profunctor.Optic.Machine ( - -- * Types + -- * Moore Moore - , Mealy - -- * Fold , moore , listing + , packing + , chunking , foldingr + , foldingr' , foldingl + , foldingl' , foldingrM , foldinglM + , traversing_ + , mappingM_ , foldMapping -- * Mealy + , Mealy , mealy , listing1 - , foldingr1 + , packing1 , foldingl1 + , foldingr1 , foldingrM1 , foldinglM1 + , traversing1_ , foldMapping1 - , intercalating -- * Optics - , foldMapped - , foldMapped1 + , head1 + , last1 + , projected , minimized , maximized - , minimizedBy - , maximizedBy , minimizedDef , maximizedDef + , minimizedBy + , maximizedBy , minimizedByDef , maximizedByDef + , foundDef + -- * Sequential optics + , packed + , packed' + , taken + , taken' + , padded + , padded' + , takenWhile + , takenWhile' + , droppedWhile + , droppedWhile' + , filteredBy + , filteredBy' + , filteredBy1 + , broken + , spanned + , partitioned + , partitioned' + , groupedAllOn + , splitWhen + , splitFirst + -- , intercalated -- * Operators + , listl + , listl1 + , steps + , buildl , buildsl + , obuildl + , obuildsl + , buildl1 , buildsl1 - , listsl - , listsl1 - , mconcatsl - , sconcatsl - , foldMapsl - , foldMapsl1 + , obuildl1 + , obuildsl1 + , postscanl + , postscansl + , mconcats + , sconcats + --, heads + --, lasts + --, headsDef + --, lastsDef + , minimizes + , maximizes + , minimizesDef + , maximizesDef + , minimizesBy + , maximizesBy + , minimizesByDef + , maximizesByDef ) where -import Data.List.NonEmpty as NonEmpty +import Data.List.NonEmpty (NonEmpty (..)) import Data.Monoid +import Data.MonoTraversable (MonoFoldable(..), Element) +import Data.NonNull (NonNull) import Data.Semigroup -import Data.Profunctor.Optic.Carrier +import Data.Sequences (IsSequence, LazySequence, Index) +import Data.Profunctor.Optic.Carrier hiding (Index) +import Data.Profunctor.Optic.Combinator import Data.Profunctor.Optic.Import import Data.Profunctor.Optic.Types import Data.Semigroup.Foldable as F1 import qualified Data.Foldable as F import qualified Data.Profunctor.Rep.Foldl as L import qualified Data.Profunctor.Rep.Foldl1 as L1 +import qualified Data.NonNull as NN +import qualified Data.Sequences as MS +import qualified Data.List.NonEmpty as NE +import qualified Data.MonoTraversable as MT + +--import qualified Control.Foldl.ByteString as LB -- $setup -- >>> :set -XNoOverloadedStrings -- >>> :set -XFlexibleContexts -- >>> :set -XTypeApplications +-- >>> :set -XTypeFamilies -- >>> :set -XTupleSections -- >>> :set -XRankNTypes +-- >>> import Data.Char -- >>> import Data.Monoid -- >>> import Data.Semigroup -- >>> import Data.List.NonEmpty (NonEmpty(..)) -- >>> import Data.Function ((&)) -- >>> import Data.Foldable -- >>> import Data.Ord +-- >>> import Data.List ((!!)) +-- >>> import qualified Data.ByteString as B +-- >>> import qualified Data.ByteString.Lazy as BL +-- >>> import qualified Data.ByteString.Char8 as C +-- >>> import qualified Data.ByteString.Lazy.Char8 as CL -- >>> :load Data.Profunctor.Optic --------------------------------------------------------------------- --- 'Foldl' & 'Foldl1' +-- 'Moore' --------------------------------------------------------------------- --- | Obtain a 'Foldl' directly. +-- | Obtain a 'Moore' directly. -- -moore :: (s -> a) -> (forall f. Foldable f => f s -> b -> t) -> Moore s t a b +-- @since 0.0.3 +moore :: (s -> a) -> (forall f. Foldable' f => f s -> b -> t) -> Moore s t a b moore sa sbt p = cotabulate $ \s -> sbt s (cosieve p . fmap sa $ s) {-# INLINE moore #-} -- | A < http://events.cs.bham.ac.uk/syco/strings3-syco5/slides/roman.pdf list lens >. -- +-- @since 0.0.3 listing :: (s -> a) -> ([s] -> b -> t) -> Moore s t a b listing sa sbt = moore sa $ sbt . F.toList {-# INLINE listing #-} -- | TODO: Document -- +-- @since 0.0.3 +packing :: IsSequence s => (Element s -> a) -> (s -> b -> t) -> Moore (Element s) t a b +packing sa sbt = moore sa $ sbt . MS.pack . F.toList +{-# INLINEABLE packing #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +chunking :: LazySequence l s => (s -> a) -> (l -> b -> t) -> Moore s t a b +chunking sa sbt = moore sa $ sbt . MS.fromChunks . F.toList +{-# INLINEABLE chunking #-} + +-- | TODO: Document +-- +-- @since 0.0.3 foldingr :: (s -> a) -> (s -> r -> r) -> r -> (r -> b -> t) -> Moore s t a b foldingr sa h z rbt = moore sa $ rbt . F.foldr h z {-# INLINE foldingr #-} -- | TODO: Document -- +-- @since 0.0.3 +foldingr' :: (s -> a) -> (s -> r -> r) -> r -> (r -> b -> t) -> Moore s t a b +foldingr' sa h z rbt = moore sa $ rbt . F.foldr' h z +{-# INLINE foldingr' #-} + +-- | TODO: Document +-- +-- @since 0.0.3 foldingl :: (s -> a) -> (r -> s -> r) -> r -> (r -> b -> t) -> Moore s t a b foldingl sa h z rbt = moore sa $ rbt . F.foldl h z {-# INLINE foldingl #-} -- | TODO: Document -- +-- @since 0.0.3 +foldingl' :: (s -> a) -> (r -> s -> r) -> r -> (r -> b -> t) -> Moore s t a b +foldingl' sa h z rbt = moore sa $ rbt . F.foldl' h z +{-# INLINE foldingl' #-} + +-- | TODO: Document +-- +-- @since 0.0.3 foldingrM :: Monad m => (s -> a) -> (s -> r -> m r) -> r -> (m r -> b -> t) -> Moore s t a b foldingrM sa h z rbt = moore sa $ rbt . F.foldrM h z {-# INLINE foldingrM #-} -- | TODO: Document -- +-- @since 0.0.3 foldinglM :: Monad m => (s -> a) -> (r -> s -> m r) -> r -> (m r -> b -> t) -> Moore s t a b foldinglM sa h z rbt = moore sa $ rbt . F.foldlM h z {-# INLINE foldinglM #-} -- | TODO: Document -- +-- @since 0.0.3 +traversing_ :: Applicative f => (s -> a) -> (s -> f r) -> (f () -> b -> t) -> Moore s t a b +traversing_ sa h sbt = moore sa $ sbt . F.traverse_ h +{-# INLINE traversing_ #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +mappingM_ :: Monad m => (s -> a) -> (s -> m r) -> (m () -> b -> t) -> Moore s t a b +mappingM_ sa h sbt = moore sa $ sbt . F.mapM_ h +{-# INLINE mappingM_ #-} + +-- | TODO: Document +-- +-- @since 0.0.3 foldMapping :: Monoid r => (s -> a) -> (s -> r) -> (r -> b -> t) -> Moore s t a b foldMapping sa sr rbt = moore sa $ rbt . F.foldMap sr {-# INLINE foldMapping #-} --------------------------------------------------------------------- --- 'Foldl1' +-- 'Mealy' --------------------------------------------------------------------- --- | Obtain a 'Foldl1' directly. +-- | Obtain a 'Mealy' directly. -- -mealy :: (s -> a) -> (forall f. Foldable1 f => f s -> b -> t) -> Mealy s t a b +-- @since 0.0.3 +mealy :: (s -> a) -> (forall f. Foldable1' f => f s -> b -> t) -> Mealy s t a b mealy sa sbt p = cotabulate $ \s -> sbt s (cosieve p . fmap sa $ s) {-# INLINE mealy #-} -- | A non-empty list lens. -- +-- @since 0.0.3 listing1 :: (s -> a) -> (NonEmpty s -> b -> t) -> Mealy s t a b listing1 sa sbt = mealy sa $ sbt . F1.toNonEmpty {-# INLINE listing1 #-} -- | TODO: Document -- +-- @since 0.0.3 +packing1 :: IsSequence s => (Element s -> a) -> (NonNull s -> b -> t) -> Mealy (Element s) t a b +packing1 sa sbt = mealy sa $ sbt . NN.fromNonEmpty . F1.toNonEmpty +{-# INLINE packing1 #-} + +-- | TODO: Document +-- +-- @since 0.0.3 foldingr1 :: (s -> a) -> (s -> s -> s) -> (s -> b -> t) -> Mealy s t a b foldingr1 sa h sbt = mealy sa $ sbt . F.foldr1 h {-# INLINE foldingr1 #-} -- | TODO: Document -- +-- @since 0.0.3 foldingl1 :: (s -> a) -> (s -> s -> s) -> (s -> b -> t) -> Mealy s t a b foldingl1 sa h sbt = mealy sa $ sbt . F.foldl1 h {-# INLINE foldingl1 #-} -- | TODO: Document -- +-- @since 0.0.3 foldingrM1 :: Monad m => (s -> a) -> (s -> s -> m s) -> (m s -> b -> t) -> Mealy s t a b foldingrM1 sa h sbt = mealy sa $ sbt . F1.foldrM1 h {-# INLINE foldingrM1 #-} -- | TODO: Document -- +-- @since 0.0.3 foldinglM1 :: Monad m => (s -> a) -> (s -> s -> m s) -> (m s -> b -> t) -> Mealy s t a b foldinglM1 sa h sbt = mealy sa $ sbt . F1.foldlM1 h {-# INLINE foldinglM1 #-} -- | TODO: Document -- -foldMapping1 :: Semigroup r => (s -> a) -> (s -> r) -> (r -> b -> t) -> Mealy s t a b -foldMapping1 sa sr rbt = mealy sa $ rbt . F1.foldMap1 sr -{-# INLINE foldMapping1 #-} +-- @since 0.0.3 +traversing1_ :: Apply f => (s -> a) -> (s -> f r) -> (f () -> b -> t) -> Mealy s t a b +traversing1_ sa h sbt = mealy sa $ sbt . F1.traverse1_ h +{-# INLINE traversing1_ #-} -- | TODO: Document -- -intercalating :: Semigroup r => (s -> a) -> (s -> r) -> r -> (r -> b -> t) -> Mealy s t a b -intercalating sa sr r rbt = mealy sa $ rbt . F1.intercalateMap1 r sr -{-# INLINE intercalating #-} +-- @since 0.0.3 +foldMapping1 :: Semigroup r => (s -> a) -> (s -> r) -> (r -> b -> t) -> Mealy s t a b +foldMapping1 sa sr rbt = mealy sa $ rbt . F1.foldMap1 sr +{-# INLINE foldMapping1 #-} --------------------------------------------------------------------- -- Optics --------------------------------------------------------------------- --- | TODO: Document +-- | Retain the first out-of-focus part of a lens. -- --- >>> buildsl (foldMapped second') (++) [] id [(Sum 1,"one"),(Sum 2,"two")] --- (Sum {getSum = 3},"onetwo") +-- >>> sconcats (head1 second) Sum getSum $ ("foo",1) :| [("bar",2),("baz",3)] +-- ("foo",6) +-- >>> listl1 (head1 second . last1 first) $ ("key1",(0,"apples")) :| [("key2",(4,"oranges")),("key3",(1,"beets"))] +-- ("key1",(0 :| [4,1],"beets")) -- -foldMapped :: Monoid s => Lens s t a b -> Moore s t a b -foldMapped o = withLens o $ \sa sbt -> foldMapping sa id sbt -{-# INLINE foldMapped #-} +-- @since 0.0.3 +head1 :: Lens s t a b -> Mealy s t a b +head1 o = withLens o $ \sa sbt -> listing1 sa $ sbt . NE.head +{-# INLINE head1 #-} --- | TODO: Document +-- | Retain the last out-of-focus part of a lens. -- -foldMapped1 :: Semigroup s => Lens s t a b -> Mealy s t a b -foldMapped1 o = withLens o $ \sa sbt -> foldMapping1 sa id sbt -{-# INLINE foldMapped1 #-} +-- >>> sconcats (last1 second) Sum getSum $ ("one",1) :| [("two",2),("three",3)] +-- ("three",6) +-- +-- @since 0.0.3 +last1 :: Lens s t a b -> Mealy s t a b +last1 o = withLens o $ \sa sbt -> listing1 sa $ sbt . NE.last +{-# INLINE last1 #-} --- | TODO: Document +-- | Project away a structure. +-- +-- >>> listl1 (projected snd . maximized second) $ ("key1",(0,"apples")) :| [("key2",(4,"oranges")),("key3",(1,"beets"))] +-- (4,"apples" :| ["oranges","beets"]) -- +-- @since 0.0.3 +projected :: (s -> a) -> Moore s b a b +projected sa = moore sa (flip const) +{-# INLINE projected #-} + +-- | Minimize over a lens. +-- +-- @since 0.0.3 minimized :: Ord s => Lens s t a b -> Mealy s t a b minimized o = withLens o $ \sa sbt -> mealy sa $ \fs b -> sbt (F.minimum fs) b {-# INLINE minimized #-} --- | TODO: Document +-- | Maximize over a lens. -- --- >>> listsl1 (maximized second') $ (0,"zero") :| [(1,"one"),(2,"two")] +-- >>> listl1 (maximized second) $ (0,"zero") :| [(1,"one"),(2,"two")] -- (2,"zero" :| ["one","two"]) --- >>> buildsl1 (maximized second') (++) id id $ (0,"zero") :| [(1,"one"),(2,"two")] +-- >>> buildsl1 (maximized second) (++) id id $ (0,"zero") :| [(1,"one"),(2,"two")] -- (2,"zeroonetwo") +-- >>> listl1 (maximized second . minimized first) $ ("key1",(0,"apples")) :| [("key2",(4,"oranges")),("key3",(1,"beets"))] +-- ("key3",(0 :| [4,1],"apples")) -- +-- @since 0.0.3 maximized :: Ord s => Lens s t a b -> Mealy s t a b maximized o = withLens o $ \sa sbt -> mealy sa $ \fs b -> sbt (F.maximum fs) b {-# INLINE maximized #-} --- | TODO: Document +-- | Minimize over a 'Lens' using a default. +-- +-- @since 0.0.3 +minimizedDef :: Ord s => s -> Lens s t a b -> Moore s t a b +minimizedDef s o = withLens o $ \sa sbt -> moore sa $ \fs b -> flip sbt b $ maybe s id $ minimumMay fs +{-# INLINE minimizedDef #-} + +-- | Maximize over a 'Lens' using a default. +-- +-- >>> [(1,"one"),(2,"two")] & maximizedDef (0,[]) second //~ head +-- (2,"one") +-- >>> [(1,"one"),(2,"two")] & swapped . maximizedDef ([],0) first //~ head +-- (2,"one") +-- >>> listl (maximizedDef (0,[]) second) [(1,"one"),(2,"two")] +-- (2,["one","two"]) +-- >>> listl (maximizedDef (0,[]) second . cotraversed1) [(1,"one"),(2,"two")] +-- (2,["ot","nw","eo"]) +-- >>> buildsl (maximizedDef (0,[]) second . cotraversed1) (\x a -> a:x) [] id [(1,"one"),(2,"two")] +-- (2,["to","wn","oe"]) +-- +-- @since 0.0.3 +maximizedDef :: Ord s => s -> Lens s t a b -> Moore s t a b +maximizedDef s o = withLens o $ \sa sbt -> moore sa $ \fs b -> flip sbt b $ maybe s id $ maximumMay fs +{-# INLINE maximizedDef #-} + +-- | Minimize over a 'Lens' using a comparator. -- +-- @since 0.0.3 minimizedBy :: (s -> s -> Ordering) -> Lens s t a b -> Mealy s t a b minimizedBy cmp o = withLens o $ \sa sbt -> mealy sa $ \fs b -> sbt (F.minimumBy cmp fs) b {-# INLINE minimizedBy #-} --- | TODO: Document +-- | Maximize over a 'Lens' using a comparator. -- +-- @since 0.0.3 maximizedBy :: (s -> s -> Ordering) -> Lens s t a b -> Mealy s t a b maximizedBy cmp o = withLens o $ \sa sbt -> mealy sa $ \fs b -> sbt (F.maximumBy cmp fs) b {-# INLINE maximizedBy #-} +-- | Minimize over a 'Lens' using a comparator and a default. +-- +-- @since 0.0.3 +minimizedByDef :: (s -> s -> Ordering) -> s -> Lens s t a b -> Moore s t a b +minimizedByDef cmp s o = withLens o $ \sa sbt -> moore sa $ \fs b -> flip sbt b $ maybe s id $ minimumByMay cmp fs +{-# INLINE minimizedByDef #-} + +-- | Maximize over a 'Lens' using a comparator and a default. +-- +-- @since 0.0.3 +maximizedByDef :: (s -> s -> Ordering) -> s -> Lens s t a b -> Moore s t a b +maximizedByDef cmp s o = withLens o $ \sa sbt -> moore sa $ \fs b -> flip sbt b $ maybe s id $ maximumByMay cmp fs +{-# INLINE maximizedByDef #-} + +-- | Search over the a 'Lens' using a predicate and a default. +-- +-- >>> mconcats (foundDef (const . even . fst) (0,0) second) Sum getSum $ [(1,1),(3,2),(4,3),(6,4)] :: (Int, Int) +-- (4,10) +-- >>> ("key1",(0,"apples")) :| [("key2",(4,"oranges")),("key3",(1,"beets"))] & (minimized second . foundDef (\s b -> snd s == b) (0,mempty) second) /~ "oranges" +-- ("key1",(4,"oranges")) +-- +-- @since 0.0.3 +foundDef :: (s -> b -> Bool) -> s -> Lens s t a b -> Moore s t a b +foundDef p s o = withLens o $ \sa sbt -> moore sa $ \fs b -> flip sbt b $ maybe s id $ F.find (flip p b) fs +{-# INLINE foundDef #-} + +--------------------------------------------------------------------- +-- Sequential optics +--------------------------------------------------------------------- + -- | TODO: Document -- --- >>> listsl (minimizedDef (0,[]) second') [(1,"one"),(2,"two")] --- (1,["one","two"]) --- >>> listsl (minimizedDef (0,[]) second' . cotraversed1) [(1,"one"),(2,"two")] --- (1,["ot","nw","eo"]) +-- @since 0.0.3 +packed :: IsSequence s => Lens s t a b -> Moore (Element s) t a b +packed o = withLens o $ \sa sbt -> packing (sa . MT.opoint) sbt + +-- | TODO: Document -- -minimizedDef :: Ord s => t -> Lens s t a b -> Moore s t a b -minimizedDef t o = withLens o $ \sa sbt -> moore sa $ \fs b -> maybe t (flip sbt b) $ minimumMay fs -{-# INLINE minimizedDef #-} +-- @since 0.0.3 +packed' :: LazySequence l s => Lens l t a b -> Moore s t a b +packed' o = withLens o $ \la lbt -> chunking (la . MS.fromStrict) lbt +{-# INLINE packed' #-} -- | TODO: Document -- -maximizedDef :: Ord s => t -> Lens s t a b -> Moore s t a b -maximizedDef t o = withLens o $ \sa sbt -> moore sa $ \fs b -> maybe t (flip sbt b) $ maximumMay fs -{-# INLINE maximizedDef #-} +-- >>> "foobar" & taken id /~ 3 :: String +-- "foo" +-- >>> listl (taken $ const 3) "foobar" :: String +-- "foo" +-- +-- @since 0.0.3 +taken :: IsSequence s => (b -> Index s) -> Moore (Element s) s (Element s) b +taken f = packing id $ \s b -> MS.take (f b) s -- | TODO: Document -- -minimizedByDef :: (s -> s -> Ordering) -> t -> Lens s t a b -> Moore s t a b -minimizedByDef cmp t o = withLens o $ \sa sbt -> moore sa $ \fs b -> maybe t (flip sbt b) $ minimumByMay cmp fs -{-# INLINE minimizedByDef #-} +-- >>> CL.unpack $ listl (taken' $ const 9) $ fmap C.pack ["foo","bar","baz","bip"] +-- "foobarbaz" +-- >>> CL.unpack $ fmap C.pack ["foo","bar","baz","bip"] & taken' fromIntegral /~ 9 +-- "foobarbaz" +-- >>> CL.unpack $ fmap C.pack ["foo","bar","baz","bip"] & taken' fromIntegral //~ (3 *) . head +-- "foobarbaz" +-- >>> fmap CL.unpack $ (fmap.fmap) C.pack [(0,"zero"),(1,"one"),(2,"two")] & maximizedDef (0,mempty) second . taken' fromIntegral //~ maximum +-- (2,"zero") +-- +-- @since 0.0.3 +taken' :: LazySequence l s => (b -> Index l) -> Moore s l (Index s) b +taken' f = chunking MS.lengthIndex $ \s b -> MS.take (f b) s +{-# INLINE taken' #-} -- | TODO: Document -- -maximizedByDef :: (s -> s -> Ordering) -> t -> Lens s t a b -> Moore s t a b -maximizedByDef cmp t o = withLens o $ \sa sbt -> moore sa $ \fs b -> maybe t (flip sbt b) $ maximumByMay cmp fs -{-# INLINE maximizedByDef #-} +-- @since 0.0.3 +padded :: IsSequence s => Element s -> Moore s [s] (Index s) (Index s) +padded w = listing MS.lengthIndex $ \s b -> fmap (\x -> MS.take b (x <> MS.replicate b w)) s +{-# INLINE padded #-} + +-- | TODO: Document +-- +-- >>> ["foo","barbaz","bippy"] & padded ' ' /~ 4 +-- ["foo ","barb","bipp"] +-- >>> ["foo","barbaz","bippy"] & padded ' ' //~ maximum +-- ["foo ","barbaz","bippy "] +-- +-- @since 0.0.3 +padded' :: LazySequence l s => Element s -> Moore s l (Index s) (Index s) +padded' w = moore MS.lengthIndex $ \bs n -> MS.fromChunks $ + fmap (\s -> MS.take n $ s <> MS.replicate n w) $ F.toList bs +{-# INLINE padded' #-} + +-- | TODO: Document +-- +-- >>> listl (takenWhile id $ const . isAlpha) "foo2bar" :: String +-- "foo" +-- >>> [(1,"one"),(0,"zero"),(2,"two")] & minimizedDef (0,[]) second . takenWhile id (/=) /~ "two" :: (Int,[String]) +-- (0,["one","zero"]) +-- >>> [(1,"one"),(0,"zero"),(2,"two")] & maximizedDef (0,[]) second . takenWhile id (/=) //~ maximum :: (Int,[String]) +-- (2,["one"]) +-- +-- @since 0.0.3 +takenWhile :: IsSequence s => (Element s -> a) -> (Element s -> b -> Bool) -> Moore (Element s) s a b +takenWhile sa sbt = packing sa $ \s b -> MS.takeWhile (flip sbt b) s +{-# INLINE takenWhile #-} + +-- | TODO: Document +-- +-- >>> CL.unpack $ listl (takenWhile' id $ const . isAlpha . chr . fromIntegral) $ fmap C.pack ["foo","bar","2baz"] +-- "foobar" +-- +-- @since 0.0.3 +takenWhile' :: LazySequence l s => (s -> a) -> (Element l -> b -> Bool) -> Moore s l a b +takenWhile' sa sbt = chunking sa $ \s b -> MS.takeWhile (flip sbt b) s +{-# INLINE takenWhile' #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +droppedWhile :: IsSequence s => (Element s -> a) -> (Element s -> b -> Bool) -> Moore (Element s) s a b +droppedWhile sa sbt = packing sa $ \s b -> MS.dropWhile (flip sbt b) s +{-# INLINE droppedWhile #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +droppedWhile' :: LazySequence l s => (s -> a) -> (Element l -> b -> Bool) -> Moore s l a b +droppedWhile' sa sbt = chunking sa $ \s b -> MS.dropWhile (flip sbt b) s +{-# INLINE droppedWhile' #-} + +-- | Filter a sequence of elements using a Moore machine. +-- +-- >>> "foobar" & filteredBy id (==) /~ 'b' :: String +-- "b" +-- >>> "foobar" & filteredBy id (\c i -> ord c /= i) /~ 111 :: String +-- "fbar" +-- >>> "fizbuz" & filteredBy ord (\c i -> ord c /= i) //~ maximum :: String +-- "fibu" +-- >>> ["bob","oob","baz"] & filteredBy head (==) //~ reverse :: [String] +-- ["bob"] +-- >>> ["bob","bob","baz"] & filteredBy head (==) //~ reverse :: [String] +-- [] +-- +-- Take all strings @s@ that pass the following test: +-- +-- * @s@ must not include a /z/ +-- +-- * @s@ must be equal to the concatenation of the first characters in each string in the remaining list +-- +-- >>> ["z","obb","bob","baz"] & (filteredBy head (==) . filteredBy id (/=)) /~ 'z' :: [String] +-- ["obb"] +-- +-- @since 0.0.3 +filteredBy :: IsSequence s => (Element s -> a) -> (Element s -> b -> Bool) -> Moore (Element s) s a b +filteredBy sa sbt = packing sa $ \s b -> MS.filter (flip sbt b) s +{-# INLINE filteredBy #-} + +-- | Filter a chunked sequence of elements using a Moore machine. +-- +-- >>> CL.unpack $ fmap C.pack ["foo","bar"] & filteredBy' (ord . C.head) (/=) /~ 111 +-- "fbar" +-- >>> CL.unpack $ fmap C.pack ["f","i","z","b","u","z"] & filteredBy' (ord . C.head) (\e b -> fromIntegral e /= b) //~ maximum +-- "fibu" +-- >>> CL.unpack $ fmap C.pack ["fizbuz"] & filteredBy' (ord . C.head) (\e b -> fromIntegral e /= b) //~ maximum +-- "izbuz" +-- +-- @since 0.0.3 +filteredBy' :: LazySequence l s => (s -> a) -> (Element l -> b -> Bool) -> Moore s l a b +filteredBy' sa sbt = chunking sa $ \s b -> MS.filter (flip sbt b) s +{-# INLINE filteredBy' #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +filteredBy1 :: IsSequence s => (Element s -> a) -> (Element s -> b -> Bool) -> Mealy (Element s) s a b +filteredBy1 sa sbt = packing1 sa $ \s b -> NN.nfilter (flip sbt b) s +{-# INLINE filteredBy1 #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +broken :: IsSequence s => (Element s -> a) -> (Element s -> b -> Bool) -> Moore (Element s) (s, s) a b +broken sa sbt = packing sa $ \s b -> MS.break (flip sbt b) s +{-# INLINE broken #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +spanned :: IsSequence s => (Element s -> a) -> (Element s -> b -> Bool) -> Moore (Element s) (s, s) a b +spanned sa sbt = packing sa $ \s b -> MS.span (flip sbt b) s +{-# INLINE spanned #-} + +-- | TODO: Document +-- +-- >>> listl (partitioned id $ const . odd) [1..10] :: ([Int],[Int]) +-- ([1,3,5,7,9],[2,4,6,8,10]) +-- >>> [1..10] & partitioned id (>=) //~ (!! 5) :: ([Int],[Int]) +-- ([6,7,8,9,10],[1,2,3,4,5]) +-- >>> [1..10] & partitioned id (<) /~ 6 :: ([Int],[Int]) +-- ([1,2,3,4,5],[6,7,8,9,10]) +-- >>> [1..10] & partitioned id (==) /~ 6 :: (B.ByteString, B.ByteString) +-- ("\ACK","\SOH\STX\ETX\EOT\ENQ\a\b\t\n") +-- +-- @since 0.0.3 +partitioned :: IsSequence s => (Element s -> a) -> (Element s -> b -> Bool) -> Moore (Element s) (s, s) a b +partitioned sa sbt = packing sa $ \s b -> MS.partition (flip sbt b) s +{-# INLINE partitioned #-} -{- -- | TODO: Document -- -prefiltered :: (a -> Bool) -> AFoldl a b a b -prefiltered = L.prefilter -{-# INLINE prefiltered #-} +-- @since 0.0.3 +partitioned' :: LazySequence l s => (s -> a) -> (Element l -> b -> Bool) -> Moore s (l, l) a b +partitioned' sa sbt = chunking sa $ \s b -> MS.partition (flip sbt b) s +{-# INLINE partitioned' #-} +-- | TODO: Document +-- +-- >>> "Mississippi" & groupedAllOn id (==) /~ 'i' :: [String] +-- ["Msssspp","iiii"] +-- >>> listl (minimizedDef (' ',0) first . groupedAllOn id (const . (== 'i'))) [('H',1),('a',2),('w',3),('a',4),('i',5),('i',6)] :: ([String],Int) +-- (["Hawa","ii"],1) +-- +-- @since 0.0.3 +groupedAllOn :: IsSequence s => Eq r => (Element s -> a) -> (Element s -> b -> r) -> Moore (Element s) [s] a b +groupedAllOn sa sbt = packing sa $ \s b -> MS.groupAllOn (flip sbt b) s +{-# INLINE groupedAllOn #-} --- requires latest version of foldl -dropped :: Natural -> AFoldl' a b -dropped = L.drop +-- | TODO: Document +-- +-- @since 0.0.3 +splitWhen :: IsSequence s => (Element s -> a) -> (Element s -> b -> Bool) -> Moore (Element s) [s] a b +splitWhen sa sbt = packing sa $ \s b -> MS.splitWhen (flip sbt b) s +{-# INLINE splitWhen #-} --- requires latest version of foldl -predroppedWhile :: (a -> Bool) -> AFoldl' a b -predroppedWhile = L.predropWhile --} +-- | TODO: Document +-- +-- @since 0.0.3 +splitFirst :: IsSequence s => (Element s -> a) -> ((Element s, s) -> b -> t) -> Mealy (Element s) t a b +splitFirst sa sbt = packing1 sa $ sbt . NN.splitFirst +{-# INLINE splitFirst #-} --------------------------------------------------------------------- -- Operators @@ -271,69 +642,209 @@ predroppedWhile = L.predropWhile -- | TODO: Document -- --- A left-hand, optic version of the < http://hackage.haskell.org/package/base-4.12.0.0/docs/src/GHC.Base.html#build /build/ > in 'GHC.Base'. +-- @ +-- 'listl' o = 'buildl' o 'Control.Foldl.list' +-- 'listl' id = 'Control.Foldl.fold' 'Control.Foldl.list' = 'Data.Foldable.toList' +-- @ +-- +-- >>> listl closed [("foo: "++) . show, ("bar: "++) . show] 42 +-- ["foo: 42","bar: 42"] +-- +-- @since 0.0.3 +listl :: Foldable f => AFoldl s t a [a] -> f s -> t +listl o = buildl o L.list +{-# INLINE listl #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +listl1 :: Foldable1 f => AFoldl1 s t a (NonEmpty a) -> f s -> t +listl1 o s = flip L1.foldl1 s . o $ L1.list1 +{-# INLINE listl1 #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +steps :: Foldable1 f => AFoldl1 s t a b -> (x -> a -> x) -> x -> (x -> b) -> f s -> t +steps o h z k = buildl1 o $ L1.Foldl1 h (h z) k +{-# INLINE steps #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +buildl :: Foldable f => AFoldl s t a b -> L.Foldl a b -> f s -> t +buildl o f s = flip L.foldl s . o $ f +{-# INLINE buildl #-} + +-- | TODO: Document -- --- >>> buildsl (foldMapped second') (++) [] id [(Sum 1,"one"),(Sum 2,"two")] --- (Sum {getSum = 3},"onetwo") +-- >>> buildsl unpacked (++) [] id ["foo","bar","baz"] +-- "foobarbaz" -- +-- A version of the /build/ function from < http://hackage.haskell.org/package/base-4.12.0.0/docs/src/GHC.Base.html#build base >. +-- +-- See also < https://www.microsoft.com/en-us/research/wp-content/uploads/2016/07/deforestation-short-cut.pdf A Short Cut to Deforestation.> +-- +-- Usable in conjunction with /purely/ from the /foldl/ package. +-- +-- @since 0.0.3 buildsl :: Foldable f => AFoldl s t a b -> (x -> a -> x) -> x -> (x -> b) -> f s -> t -buildsl o h z k s = flip L.fold s . o $ L.Fold h z k +buildsl o h z k = buildl o $ L.Foldl h z k {-# INLINE buildsl #-} -- | TODO: Document -- --- >>> buildsl1 (foldMapped1 second') (++) id id $ (Min 0, "zero") :| [(Min 1,"one"),(Min 2,"two")] --- (Min {getMin = 0},"zeroonetwo") --- +-- @since 0.0.3 +obuildl :: MonoFoldable s => AFoldl (Element s) t a b -> L.Foldl a b -> s -> t +obuildl o f = (`L.withFoldl` MT.ofoldlUnwrap) . o $ f +{-# INLINE obuildl #-} + +-- | TODO: Document +-- +-- Usable in conjunction with /purely/ from the /foldl/ package. +-- +-- @since 0.0.3 +obuildsl :: MonoFoldable s => AFoldl (Element s) t a b -> (x -> a -> x) -> x -> (x -> b) -> s -> t +obuildsl o h z k = obuildl o $ L.Foldl h z k +{-# INLINE obuildsl #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +buildl1 :: Foldable1 f => AFoldl1 s t a b -> L1.Foldl1 a b -> f s -> t +buildl1 o f s = flip L1.foldl1 s . o $ f +{-# INLINE buildl1 #-} + +-- | TODO: Document +-- +-- @since 0.0.3 buildsl1 :: Foldable1 f => AFoldl1 s t a b -> (x -> a -> x) -> (a -> x) -> (x -> b) -> f s -> t -buildsl1 o h z k s = flip L1.foldl1 s . o $ L1.Foldl1 h z k +buildsl1 o h z k = buildl1 o $ L1.Foldl1 h z k {-# INLINE buildsl1 #-} -- | TODO: Document -- --- > 'listsl' id = 'Control.Foldl.fold' 'Control.Foldl.list' = 'Data.Foldable.toList' +-- @since 0.0.3 +obuildl1 :: IsSequence s => AFoldl1 (Element s) t a b -> L1.Foldl1 a b -> NonNull s -> t +obuildl1 o f = (`L1.withFoldl1` ofoldl1Unwrap) . o $ f +{-# INLINE obuildl1 #-} + +-- | TODO: Document -- --- >>> listsl closed [("foo: "++) . show, ("bar: "++) . show] 42 --- ["foo: 42","bar: 42"] +-- @since 0.0.3 +obuildsl1 :: IsSequence s => AFoldl1 (Element s) t a b -> (x -> a -> x) -> (a -> x) -> (x -> b) -> NonNull s -> t +obuildsl1 o h z k = obuildl1 o $ L1.Foldl1 h z k +{-# INLINE obuildsl1 #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +postscanl :: Traversable f => AFoldl s t a b -> L.Foldl a b -> f s -> f t +postscanl o f s = flip L.postscan s . o $ f +{-# INLINE postscanl #-} + +-- | TODO: Document +-- +-- Usable in conjunction with /purely/ from the /foldl/ package. +-- +-- >>> postscansl cotraversed1 (+) 0 id [[1,2],[3,4],[5,6]] +-- [[1,2],[4,6],[9,12]] +-- >>> postscansl cotraversed1 (<>) mempty id [["foo","bar"],["baz","bip"],["bip","bop"]] +-- [["foo","bar"],["foobaz","barbip"],["foobazbip","barbipbop"]] +-- +-- @since 0.0.3 +postscansl :: Traversable f => AFoldl s t a b -> (x -> a -> x) -> x -> (x -> b) -> f s -> f t +postscansl o h z k = postscanl o $ L.Foldl h z k +{-# INLINE postscansl #-} + +-- | TODO: Document +-- +-- >>> mconcats cotraversed1 id id [["foo","bar"],["baz","bip"]] +-- ["foobaz","barbip"] +-- >>> mconcats cotraversed1 Sum getSum [[1,2,3],[4,5,6,7]] +-- [5,7,9] +-- +-- @since 0.0.3 +mconcats :: Foldable f => Monoid m => AFoldl s t a b -> (a -> m) -> (m -> b) -> f s -> t +mconcats o f g s = flip L.foldl s . o $ L.mconcat f g +{-# INLINE mconcats #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +sconcats :: Foldable1 f => Semigroup m => AFoldl1 s t a b -> (a -> m) -> (m -> b) -> f s -> t +sconcats o f g s = flip L1.foldl1 s . o $ L1.sconcat f g +{-# INLINE sconcats #-} + +-- | TODO: Document -- -listsl :: Foldable f => AFoldl s t a [a] -> f s -> t -listsl o s = flip L.fold s . o $ L.list -{-# INLINE listsl #-} +-- @since 0.0.3 +minimizes :: Foldable1 f => Ord a => AFoldl1 s t a a -> f s -> t +minimizes o = buildl1 o $ L1.minimum +{-# INLINE minimizes #-} -- | TODO: Document -- -listsl1 :: Foldable1 f => AFoldl1 s t a (NonEmpty a) -> f s -> t -listsl1 o s = flip L1.foldl1 s . o $ L1.list1 -{-# INLINE listsl1 #-} +-- @since 0.0.3 +maximizes :: Foldable1 f => Ord a => AFoldl1 s t a a -> f s -> t +maximizes o = buildl1 o $ L1.maximum +{-# INLINE maximizes #-} -- | TODO: Document -- -mconcatsl :: Foldable f => Monoid m => AFoldl s t m m -> f s -> t -mconcatsl o s = flip L.fold s . o $ L.mconcat -{-# INLINE mconcatsl #-} +-- >>> minimizesDef (maximizedDef (0,[]) second) "" [(0,"zero"),(1,"one"),(2,"two")] +-- (2,"one") +-- +-- @since 0.0.3 +minimizesDef :: Foldable f => Ord a => AFoldl s t a a -> a -> f s -> t +minimizesDef o a = buildl o $ L.minimumDef a +{-# INLINE minimizesDef #-} -- | TODO: Document -- -sconcatsl :: Foldable1 f => Semigroup m => AFoldl1 s t m m -> f s -> t -sconcatsl o s = flip L1.foldl1 s . o $ L1.sconcat -{-# INLINE sconcatsl #-} +-- >>> maximizesDef (maximizedDef (0,[]) second) "" [(0,"zero"),(1,"one"),(2,"two")] +-- (2,"zero") +-- +-- @since 0.0.3 +maximizesDef :: Foldable f => Ord a => AFoldl s t a a -> a -> f s -> t +maximizesDef o a = buildl o $ L.maximumDef a +{-# INLINE maximizesDef #-} -- | TODO: Document -- -foldMapsl :: Foldable f => Monoid m => AFoldl s t a b -> (a -> m) -> (m -> b) -> f s -> t -foldMapsl o f g s = flip L.fold s . o $ L.foldMap f g -{-# INLINE foldMapsl #-} +-- @since 0.0.3 +minimizesBy :: Foldable1 f => AFoldl1 s t a a -> (a -> a -> Ordering) -> f s -> t +minimizesBy o f = buildl1 o $ L1.minimumBy f +{-# INLINE minimizesBy #-} -- | TODO: Document -- -foldMapsl1 :: Foldable1 f => Semigroup m => AFoldl1 s t a b -> (a -> m) -> (m -> b) -> f s -> t -foldMapsl1 o f g s = flip L1.foldl1 s . o $ L1.foldMap1 f g -{-# INLINE foldMapsl1 #-} +-- @since 0.0.3 +maximizesBy :: Foldable1 f => AFoldl1 s t a a -> (a -> a -> Ordering) -> f s -> t +maximizesBy o f = buildl1 o $ L1.maximumBy f +{-# INLINE maximizesBy #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +minimizesByDef :: Foldable f => AFoldl s t a a -> (a -> a -> Ordering) -> a -> f s -> t +minimizesByDef o f a = buildl o $ L.minimumByDef f a +{-# INLINE minimizesByDef #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +maximizesByDef :: Foldable f => AFoldl s t a a -> (a -> a -> Ordering) -> a -> f s -> t +maximizesByDef o f a = buildl o $ L.maximumByDef f a +{-# INLINE maximizesByDef #-} --------------------------------------------------------------------- -- Internal --------------------------------------------------------------------- +ofoldl1Unwrap :: IsSequence s => (x -> Element s -> x) -> (Element s -> x) -> (x -> b) -> NonNull s -> b +ofoldl1Unwrap h z k nnull = k (ofoldl' h (z initial) s) where (initial,s) = NN.splitFirst nnull + liftMay :: (a -> Bool) -> (a -> b) -> a -> Maybe b liftMay prd f a = if prd a then Nothing else Just $ f a diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Pattern.hs b/profunctor-optics/src/Data/Profunctor/Optic/Pattern.hs new file mode 100644 index 0000000..f0e8b64 --- /dev/null +++ b/profunctor-optics/src/Data/Profunctor/Optic/Pattern.hs @@ -0,0 +1,44 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE QuantifiedConstraints #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE PatternSynonyms #-} +module Data.Profunctor.Optic.Pattern where + +import Data.Profunctor.Optic.Types +import Data.Profunctor.Optic.Iso +import Data.Profunctor.Optic.View +import Data.MonoTraversable (Element) +import Data.Sequences + +pattern Lazy :: LazySequence l s => l -> s +pattern Lazy a <- (view (re strict) -> a) where + Lazy a = review (re strict) a + +pattern Strict :: LazySequence l s => s -> l +pattern Strict a <- (view strict -> a) where + Strict a = review strict a + +pattern Chunked :: LazySequence l s => [s] -> l +pattern Chunked a <- (view chunked -> a) where + Chunked a = review chunked a + +pattern Packed :: IsSequence s => s -> [Element s] +pattern Packed a <- (view (re unpacked) -> a) where + Packed a = review (re unpacked) a + +pattern Unpacked :: IsSequence s => [Element s] -> s +pattern Unpacked a <- (view unpacked -> a) where + Unpacked a = review unpacked a + +pattern Swapped :: (a, b) -> (b, a) +pattern Swapped a <- (view swapped -> a) where + Swapped a = review swapped a + +pattern Reversed :: IsSequence s => s -> s +pattern Reversed a <- (view reversed -> a) where + Reversed a = review reversed a diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Prism.hs b/profunctor-optics/src/Data/Profunctor/Optic/Prism.hs index 505933b..5fec1cd 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Prism.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Prism.hs @@ -5,24 +5,23 @@ {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE PackageImports #-} module Data.Profunctor.Optic.Prism ( - -- * Prism & Cxprism + -- * Prism Prism , Prism' - , Coprism - , Coprism' , prism , prism' , handling , clonePrism - , coprism - , coprism' - , rehandling - , cloneCoprism -- * Optics + , left + , right , just - , cojust , nothing + , this + , that + , both , prefixed , only , nearly @@ -34,7 +33,6 @@ module Data.Profunctor.Optic.Prism ( , toPastroSum , toTambaraSum , withPrism - , withCoprism -- * Classes , Choice(..) ) where @@ -47,6 +45,7 @@ import Data.Profunctor.Choice import Data.Profunctor.Optic.Carrier import Data.Profunctor.Optic.Import import Data.Profunctor.Optic.Types +import "these-skinny" Data.These -- $setup -- >>> :set -XNoOverloadedStrings @@ -88,7 +87,10 @@ prism sta bt = dimap sta (id ||| bt) . right' -- | Obtain a 'Prism'' from a reviewer and a matcher function that produces a 'Maybe'. -- -prism' :: (s -> Maybe a) -> (a -> s) -> Prism' s a +-- /Note/: The arguments are reversed from the equivalent in the /lens/ package. +-- This is unfortunate but done to maintain cosistency with 'traversal0' etc. +-- +prism' :: (s -> Maybe a) -> (b -> s) -> Prism s s a b prism' sa as = flip prism as $ \s -> maybe (Left s) Right (sa s) -- | Obtain a 'Prism' from its free tensor representation. @@ -103,47 +105,19 @@ handling sca cbt = dimap sca cbt . right' clonePrism :: APrism s t a b -> Prism s t a b clonePrism o = withPrism o prism --- | Obtain a 'Cochoice' optic from a constructor and a matcher function. --- --- @ --- coprism f g ≡ \f g -> re (prism f g) --- @ --- --- /Caution/: In order for the generated optic to be well-defined, --- you must ensure that the input functions satisfy the following --- properties: --- --- * @bat (bt b) ≡ Right b@ --- --- * @(id ||| bt) (bat b) ≡ b@ --- --- * @left bat (bat b) ≡ left Left (bat b)@ --- --- A 'Coprism' is a 'View', so you can specialise types to obtain: --- --- @ view :: 'Coprism'' s a -> s -> a @ --- -coprism :: (s -> a) -> (b -> a + t) -> Coprism s t a b -coprism sa bat = unright . dimap (id ||| sa) bat - --- | Create a 'Coprism' from a reviewer and a matcher function that produces a 'Maybe'. --- -coprism' :: (s -> a) -> (a -> Maybe s) -> Coprism' s a -coprism' tb bt = coprism tb $ \b -> maybe (Left b) Right (bt b) +--------------------------------------------------------------------- +-- Common 'Prism's and 'Coprism's +--------------------------------------------------------------------- --- | Obtain a 'Coprism' from its free tensor representation. +-- | Focus on the `Left` constructor of `Either`. -- -rehandling :: (c + s -> a) -> (b -> c + t) -> Coprism s t a b -rehandling csa bct = unright . dimap csa bct +left :: Prism (a + c) (b + c) a b +left = left' --- | TODO: Document +-- | Focus on the `Right` constructor of `Either`. -- -cloneCoprism :: ACoprism s t a b -> Coprism s t a b -cloneCoprism o = withCoprism o coprism - ---------------------------------------------------------------------- --- Common 'Prism's and 'Coprism's ---------------------------------------------------------------------- +right :: Prism (c + a) (c + b) a b +right = right' -- | Focus on the `Just` constructor of `Maybe`. -- @@ -155,16 +129,29 @@ cloneCoprism o = withCoprism o coprism just :: Prism (Maybe a) (Maybe b) a b just = flip prism Just $ maybe (Left Nothing) Right --- | Unfocus on the `Just` constructor of `Maybe`. --- -cojust :: Coprism a b (Maybe a) (Maybe b) -cojust = coprism Just $ maybe (Left Nothing) Right - -- | Focus on the `Nothing` constructor of `Maybe`. -- nothing :: Prism (Maybe a) (Maybe b) () () nothing = flip prism (const Nothing) $ maybe (Right ()) (const $ Left Nothing) +-- | Focus on the 'This' constructor of 'Data.These'. +-- +-- /Note:/ cannot change type. +this :: Prism' (These a b) a +this = prism (these Right (Left . That) (\x y -> Left $ These x y)) This + +-- | Focus on the 'That' constructor of 'Data.These'. +-- +-- /Note:/ cannot change type. +that :: Prism' (These a b) b +that = prism (these (Left . This) Right (\x y -> Left $ These x y)) That + +-- | Focus on the 'These' constructor of 'Data.These'. +-- +-- /Note:/ cannot change type. +both :: Prism' (These a b) (a, b) +both = prism (these (Left . This) (Left . That) (\x y -> Right (x, y))) $ uncurry These + -- | Focus on the remainder of a list with a given prefix. -- prefixed :: Eq a => [a] -> Prism' [a] [a] diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Property.hs b/profunctor-optics/src/Data/Profunctor/Optic/Property.hs index 15943a4..2bb2228 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Property.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Property.hs @@ -21,8 +21,8 @@ module Data.Profunctor.Optic.Property ( , tofrom_lens , fromto_lens , idempotent_lens - -- * Grate - , Grate + -- * Colens + , Colens , id_grate , const_grate , compose_grate @@ -102,7 +102,7 @@ idempotent_prism o s = withPrism o $ \sta _ -> left' sta (sta s) == left' Left ( --------------------------------------------------------------------- -- A 'Lens' is a valid 'Traversal' with the following additional laws: - +-- id_lens :: Eq s => Lens' s a -> s -> Bool id_lens o = M.join invertible $ runIdentity . cloneLensVl o Identity @@ -128,25 +128,25 @@ idempotent_lens :: Eq s => Lens' s a -> s -> a -> a -> Bool idempotent_lens o s a1 a2 = withLens o $ \_ sas -> sas (sas s a1) a2 == sas s a2 --------------------------------------------------------------------- --- 'Grate' +-- 'Colens' --------------------------------------------------------------------- --- The 'Grate' laws are that of an algebra for the parameterised continuation 'Coindex'. +-- The 'Colens' laws are that of an algebra for the parameterised continuation 'Coindex'. -id_grate :: Eq s => Grate' s a -> s -> Bool -id_grate o = M.join invertible $ cloneGrateVl o runIdentity . Identity +id_grate :: Eq s => Colens' s a -> s -> Bool +id_grate o = M.join invertible $ cloneColensVl o runIdentity . Identity -- | -- -- * @sabt ($ s) ≡ s@ -- -const_grate :: Eq s => Grate' s a -> s -> Bool -const_grate o s = withGrate o $ \sabt -> sabt ($ s) == s +const_grate :: Eq s => Colens' s a -> s -> Bool +const_grate o s = withColens o $ \sabt -> sabt ($ s) == s -compose_grate :: Eq s => Functor f => Functor g => Grate' s a -> (f a -> a) -> (g a -> a) -> f (g s) -> Bool +compose_grate :: Eq s => Functor f => Functor g => Colens' s a -> (f a -> a) -> (g a -> a) -> f (g s) -> Bool compose_grate o f g = liftA2 (==) lhs rhs - where lhs = cloneGrateVl o f . fmap (cloneGrateVl o g) - rhs = cloneGrateVl o (f . fmap g . getCompose) . Compose + where lhs = cloneColensVl o f . fmap (cloneColensVl o g) + rhs = cloneColensVl o (f . fmap g . getCompose) . Compose --------------------------------------------------------------------- -- 'Traversal0' diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Setter.hs b/profunctor-optics/src/Data/Profunctor/Optic/Setter.hs index c8cde14..9324729 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Setter.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Setter.hs @@ -12,10 +12,9 @@ module Data.Profunctor.Optic.Setter ( , Resetter , Resetter' , setter + , ixsetter , closing , resetter - , asetter - , aresetter -- * Setter1 , Setter1 , Setter1' @@ -26,27 +25,38 @@ module Data.Profunctor.Optic.Setter ( -- * Optics , cod , dom - , bound , fmapped + , omapped + , imappedRep , contramapped , liftedM , liftedA , reliftedA - , zipListed , reliftedF + , zipListed , forwarded , censored , zipped , modded , cond -- * Operators - , over , (.~) , (..~) + , over + , (%~) + , (%%~) + , overWithKey + , (#~) + , (##~) + , reoverWithKey , set , sets + , setWithKey + , setsWithKey , reset , resets + , resetWithKey + , resetsWithKey -- * mtl , (.=) , (..=) @@ -60,12 +70,14 @@ import Control.Applicative (liftA,ZipList(..)) import Control.Monad.Reader as Reader import Control.Monad.State as State import Control.Monad.Writer as Writer +import Data.MonoTraversable as M import Data.Profunctor.Optic.Carrier import Data.Profunctor.Optic.Import hiding ((&&&)) import Data.Profunctor.Optic.Combinator import Data.Profunctor.Optic.Types import Data.Profunctor.Optic.Iso (sieved,cosieved) import Data.Profunctor.Optic.Traversal +import qualified Data.Functor.Rep as F -- $setup -- >>> :set -XNoOverloadedStrings @@ -83,8 +95,7 @@ import Data.Profunctor.Optic.Traversal -- >>> import Data.Functor.Rep -- >>> import Data.Functor.Identity -- >>> import Data.Functor.Contravariant --- >>> import Data.IntSet as IntSet --- >>> import Data.Set as Set +-- >>> import Data.Semigroup -- >>> import Data.Tuple (swap) -- >>> :load Data.Profunctor.Optic @@ -120,7 +131,28 @@ setter :: ((a -> b) -> s -> t) -> Setter s t a b setter abst = sieved abst . represent (\f -> distribute . fmap f) {-# INLINE setter #-} --- | Every valid 'Grate' is a 'Setter'. +-- | Build an 'Ixsetter' from an indexed function. +-- +-- @ +-- 'ixsetter' '.' 'setsWithKey' ≡ 'id' +-- 'setsWithKey' '.' 'ixsetter' ≡ 'id' +-- @ +-- +-- /Caution/: In order for the generated optic to be well-defined, +-- you must ensure that the input satisfies the following properties: +-- +-- * @iabst (const id) ≡ id@ +-- +-- * @fmap (iabst $ const f) . (iabst $ const g) ≡ iabst (const $ f . g)@ +-- +-- See 'Data.Profunctor.Optic.Property'. +-- +-- @since 0.0.3 +ixsetter :: ((i -> a -> b) -> s -> t) -> Ixsetter i s t a b +ixsetter f = setter $ \iab -> f (curry iab) . snd +{-# INLINE ixsetter #-} + +-- | Every valid 'Colens' is a 'Setter'. -- closing :: (((s -> a) -> b) -> t) -> Setter s t a b closing sabt = setter $ \ab s -> sabt $ \sa -> ab (sa s) @@ -140,30 +172,20 @@ resetter :: ((a -> t) -> s -> t) -> Resetter s t a t resetter abst = cosieved abst . corepresent (\f -> fmap f . sequenceA) {-# INLINE resetter #-} --- | TODO: Document --- -asetter :: ((a -> f b) -> s -> f t) -> ASetter f s t a b -asetter = atraversal -{-# INLINE asetter #-} - --- | TODO: Document --- -aresetter :: ((f a -> b) -> f s -> t) -> AResetter f s t a b -aresetter = acotraversal -{-# INLINE aresetter #-} - --------------------------------------------------------------------- -- Setter1 --------------------------------------------------------------------- -- | TODO: Document -- +-- @since 0.0.3 setter1 :: ((a -> b) -> a -> t) -> Setter1 a t a b setter1 abst = sieved abst . represent (\f -> distribute1 . fmap f) {-# INLINE setter1 #-} -- | TODO: Document -- +-- @since 0.0.3 resetter1 :: ((a -> t) -> s -> t) -> Resetter1 s t a t resetter1 abst = cosieved abst . corepresent (\f -> fmap f . sequence1) {-# INLINE resetter1 #-} @@ -178,7 +200,7 @@ resetter1 abst = cosieved abst . corepresent (\f -> fmap f . sequence1) -- -- @ -- (dom ..~ f) g x ≡ f (g x) --- cod @(->) ≡ 'Data.Profunctor.Optic.Lens.withGrate' 'Data.Profunctor.Closed.closed' 'Data.Profunctor.Optic.Setter.closing' +-- cod @(->) ≡ 'Data.Profunctor.Optic.Lens.withColens' 'Data.Profunctor.Closed.closed' 'Data.Profunctor.Optic.Setter.closing' -- @ -- -- >>> (cod ..~ show) length [1,2,3] @@ -203,18 +225,27 @@ dom :: Profunctor p => Setter (p b r) (p a r) a b dom = setter lmap {-# INLINE dom #-} --- | 'Setter' for monadically transforming a monadic value. --- -bound :: Monad m => Setter (m a) (m b) a (m b) -bound = setter (=<<) -{-# INLINE bound #-} - -- | 'Setter' on each value of a functor. -- fmapped :: Functor f => Setter (f a) (f b) a b fmapped = setter fmap {-# INLINE fmapped #-} +-- | 'Setter' on each value of a monofunctor. +-- +omapped :: MonoFunctor a => Setter' a (Element a) +omapped = setter omap +{-# INLINE omapped #-} + +-- | 'Ixsetter' on each value of a representable functor. +-- +-- >>> 1 :+ 2 & ixany imappedRep %~ bool 20 10 . getAny +-- 20 :+ 10 +-- +imappedRep :: F.Representable f => Ixsetter (F.Rep f) (f a) (f b) a b +imappedRep = ixsetter F.imapRep +{-# INLINE imappedRep #-} + -- | 'Setter' on each value of a contravariant functor. -- -- @ @@ -257,20 +288,22 @@ reliftedA :: Applicative f => Resetter (f a) (f b) a b reliftedA p = cotabulate $ fmap (cosieve p) . sequenceA {-# INLINE reliftedA #-} +-- | TODO: Document +-- +-- @since 0.0.3 +reliftedF :: Apply f => Resetter1 (f a) (f b) a b +reliftedF p = cotabulate $ fmap (cosieve p) . sequence1 +{-# INLINE reliftedF #-} + -- | Variant of 'reliftedA' specialized to zip-lists. -- -- Useful because lists are not 'Control.Coapplicative.Coapplicative'. -- +-- @since 0.0.3 zipListed :: Resetter [a] [b] a b zipListed = dimap ZipList getZipList . reliftedA {-# INLINE zipListed #-} --- | TODO: Document --- -reliftedF :: Apply f => Resetter1 (f a) (f b) a b -reliftedF p = cotabulate $ fmap (cosieve p) . sequence1 -{-# INLINE reliftedF #-} - -- | 'Setter' on the local environment of a 'Reader'. -- -- Use to lift reader actions into a larger environment: @@ -315,45 +348,6 @@ cond p = setter $ \f a -> if p a then f a else a -- Operators --------------------------------------------------------------------- --- | Map over a setter. --- --- @ --- 'over' o 'id' ≡ 'id' --- 'over' o f '.' 'over' o g ≡ 'over' o (f '.' g) --- 'over' '.' 'setter' ≡ 'id' --- 'over' '.' 'resetter' ≡ 'id' --- @ --- --- >>> over fmapped (+1) (Just 1) --- Just 2 --- >>> over fmapped (*10) [1,2,3] --- [10,20,30] --- >>> over first' (+1) (1,2) --- (2,2) --- >>> over first' show (10,20) --- ("10",20) --- -over :: Optic (->) s t a b -> (a -> b) -> s -> t -over = id -{-# INLINE over #-} - -infixr 4 .~, ..~ - --- | Map over an optic. --- --- >>> (10,20) & first' ..~ show --- ("10",20) --- -(..~) :: Optic (->) s t a b -> (a -> b) -> s -> t -(..~) = over -{-# INLINE (..~) #-} - --- | Set the focus of a /->/ optic. --- -(.~) :: Optic (->) s t a b -> b -> s -> t -(.~) o b = o (const b) -{-# INLINE (.~) #-} - -- | Set the focus of a 'Setter'. -- -- @ @@ -361,31 +355,66 @@ infixr 4 .~, ..~ -- 'set' o b = 'Data.Functor.runIdentity' . (o *~ 'Data.Functor.Identity' b) -- @ -- -set :: ASetter Identity s t a b -> b -> s -> t +set :: ASetter s t a b -> b -> s -> t set o b = sets o $ const b {-# INLINE set #-} --- | TODO: Document +-- | Set the focus of a 'Setter'. -- -sets :: ASetter Identity s t a b -> (a -> b) -> s -> t +sets :: ASetter s t a b -> (a -> b) -> s -> t sets o = (runIdentity #.) #. traverses o .# (Identity #.) {-# INLINE sets #-} +-- | Set the focus of a 'Ixsetter'. +-- +-- Equivalent to 'setsWithKey' with the current value ignored. +-- +-- @ +-- 'set' o ≡ 'setWithKey' o '.' 'const' +-- @ +-- +-- @since 0.0.3 +setWithKey :: Monoid i => AIxsetter i s t a b -> (i -> b) -> s -> t +setWithKey o = setsWithKey o . (const .) +{-# INLINE setWithKey #-} + +-- | Set the focus of a 'Ixsetter'. +-- +-- @since 0.0.3 +setsWithKey :: Monoid i => AIxsetter i s t a b -> (i -> a -> b) -> s -> t +setsWithKey o f = curry (sets o $ uncurry f) mempty +{-# INLINE setsWithKey #-} + -- | Set the focus of a 'Resetter'. -- -- @ -- 'reset' o b = (o '/~' b) . 'Data.Functor.Identity' -- @ -- -reset :: AResetter Identity s t a b -> b -> s -> t +reset :: AResetter s t a b -> b -> s -> t reset o b = resets o $ const b --- | TODO: Document +-- | Set the focus of a 'Resetter'. -- -resets :: AResetter Identity s t a b -> (a -> b) -> s -> t +resets :: AResetter s t a b -> (a -> b) -> s -> t resets o = (.# Identity) #. cotraverses o .# (.# runIdentity) {-# INLINE resets #-} +-- | Set the focus of a 'Rxsetter'. +-- +-- Equivalent to 'resetsWithKey' with the current value ignored. +-- +-- @since 0.0.3 +resetWithKey :: Monoid i => ARxsetter i s t a b -> (i -> b) -> s -> t +resetWithKey o ib = resetsWithKey o $ flip (const ib) +{-# INLINE resetWithKey #-} + +-- | Set the focus of a 'Rxsetter'. +-- +-- @since 0.0.3 +resetsWithKey :: Monoid i => ARxsetter i s t a b -> (i -> a -> b) -> s -> t +resetsWithKey o f = flip (resets o $ flip f) mempty +{-# INLINE resetsWithKey #-} --------------------------------------------------------------------- -- Mtl @@ -395,11 +424,11 @@ infix 4 .=, ..= -- | Replace the target(s) of a settable in a monadic state. -- --- This is an infixversion of 'assigns'. +-- This is an infiversion of 'assigns'. -- -- >>> execState (do first' .= 1; second' .= 2) (3,4) -- (1,2) --- >>> execState (both .= 3) (1,2) +-- >>> execState (bitraversed .= 3) (1,2) -- (3,3) -- (.=) :: MonadState s m => Optic (->) s s a b -> b -> m () @@ -408,13 +437,13 @@ o .= b = State.modify (o .~ b) -- | Map over the target(s) of a 'Setter' in a monadic state. -- --- This is an infixversion of 'modifies'. +-- This is an infiversion of 'modifies'. -- -- >>> execState (do just ..= (+1) ) Nothing -- Nothing -- >>> execState (do first' ..= (+1) ;second' ..= (+2)) (1,2) -- (2,4) --- >>> execState (do both ..= (+1)) (1,2) +-- >>> execState (do bitraversed ..= (+1)) (1,2) -- (2,3) -- (..=) :: MonadState s m => Optic (->) s s a b -> (a -> b) -> m () diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Traversal.hs b/profunctor-optics/src/Data/Profunctor/Optic/Traversal.hs index 13e5271..2b432ae 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Traversal.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Traversal.hs @@ -5,6 +5,7 @@ {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE PackageImports #-} module Data.Profunctor.Optic.Traversal ( -- * Traversal0 Traversal0 @@ -13,71 +14,88 @@ module Data.Profunctor.Optic.Traversal ( , Cotraversal0' , traversal0 , traversal0' - , traversal0Vl + , ixtraversal0 + , ixtraversal0' + , traversalVl0 + , ixtraversalVl0 -- * Traversal , Traversal , Traversal' , Cotraversal , Cotraversal' + , atraversal , traversing + , ixtraversing , traversalVl + , ixtraversalVl + , ix, noix + , acotraversal , cotraversing , retraversing , cotraversalVl - , atraversal - , acotraversal + , cxtraversalVl + , beside + , reversing -- * Traversal1 , Traversal1 , Traversal1' , Cotraversal1 , Cotraversal1' , traversing1 - , traversal1Vl + , ixtraversing1 + , traversalVl1 + , ixtraversalVl1 + , cotraversing1 + , retraversing1 + , cotraversalVl1 + , cxtraversalVl1 + , beside1 , pappend , divide - , divide' , cochoose - , cochoose' - , cotraversing1 - , retraversing1 - , cotraversal1Vl , codivide - , codivide' , choose - , choose' , (<<*>>) , (****) , (&&&&) , (++++) , (||||) -- * Optics + , sat + , here + , there , anulled , selected , traversed + , otraversed , cotraversed + , itraversedRep , traversed1 , cotraversed1 - , both - , coboth - , duplicated - , beside , bitraversed , bitraversed1 + , unforked + , duplicated , repeated , iterated , cycled -- * Operators , matches , (*~) - , (**~) , sequences + , (**~) , traverses + , traversesWithKey + , backwards + , mapAccumsL + , mapAccumsR + , scansl1 + , scansr1 , (/~) - , (//~) , collects + , (//~) , cotraverses - , withAffine - , withCoaffine + , cotraversesWithKey -- * Classes , Strong(..) , Choice(..) @@ -86,8 +104,11 @@ module Data.Profunctor.Optic.Traversal ( , Corepresentable(..) ) where +import Control.Monad.State +import Control.Applicative.Backwards import Data.Function import Data.Bitraversable +import Data.MonoTraversable as M (Element, MonoTraversable(..)) import Data.Profunctor.Optic.Carrier import Data.Profunctor.Optic.Prism import Data.Profunctor.Optic.Lens @@ -95,6 +116,10 @@ import Data.Profunctor.Optic.Import import Data.Profunctor.Optic.Types import Data.Profunctor.Optic.Combinator import Data.Semigroup.Bitraversable +import Data.Sequences (IsSequence) +import qualified Data.Sequences as S +import qualified Data.Functor.Rep as F +import "these-skinny" Data.These hiding (here, there) -- $setup -- >>> :set -XNoOverloadedStrings @@ -102,10 +127,16 @@ import Data.Semigroup.Bitraversable -- >>> :set -XTypeApplications -- >>> :set -XTupleSections -- >>> :set -XRankNTypes +-- >>> :set -XPackageImports +-- >>> import Data.Char +-- >>> import Data.Function ((&)) -- >>> import Data.Int --- >>> import Data.String --- >>> import Data.Maybe -- >>> import Data.List.NonEmpty (NonEmpty(..)) +-- >>> import Data.Maybe +-- >>> import Data.String +-- >>> import Data.Semigroup +-- >>> import "these-skinny" Data.These (These(..)) +-- >>> import qualified Data.Bifunctor as B -- >>> import qualified Data.List.NonEmpty as NE -- >>> import Data.Functor.Identity -- >>> :load Data.Profunctor.Optic @@ -141,23 +172,49 @@ traversal0 sta sbt = dimap (\s -> (s,) <$> sta s) (id ||| uncurry sbt) . right' -- | Obtain a 'Traversal0'' from match and constructor functions. -- -traversal0' :: (s -> Maybe a) -> (s -> a -> s) -> Traversal0' s a +traversal0' :: (s -> Maybe a) -> (s -> b -> s) -> Traversal0 s s a b traversal0' sa sas = flip traversal0 sas $ \s -> maybe (Left s) Right (sa s) {-# INLINE traversal0' #-} -- | Transform a Van Laarhoven 'Traversal0' into a profunctor 'Traversal0'. -- -traversal0Vl :: (forall f. Functor f => (forall c. c -> f c) -> (a -> f b) -> s -> f t) -> Traversal0 s t a b -traversal0Vl f = dimap (\s -> (s,) <$> eswap (sat s)) (id ||| uncurry sbt) . right' . second' +traversalVl0 :: (forall f. Functor f => (forall c. c -> f c) -> (a -> f b) -> s -> f t) -> Traversal0 s t a b +traversalVl0 f = dimap (\s -> (s,) <$> eswap (f Right Left s)) (id ||| uncurry sbt) . right' . second' where - sat = f Right Left sbt s b = runIdentity $ f Identity (\_ -> Identity b) s -{-# INLINE traversal0Vl #-} +{-# INLINE traversalVl0 #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +ixtraversal0 :: (s -> t + (k , a)) -> (s -> b -> t) -> Ixtraversal0 k s t a b +ixtraversal0 stia sbt = ixtraversalVl0 $ \point f s -> either point (fmap (sbt s) . uncurry f) (stia s) +{-# INLINE ixtraversal0 #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +ixtraversal0' :: (s -> Maybe (k , a)) -> (s -> a -> s) -> Ixtraversal0' k s a +ixtraversal0' sia = ixtraversal0 $ \s -> maybe (Left s) Right (sia s) +{-# INLINE ixtraversal0' #-} + +-- | Transform an indexed Van Laarhoven 'Traversal0' into an indexed profunctor 'Traversal0'. +-- +-- @since 0.0.3 +ixtraversalVl0 :: (forall f. Functor f => (forall c. c -> f c) -> (k -> a -> f b) -> s -> f t) -> Ixtraversal0 k s t a b +ixtraversalVl0 f = traversalVl0 $ \cc kab -> f cc (curry kab) . snd +{-# INLINE ixtraversalVl0 #-} --------------------------------------------------------------------- -- 'Traversal' --------------------------------------------------------------------- +-- | TODO: Document +-- +atraversal :: ((a -> f b) -> s -> f t) -> ATraversal f s t a b +atraversal f = Star #. f .# runStar +{-# INLINE atraversal #-} + -- | Obtain a 'Traversal' by lifting a lens getter and setter into a 'Traversable' functor. -- -- @ @@ -187,6 +244,28 @@ traversing :: Traversable f => (s -> a) -> (s -> b -> t) -> Traversal (f s) (f t traversing sa sbt = represent traverse . lens sa sbt {-# INLINE traversing #-} +-- | Obtain a 'Ixtraversal' by lifting an indexed lens getter and setter into a 'Traversable' functor. +-- +-- @ +-- 'withIxlens' o 'ixtraversing' ≡ 'ixtraversed' . o +-- @ +-- +-- /Caution/: In order for the generated optic to be well-defined, +-- you must ensure that the input functions constitute a legal +-- indexed lens: +-- +-- * @snd . sia (sbt s a) ≡ a@ +-- +-- * @sbt s (snd $ sia s) ≡ s@ +-- +-- * @sbt (sbt s a1) a2 ≡ sbt s a2@ +-- +-- See 'Data.Profunctor.Optic.Property'. +-- +-- @since 0.0.3 +ixtraversing :: Monoid k => Traversable f => (s -> (k , a)) -> (s -> b -> t) -> Ixtraversal k (f s) (f t) a b +ixtraversing sia sbt = represent (\kab -> traverse (curry kab mempty) . snd) . ixlens sia sbt + -- | Obtain a profunctor 'Traversal' from a Van Laarhoven 'Traversal'. -- -- /Caution/: In order for the generated optic to be well-defined, @@ -205,13 +284,65 @@ traversing sa sbt = represent traverse . lens sa sbt -- See 'Data.Profunctor.Optic.Property'. -- traversalVl :: (forall f. Applicative' f => (a -> f b) -> s -> f t) -> Traversal s t a b -traversalVl abst = tabulate . abst . sieve +traversalVl = represent {-# INLINE traversalVl #-} +-- | Lift an indexed VL traversal into an indexed profunctor traversal. +-- +-- /Caution/: In order for the generated optic to be well-defined, +-- you must ensure that the input satisfies the following properties: +-- +-- * @kabst (const pure) ≡ pure@ +-- +-- * @fmap (kabst $ const f) . (kabst $ const g) ≡ getCompose . kabst (const $ Compose . fmap f . g)@ +-- +-- See 'Data.Profunctor.Optic.Property'. +-- +-- @since 0.0.3 +ixtraversalVl :: (forall f. Applicative f => (k -> a -> f b) -> s -> f t) -> Ixtraversal k s t a b +ixtraversalVl f = traversalVl $ \kab -> f (curry kab) . snd +{-# INLINE ixtraversalVl #-} + +-- | Iteratively index a traversal with an incrementing value. +-- +-- >>> B.first getSum <$> listsWithKey (ix (Sum 1) traversed) "foobar" +-- [(0,'f'),(1,'o'),(2,'o'),(3,'b'),(4,'a'),(5,'r')] +-- >>> listsWithKey (noix traversed . ix "o" traversed) ["foo", "bar"] +-- [("",'f'),("o",'o'),("oo",'o'),("",'b'),("o",'a'),("oo",'r')] +-- >>> listsWithKey (ix "x" traversed % ix "o" traversed) ["foo", "bar"] +-- [("",'f'),("o",'o'),("oo",'o'),("x",'b'),("xo",'a'),("xoo",'r')] +-- >>> B.first getSum <$> listsWithKey (ix (Sum 3) traversed % ix (Sum 1) traversed) ["foo", "bar"] +-- [(0,'f'),(1,'o'),(2,'o'),(3,'b'),(4,'a'),(5,'r')] +-- +-- @since 0.0.3 +ix :: Monoid k => k -> Traversal s t a b -> Ixtraversal k s t a b +ix k o = ixrepresent $ \f s -> + flip evalState mempty . getCompose . flip runStar s . o . Star $ \a -> + Compose $ (f <$> get <*> pure a) <* modify (<> k) + +-- | Lift a VL traversal into an indexed profunctor traversal that ignores its input. +-- +-- Useful as the first optic in a chain when no indexed equivalent is at hand. +-- +-- >>> B.first getSum <$> listsWithKey (noix traversed . ix (Sum 1) traversed) ["foo", "bar"] +-- [(0,'f'),(1,'o'),(2,'o'),(0,'b'),(1,'a'),(2,'r')] +-- >>> B.first getSum <$> listsWithKey (ix (Sum 1) traversed . noix traversed) ["foo", "bar"] +-- [(0,'f'),(0,'o'),(0,'o'),(0,'b'),(0,'a'),(0,'r')] +-- +-- @since 0.0.3 +noix :: Monoid k => Traversal s t a b -> Ixtraversal k s t a b +noix o = ixrepresent $ \iab s -> flip runStar s . o . Star $ iab mempty + +-- | TODO: Document +-- +acotraversal :: ((f a -> b) -> f s -> t) -> ACotraversal f s t a b +acotraversal f = Costar #. f .# runCostar +{-# INLINE acotraversal #-} + -- | Obtain a 'Cotraversal' by embedding a continuation into a 'Distributive' functor. -- -- @ --- 'withGrate' o 'cotraversing' ≡ 'cotraversed' . o +-- 'withColens' o 'cotraversing' ≡ 'cotraversed' . o -- @ -- -- /Caution/: In order for the generated optic to be well-defined, @@ -228,11 +359,11 @@ cotraversing sabt = corepresent cotraverse . grate sabt -- | Obtain a 'Cotraversal' by embedding a reversed lens getter and setter into a 'Distributive' functor. -- -- @ --- 'withLens' ('re' o) 'cotraversing' ≡ 'cotraversed' . o +-- 'withLens' ('re' o) 'retraversing' ≡ 'cotraversed' . o -- @ -- retraversing :: Distributive g => (b -> t) -> (b -> s -> a) -> Cotraversal (g s) (g t) a b -retraversing bsa bt = corepresent cotraverse . (re $ lens bsa bt) +retraversing bt bsa = corepresent cotraverse . (re $ lens bt bsa) -- | Obtain a profunctor 'Cotraversal' from a Van Laarhoven 'Cotraversal'. -- @@ -252,19 +383,36 @@ retraversing bsa bt = corepresent cotraverse . (re $ lens bsa bt) -- See 'Data.Profunctor.Optic.Property'. -- cotraversalVl :: (forall f. Coapplicative f => (f a -> b) -> f s -> t) -> Cotraversal s t a b -cotraversalVl abst = cotabulate . abst . cosieve +cotraversalVl = corepresent + +-- | Lift a coindexed VL cotraversal into a coindexed profunctor cotraversal. +-- +-- /Caution/: In order for the generated optic to be well-defined, +-- you must ensure that the input satisfies the following properties: +-- +-- * @aibst (const . copure) ≡ copure@ +-- +-- * @(aibst $ const . f) . fmap (aibst $ const . g) ≡ aibst (const . f . fmap g . getCompose) . Compose@ +-- +-- See 'Data.Profunctor.Optic.Property'. +-- +-- @since 0.0.3 +cxtraversalVl :: (forall f. Coapplicative f => (f a -> k -> b) -> f s -> t) -> Cxtraversal k s t a b +cxtraversalVl f = cotraversalVl $ \akb -> const . f akb +{-# INLINE cxtraversalVl #-} -- | TODO: Document -- -atraversal :: ((a -> f b) -> s -> f t) -> ATraversal f s t a b -atraversal f = Star #. f .# runStar -{-# INLINE atraversal #-} +beside :: Bitraversable r => Traversal s1 t1 a b -> Traversal s2 t2 a b -> Traversal (r s1 s2) (r t1 t2) a b +beside x y p = tabulate go where go rss = bitraverse (sieve $ x p) (sieve $ y p) rss +{-# INLINE beside #-} -- | TODO: Document -- -acotraversal :: ((f a -> b) -> f s -> t) -> ACotraversal f s t a b -acotraversal f = Costar #. f .# runCostar -{-# INLINE acotraversal #-} +-- @since 0.0.3 +reversing :: ATraversal (Backwards f) s t a b -> ATraversal f s t a b +reversing = atraversal . backwards +{-# INLINE reversing #-} --------------------------------------------------------------------- -- 'Traversal1' @@ -299,6 +447,28 @@ traversing1 :: Traversable1 f => (s -> a) -> (s -> b -> t) -> Traversal1 (f s) ( traversing1 sa sbt = represent traverse1 . lens sa sbt {-# INLINE traversing1 #-} +-- | Obtain a 'Ixtraversal' by lifting an indexed lens getter and setter into a 'Traversable' functor. +-- +-- @ +-- 'withIxlens' o 'ixtraversing' ≡ 'ixtraversed' . o +-- @ +-- +-- /Caution/: In order for the generated optic to be well-defined, +-- you must ensure that the input functions constitute a legal +-- indexed lens: +-- +-- * @snd . sia (sbt s a) ≡ a@ +-- +-- * @sbt s (snd $ sia s) ≡ s@ +-- +-- * @sbt (sbt s a1) a2 ≡ sbt s a2@ +-- +-- See 'Data.Profunctor.Optic.Property'. +-- +-- @since 0.0.3 +ixtraversing1 :: Monoid k => Traversable1 f => (s -> (k , a)) -> (s -> b -> t) -> Ixtraversal1 k (f s) (f t) a b +ixtraversing1 sia sbt = represent (\kab -> traverse1 (curry kab mempty) . snd) . ixlens sia sbt + -- | Obtain a profunctor 'Traversal1' from a Van Laarhoven 'Traversal1'. -- -- /Caution/: In order for the generated family to be well-defined, @@ -308,14 +478,30 @@ traversing1 sa sbt = represent traverse1 . lens sa sbt -- -- See 'Data.Profunctor.Optic.Property'. -- -traversal1Vl :: (forall f. Apply f => (a -> f b) -> s -> f t) -> Traversal1 s t a b -traversal1Vl abst = tabulate . abst . sieve -{-# INLINE traversal1Vl #-} +traversalVl1 :: (forall f. Apply f => (a -> f b) -> s -> f t) -> Traversal1 s t a b +traversalVl1 abst = tabulate . abst . sieve +{-# INLINE traversalVl1 #-} + +-- | Obtain a profunctor 'Ixtraversal1' from a Van Laarhoven 'Ixtraversal1'. +-- +-- /Caution/: In order for the generated optic to be well-defined, +-- you must ensure that the input satisfies the following properties: +-- +-- * @kabst (const Identity) ≡ Identity@ +-- +-- * @fmap (kabst $ const f) . (kabst $ const g) ≡ getCompose . kabst (const $ Compose . fmap f . g)@ +-- +-- See 'Data.Profunctor.Optic.Property'. +-- +-- @since 0.0.3 +ixtraversalVl1 :: (forall f. Apply f => (k -> a -> f b) -> s -> f t) -> Ixtraversal1 k s t a b +ixtraversalVl1 f = traversalVl1 $ \kab -> f (curry kab) . snd +{-# INLINE ixtraversalVl1 #-} -- | Obtain a 'Cotraversal1' by embedding a continuation into a 'Distributive1' functor. -- -- @ --- 'withGrate' o 'cotraversing1' ≡ 'cotraversed1' . o +-- 'withColens' o 'cotraversing1' ≡ 'cotraversed1' . o -- @ -- -- /Caution/: In order for the generated optic to be well-defined, @@ -332,11 +518,11 @@ cotraversing1 sabt = corepresent cotraverse1 . grate sabt -- | Obtain a 'Cotraversal1' by embedding a reversed lens getter and setter into a 'Distributive1' functor. -- -- @ --- 'withLens' ('re' o) 'cotraversing' ≡ 'cotraversed' . o +-- 'withLens' ('re' o) 'retraversing1' ≡ 'cotraversed1' . o -- @ -- retraversing1 :: Distributive1 g => (b -> t) -> (b -> s -> a) -> Cotraversal1 (g s) (g t) a b -retraversing1 bsa bt = corepresent cotraverse1 . (re $ lens bsa bt) +retraversing1 bt bsa = corepresent cotraverse1 . (re $ lens bt bsa) -- | Obtain a profunctor 'Cotraversal1' from a Van Laarhoven 'Cotraversal1'. -- @@ -355,13 +541,75 @@ retraversing1 bsa bt = corepresent cotraverse1 . (re $ lens bsa bt) -- -- See 'Data.Profunctor.Optic.Property'. -- -cotraversal1Vl :: (forall f. Coapply f => (f a -> b) -> f s -> t) -> Cotraversal1 s t a b -cotraversal1Vl abst = cotabulate . abst . cosieve +cotraversalVl1 :: (forall f. Coapply f => (f a -> b) -> f s -> t) -> Cotraversal1 s t a b +cotraversalVl1 abst = cotabulate . abst . cosieve + +-- | Obtain a profunctor 'Cxtraversal1' from a Van Laarhoven 'Cxtraversal1'. +-- +-- /Caution/: In order for the generated optic to be well-defined, +-- you must ensure that the input satisfies the following properties: +-- +-- * @aibst (const . runIdentity) ≡ runIdentity@ +-- +-- * @(aibst $ const . f) . fmap (aibst $ const . g) ≡ aibst (const . f . fmap g . getCompose) . Compose@ +-- +-- See 'Data.Profunctor.Optic.Property'. +-- +-- @since 0.0.3 +cxtraversalVl1 :: (forall f. Coapply f => (f a -> k -> b) -> f s -> t) -> Cxtraversal1 k s t a b +cxtraversalVl1 f = cotraversalVl1 $ \aib -> const . f aib +{-# INLINE cxtraversalVl1 #-} + +-- | TODO: Document +-- +beside1 :: Bitraversable1 r => Traversal1 s1 t1 a b -> Traversal1 s2 t2 a b -> Traversal1 (r s1 s2) (r t1 t2) a b +beside1 x y p = tabulate go where go rss = bitraverse1 (sieve $ x p) (sieve $ y p) rss +{-# INLINE beside1 #-} --------------------------------------------------------------------- -- Optics --------------------------------------------------------------------- +-- | TODO: Document +-- +-- >>> "foobar" ^? sat 3 :: Maybe Char +-- Just 'b' +-- +-- @since 0.0.3 +sat :: IsSequence a => S.Index a -> Traversal0' a (Element a) +sat e = traversalVl0 $ \point f s -> + case S.splitAt e s of + (l, mr) -> case S.uncons mr of + Nothing -> point s + Just (c, xs) -> f c <&> \d -> l <> S.singleton d <> xs +{-# INLINE sat #-} + +-- | A 'Traversal0' of the first half of a 'These'. +-- +-- >>> over here show (That 1) +-- That 1 +-- +-- >>> over here show (These 'a' 2) +-- These "'a'" 2 +-- +-- @since 0.0.3 +here :: Traversal0 (These a c) (These b c) a b +here = traversalVl0 $ \point afb -> these (fmap This . afb) (point . That) (\x y -> flip These y <$> afb x) +{-# INLINE here #-} + +-- | A 'Traversal0' of the second half of a 'These'. +-- +-- >>> over there show (That 1) +-- That "1" +-- +-- >>> over there show (These 'a' 2) +-- These 'a' "2" +-- +-- @since 0.0.3 +there :: Traversal0 (These c a) (These c b) a b +there = traversalVl0 $ \point afb -> these (point . This) (fmap That . afb) (\x y -> These x <$> afb y) +{-# INLINE there #-} + -- | TODO: Document -- anulled :: Traversal0' s a @@ -380,6 +628,19 @@ traversed :: Traversable f => Traversal (f a) (f b) a b traversed = traversalVl traverse {-# INLINE traversed #-} +-- | TODO: Document +-- +-- @since 0.0.3 +otraversed :: MonoTraversable a => Traversal' a (Element a) +otraversed = traversalVl otraverse +{-# INLINE otraversed #-} + +-- | TODO: Document +-- +itraversedRep :: F.Representable f => Traversable f => Ixtraversal (F.Rep f) (f a) (f b) a b +itraversedRep = ixtraversalVl F.itraverseRep +{-# INLINE itraversedRep #-} + -- | TODO: Document -- cotraversed :: Distributive f => Cotraversal (f a) (f b) a b @@ -389,7 +650,7 @@ cotraversed = cotraversalVl cotraverse -- | Obtain a 'Traversal1' from a 'Traversable1' functor. -- traversed1 :: Traversable1 t => Traversal1 (t a) (t b) a b -traversed1 = traversal1Vl traverse1 +traversed1 = traversalVl1 traverse1 {-# INLINE traversed1 #-} -- | TODO: Document @@ -397,46 +658,9 @@ traversed1 = traversal1Vl traverse1 -- > 'cotraversed1' :: 'Cotraversal1' [a] [b] a b -- cotraversed1 :: Distributive1 f => Cotraversal1 (f a) (f b) a b -cotraversed1 = cotraversal1Vl cotraverse1 +cotraversed1 = cotraversalVl1 cotraverse1 {-# INLINE cotraversed1 #-} --- | TODO: Document --- --- >>> traverses both (pure . length) ("hello","world") --- (5,5) --- >>> traverses both (pure . NE.length) ('h' :| "ello", 'w' :| "orld") --- (5,5) --- -both :: Traversal1 (a , a) (b , b) a b -both p = p **** p -{-# INLINE both #-} - --- | TODO: Document --- --- >>> cotraverses coboth (foldMap id) $ Left "foo" :| [Right "bar"] --- Left "foo" --- >>> cotraverses coboth (foldMap id) $ Right "foo" :| [Right "bar"] --- Right "foobar" --- -coboth :: Cotraversal1 (a + a) (b + b) a b -coboth p = p ++++ p -{-# INLINE coboth #-} - --- | Duplicate the results of a 'Traversal'. --- --- >>> lists (both . duplicated) ("hello","world") --- ["hello","hello","world","world"] --- -duplicated :: Traversal a b a b -duplicated p = pappend p p -{-# INLINE duplicated #-} - --- | TODO: Document --- -beside :: Bitraversable r => Traversal s1 t1 a b -> Traversal s2 t2 a b -> Traversal (r s1 s2) (r t1 t2) a b -beside x y p = tabulate go where go rss = bitraverse (sieve $ x p) (sieve $ y p) rss -{-# INLINE beside #-} - -- | Traverse both parts of a 'Bitraversable' container with matching types. -- -- >>> traverses bitraversed (pure . length) (Right "hello") @@ -457,13 +681,34 @@ bitraversed = represent $ \f -> bitraverse f f -- | Traverse both parts of a 'Bitraversable1' container with matching types. -- --- >>> traverses bitraversed1 (pure . NE.length) ('h' :| "ello", 'w' :| "orld") +-- >>> ('h' :| "ello", 'w' :| "orld") & bitraversed1 **~ pure . NE.length -- (5,5) -- bitraversed1 :: Bitraversable1 r => Traversal1 (r a a) (r b b) a b bitraversed1 = represent $ \f -> bitraverse1 f f {-# INLINE bitraversed1 #-} +-- | TODO: Document +-- +-- >>> cotraverses unforked (foldMap id) $ Left "foo" :| [Right "bar"] +-- Left "foo" +-- >>> cotraverses unforked (foldMap id) $ Right "foo" :| [Right "bar"] +-- Right "foobar" +-- +-- @since 0.0.3 +unforked :: Cotraversal1 (a + a) (b + b) a b +unforked p = p ++++ p +{-# INLINE unforked #-} + +-- | Duplicate the results of a 'Traversal'. +-- +-- >>> lists (bitraversed . duplicated) ("hello","world") +-- ["hello","hello","world","world"] +-- +duplicated :: Traversal a b a b +duplicated p = pappend p p +{-# INLINE duplicated #-} + -- | Obtain a 'Traversal1'' by repeating the input forever. -- -- @ @@ -518,43 +763,91 @@ matches :: ATraversal0 s t a b -> s -> t + a matches o = withAffine o $ \sta _ -> sta {-# INLINE matches #-} -infixr 4 *~, **~, /~, //~ --, %~, %%~, #~, ##~ - --- | Set the focus of a representable optic. --- -(*~) :: ATraversal f s t a b -> f b -> s -> f t -(*~) o b = traverses o (const b) -{-# INLINE (*~) #-} - --- | Map over a representable optic. --- -(**~) :: ATraversal f s t a b -> (a -> f b) -> s -> f t -(**~) = traverses -{-# INLINE (**~) #-} - -- | TODO: Document -- sequences :: Applicative f => ATraversal f s t (f a) a -> s -> f t sequences o = traverses o id {-# INLINE sequences #-} --- | TODO: Document +-- | Traverse over a 'Traversal'. -- traverses :: ATraversal f s t a b -> (a -> f b) -> s -> f t -traverses o = runStar #. o .# Star +traverses = (**~) {-# INLINE traverses #-} --- | Set the focus of a co-representable optic. +-- | Traverse over an 'Ixtraversal'. +-- +-- @ +-- 'traversesWithKey' o f = 'curry' ('traverses' o '$' 'uncurry' f) 'mempty' +-- @ +-- +-- @since 0.0.3 +traversesWithKey :: Monoid k => AIxtraversal f k s t a b -> (k -> a -> f b) -> s -> f t +traversesWithKey o f = curry (o **~ uncurry f) mempty +{-# INLINE traversesWithKey #-} + +-- | This allows you to 'Control.Traversable.traverse' the elements of a 'Traversing' or 'Traversing1' optic in the opposite order. +-- +-- This will preserve indexes on 'Indexed' types and for example will give you the elements of a (finite) 'Fold' or 'Traversal' in the opposite order. +-- +-- This has no practical effect on a 'View', 'Setter', 'Lens' or 'Iso'. +-- +-- @since 0.0.3 +backwards :: ATraversal (Backwards f) s t a b -> (a -> f b) -> s -> f t +backwards o = (forwards #.) #. traverses o .# (Backwards #.) +{-# INLINE backwards #-} + +-- | Generalize 'Data.Traversable.mapAccumL' to a 'Traversing' or 'Traversing1' optic. +-- +-- @ +-- 'mapAccumL' ≡ 'mapAccumsL' 'traverse' +-- @ +-- +-- 'mapAccumsL' accumulates 'State' from left to right. -- -(/~) :: ACotraversal f s t a b -> b -> f s -> t -(/~) o b = cotraverses o (const b) -{-# INLINE (/~) #-} +-- @since 0.0.3 +mapAccumsL :: ATraversal (State r) s t a b -> (r -> a -> (r, b)) -> r -> s -> (r, t) +mapAccumsL o f acc0 s = swap (runState (traverses o g s) acc0) where + g a = state $ \acc -> swap (f acc a) --- | Map over a co-representable optic. +-- | Generalize 'Data.Traversable.mapAccumR' to a 'Traversing' or 'Traversing1' optic. -- -(//~) :: ACotraversal f s t a b -> (f a -> b) -> f s -> t -(//~) = cotraverses -{-# INLINE (//~) #-} +-- @ +-- 'mapAccumR' ≡ 'mapAccumsR' 'traverse' +-- @ +-- +-- 'mapAccumsR' accumulates 'State' from right to left. +-- +-- @since 0.0.3 +mapAccumsR :: ATraversal (Backwards (State r)) s t a b -> (r -> a -> (r, b)) -> r -> s -> (r, t) +mapAccumsR = mapAccumsL . reversing +{-# INLINE mapAccumsR #-} + +-- | Scan left over a 'Traversing' or 'Traversing1' optic. +-- +-- @ +-- 'scanl1' ≡ 'scansl1' 'traverse' +-- @ +-- +-- @since 0.0.3 +scansl1 :: ATraversal (State (Maybe a)) s t a a -> (a -> a -> a) -> s -> t +scansl1 o f = snd . mapAccumsL o step Nothing where + step Nothing a = (Just a, a) + step (Just s) a = (Just r, r) where r = f s a +{-# INLINE scansl1 #-} + +-- | Scan left over a 'Traversing' or 'Traversing1' optic. +-- +-- @ +-- 'scanr1' ≡ 'scansr1' 'traverse' +-- @ +-- +-- @since 0.0.3 +scansr1 :: ATraversal (Backwards (State (Maybe a))) s t a a -> (a -> a -> a) -> s -> t +scansr1 o f = snd . mapAccumsR o step Nothing where + step Nothing a = (Just a, a) + step (Just s) a = (Just r, r) where r = f a s +{-# INLINE scansr1 #-} -- | TODO: Document -- @@ -569,8 +862,20 @@ collects :: Coapply f => ACotraversal f s t a (f a) -> f s -> t collects o = cotraverses o id {-# INLINE collects #-} --- | TODO: Document +-- | Cotraverse over a co-representable optic. +-- | Cotraverse over a 'Cotraversal'. -- cotraverses :: ACotraversal f s t a b -> (f a -> b) -> (f s -> t) -cotraverses o = runCostar #. o .# Costar +cotraverses = (//~) {-# INLINE cotraverses #-} + +-- | Cotraverse over a 'Cxtraversal'. +-- +-- @ +-- 'cotraversesWithKey' o f = 'flip' ('cotraverses' o '$' 'flip' f) 'mempty' +-- @ +-- +-- @since 0.0.3 +cotraversesWithKey :: Monoid k => ACxtraversal f k s t a b -> (k -> f a -> b) -> f s -> t +cotraversesWithKey o f = flip (o //~ flip f) mempty +{-# INLINE cotraversesWithKey #-} diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Types.hs b/profunctor-optics/src/Data/Profunctor/Optic/Types.hs index 1061e94..d093ca2 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Types.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Types.hs @@ -6,6 +6,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE PolyKinds #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE DeriveFunctor #-} @@ -19,47 +20,64 @@ module Data.Profunctor.Optic.Types ( -- * Optic Optic, Optic' - , IndexedOptic, IndexedOptic' - , CoindexedOptic, CoindexedOptic' + , Ix, Ix', Cx, Cx' + , Ixoptic, Ixoptic' + , Cxoptic, Cxoptic' -- * Constraints - , CoerceL, CoerceR , Affine, Coaffine , Traversing, Cotraversing , Traversing1, Cotraversing1 , Mapping, Remapping , Mapping1, Remapping1 + , CoercingL, CoercingR + , Foldable', Foldable1' -- * Equality , Equality, Equality' -- * Iso , Iso, Iso' -- * Prism - , Prism, Coprism - , Prism', Coprism' + , Prism, Prism' -- * Lens , Lens, Colens + , Ixlens, Cxlens , Lens', Colens' - -- * Grate - , Grate, Grate' + , Ixlens', Cxlens' -- * Traversal , Traversal0, Cotraversal0 , Traversal, Cotraversal , Traversal1, Cotraversal1 + , Ixtraversal0, Cxtraversal0 + , Ixtraversal, Cxtraversal + , Ixtraversal1, Cxtraversal1 , Traversal0', Cotraversal0' , Traversal', Cotraversal' , Traversal1', Cotraversal1' + , Ixtraversal0', Cxtraversal0' + , Ixtraversal', Cxtraversal' + , Ixtraversal1', Cxtraversal1' + -- * Fold + , Fold0, Cofold0 + , Fold, Cofold + , Fold1, Cofold1 + , Ixfold0, Cxfold0 + , Ixfold, Cxfold + , Ixfold1, Cxfold1 -- * Machine , Moore, Mealy + , Ixmoore, Cxmoore , Moore', Mealy' - -- * Fold - , Fold0, Fold, Fold1 - , Cofold0, Cofold, Cofold1 -- * Setter , Setter, Resetter - , Setter', Resetter' , Setter1, Resetter1 + , Ixsetter, Rxsetter + , Ixsetter1, Rxsetter1 + , Setter', Resetter' , Setter1', Resetter1' + , Ixsetter', Rxsetter' + , Ixsetter1', Rxsetter1' -- * View , View, Review + , Ixview, Rxview -- * 'Re' , Re(..), re , between @@ -80,10 +98,6 @@ import Data.Profunctor.Types as Export -- Constraints --------------------------------------------------------------------- -type CoerceL p = (Bifunctor p) - -type CoerceR p = (forall x. Contravariant (p x)) - type Affine p = (Strong p, Choice p) type Coaffine p = (Closed p, Choice p) @@ -104,21 +118,37 @@ type Mapping1 p = (Representable p, Distributive1 (Rep p)) type Remapping1 p = (Corepresentable p, Traversable1 (Corep p)) +type CoercingL p = (Bifunctor p) + +type CoercingR p = (forall x. Contravariant (p x)) + +type Foldable' f = (Functor f, Foldable f) + +type Foldable1' f = (Functor f, Foldable1 f) + --------------------------------------------------------------------- -- Optic --------------------------------------------------------------------- +type Ix p k a b = p (k , a) b + +type Cx p k a b = p a (k -> b) + type Optic p s t a b = p a b -> p s t -type Optic' p s a = Optic p s s a a +type Ixoptic p k s t a b = Ix p k a b -> Ix p k s t -type IndexedOptic p i s t a b = p (i , a) b -> p (i , s) t +type Cxoptic p k s t a b = Cx p k a b -> Cx p k s t -type IndexedOptic' p i s a = IndexedOptic p i s s a a +type Ix' p a b = Ix p a b b -type CoindexedOptic p k s t a b = p a (k -> b) -> p s (k -> t) +type Cx' p a b = Cx p a a b -type CoindexedOptic' p k t b = CoindexedOptic p k t t b b +type Optic' p s a = Optic p s s a a + +type Ixoptic' p k s a = Ixoptic p k s s a a + +type Cxoptic' p k t b = Cxoptic p k t t b b --------------------------------------------------------------------- -- Equality @@ -148,14 +178,8 @@ type Iso' s a = Iso s s a a -- type Prism s t a b = forall p. Choice p => Optic p s t a b --- | \( \mathsf{Prism}\;S\;A = \exists D, S + D \cong A \) --- -type Coprism s t a b = forall p. Cochoice p => Optic p s t a b - type Prism' s a = Prism s s a a -type Coprism' t b = Coprism t t b b - --------------------------------------------------------------------- -- Lens --------------------------------------------------------------------- @@ -164,22 +188,24 @@ type Coprism' t b = Coprism t t b b -- type Lens s t a b = forall p. Strong p => Optic p s t a b --- | \( \mathsf{Lens}\;S\;A = \exists C, S \times C \cong A \) +-- | \( \mathsf{Colens}\;S\;A = \exists I, S \cong I \to A \) -- -type Colens s t a b = forall p. Costrong p => Optic p s t a b +type Colens s t a b = forall p. Closed p => Optic p s t a b --- | \( \mathsf{Grate}\;S\;A = \exists I, S \cong I \to A \) --- -type Grate s t a b = forall p. Closed p => Optic p s t a b +type Ixlens k s t a b = forall p. Strong p => Ixoptic p k s t a b + +type Cxlens k s t a b = forall p. Closed p => Cxoptic p k s t a b type Lens' s a = Lens s s a a -type Colens' t b = Lens t t b b +type Colens' s a = Colens s s a a -type Grate' s a = Grate s s a a +type Ixlens' k s a = Ixlens k s s a a + +type Cxlens' k t b = Cxlens k t t b b --------------------------------------------------------------------- --- Traversal0 +-- Traversal --------------------------------------------------------------------- -- | \( \mathsf{Traversal0}\;S\;A = \exists C, D, S \cong D + C \times A \) @@ -190,14 +216,6 @@ type Traversal0 s t a b = forall p. Affine p => Optic p s t a b -- type Cotraversal0 s t a b = forall p. Coaffine p => Optic p s t a b -type Traversal0' s a = Traversal0 s s a a - -type Cotraversal0' t b = Cotraversal0 t t b b - ---------------------------------------------------------------------- --- Traversal ---------------------------------------------------------------------- - -- | \( \mathsf{Traversal}\;S\;A = \exists F : \mathsf{Traversable}, S \equiv F\,A \) -- type Traversal s t a b = forall p. (Affine p, Traversing p) => Optic p s t a b @@ -206,14 +224,6 @@ type Traversal s t a b = forall p. (Affine p, Traversing p) => Optic p s t a b -- type Cotraversal s t a b = forall p. (Coaffine p, Cotraversing p) => Optic p s t a b -type Traversal' s a = Traversal s s a a - -type Cotraversal' t b = Cotraversal t t b b - ---------------------------------------------------------------------- --- Traversal1 ---------------------------------------------------------------------- - -- | \( \mathsf{Traversal1}\;S\;A = \exists F : \mathsf{Traversable1}, S \equiv F\,A \) -- type Traversal1 s t a b = forall p. (Strong p, Traversing1 p) => Optic p s t a b @@ -222,49 +232,89 @@ type Traversal1 s t a b = forall p. (Strong p, Traversing1 p) => Optic p s t a b -- type Cotraversal1 s t a b = forall p. (Closed p, Cotraversing1 p) => Optic p s t a b +type Ixtraversal0 k s t a b = forall p. Affine p => Ixoptic p k s t a b + +type Ixtraversal k s t a b = forall p. (Affine p, Traversing p) => Ixoptic p k s t a b + +type Ixtraversal1 k s t a b = forall p. (Strong p, Traversing1 p) => Ixoptic p k s t a b + +type Cxtraversal0 k s t a b = forall p. Coaffine p => Cxoptic p k s t a b + +type Cxtraversal k s t a b = forall p. (Coaffine p, Cotraversing p) => Cxoptic p k s t a b + +type Cxtraversal1 k s t a b = forall p. (Closed p, Cotraversing1 p) => Cxoptic p k s t a b + +type Traversal0' s a = Traversal0 s s a a + +type Cotraversal0' t b = Cotraversal0 t t b b + +type Traversal' s a = Traversal s s a a + +type Cotraversal' t b = Cotraversal t t b b + type Traversal1' s a = Traversal1 s s a a type Cotraversal1' t b = Cotraversal1 t t b b ---------------------------------------------------------------------- --- Machine ---------------------------------------------------------------------- +type Ixtraversal0' k s a = Ixtraversal0 k s s a a --- | A < https://en.wikipedia.org/wiki/Moore_machine Moore machine > --- -type Moore s t a b = forall p. (Closed p, Cotraversing1 p, Foldable (Corep p)) => Optic p s t a b +type Ixtraversal' k s a = Ixtraversal k s s a a -type Moore' t b = Moore t t b b +type Ixtraversal1' k s a = Ixtraversal1 k s s a a --- | A < https://en.wikipedia.org/wiki/Mealy_machine Mealy machine > --- -type Mealy s t a b = forall p. (Coaffine p, Cotraversing p, Foldable1 (Corep p)) => Optic p s t a b +type Cxtraversal0' k t b = Cxtraversal0 k t t b b -type Mealy' t b = Mealy t t b b +type Cxtraversal' k t b = Cxtraversal k t t b b + +type Cxtraversal1' k t b = Cxtraversal1 k t t b b --------------------------------------------------------------------- -- Fold --------------------------------------------------------------------- -type Fold0 s a = forall p. (Affine p, CoerceR p) => Optic' p s a +type Fold0 s a = forall p. (Affine p, CoercingR p) => Optic' p s a + +type Fold s a = forall p. (Affine p, Traversing p, CoercingR p) => Optic' p s a + +type Fold1 s a = forall p. (Strong p, Traversing1 p, CoercingR p) => Optic' p s a -type Fold s a = forall p. (Affine p, Traversing p, CoerceR p) => Optic' p s a +type Cofold0 t b = forall p. (Coaffine p, CoercingL p) => Optic' p t b -type Fold1 s a = forall p. (Strong p, Traversing1 p, CoerceR p) => Optic' p s a +type Cofold t b = forall p. (Affine p, Cotraversing p, CoercingL p) => Optic' p t b -type Cofold0 t b = forall p. (Coaffine p, CoerceL p) => Optic' p t b +type Cofold1 t b = forall p. (Choice p, Cotraversing1 p, CoercingL p) => Optic' p t b -type Cofold t b = forall p. (Affine p, Cotraversing p, CoerceL p) => Optic' p t b +type Ixfold0 k s a = forall p. (Affine p, CoercingR p) => Ixoptic' p k s a -type Cofold1 t b = forall p. (Choice p, Cotraversing1 p, CoerceL p) => Optic' p t b +type Ixfold k s a = forall p. (Affine p, Traversing p, CoercingR p) => Ixoptic' p k s a + +type Ixfold1 k s a = forall p. (Strong p, Traversing1 p, CoercingR p) => Ixoptic' p k s a + +type Cxfold0 k t b = forall p. (Coaffine p, CoercingL p) => Cxoptic' p k t b + +type Cxfold k t b = forall p. (Affine p, Cotraversing p, CoercingL p) => Cxoptic' p k t b + +type Cxfold1 k t b = forall p. (Choice p, Cotraversing1 p, CoercingL p) => Cxoptic' p k t b --------------------------------------------------------------------- --- View +-- Machine --------------------------------------------------------------------- -type View s a = forall p. (Strong p, CoerceR p) => Optic' p s a +-- | A < https://en.wikipedia.org/wiki/Moore_machine Moore machine > +-- +type Moore s t a b = forall p. (Closed p, Cotraversing1 p, Foldable (Corep p)) => Optic p s t a b + +-- | A < https://en.wikipedia.org/wiki/Mealy_machine Mealy machine > +-- +type Mealy s t a b = forall p. (Coaffine p, Cotraversing p, Foldable1 (Corep p)) => Optic p s t a b + +type Ixmoore k s t a b = forall p. (Closed p, Cotraversing1 p, Foldable (Corep p)) => Ixoptic p k s t a b + +type Cxmoore k s t a b = forall p. (Closed p, Cotraversing1 p, Foldable (Corep p)) => Cxoptic p k s t a b -type Review t b = forall p. (Closed p, CoerceL p) => Optic' p t b +type Moore' t b = Moore t t b b + +type Mealy' t b = Mealy t t b b --------------------------------------------------------------------- -- Setter @@ -280,21 +330,45 @@ type Setter s t a b = forall p. (Affine p, Traversing p, Mapping p) => Optic p s -- type Resetter s t a b = forall p. (Coaffine p, Cotraversing p, Remapping p) => Optic p s t a b +type Setter1 s t a b = forall p. (Strong p, Traversing1 p, Mapping1 p) => Optic p s t a b + +type Resetter1 s t a b = forall p. (Closed p, Cotraversing1 p, Remapping1 p) => Optic p s t a b + +type Ixsetter k s t a b = forall p. (Affine p, Traversing p, Mapping p) => Ixoptic p k s t a b + +type Ixsetter1 k s t a b = forall p. (Strong p, Traversing1 p, Mapping1 p) => Ixoptic p k s t a b + +type Rxsetter k s t a b = forall p. (Coaffine p, Cotraversing p, Remapping p) => Cxoptic p k s t a b + +type Rxsetter1 k s t a b = forall p. (Closed p, Cotraversing1 p, Remapping1 p) => Cxoptic p k s t a b + type Setter' s a = Setter s s a a type Resetter' s a = Resetter s s a a +type Setter1' s a = Setter1 s s a a + +type Resetter1' s a = Resetter1 s s a a + +type Ixsetter' k s a = Ixsetter k s s a a + +type Ixsetter1' k s a = Ixsetter1 k s s a a + +type Rxsetter' k t b = Rxsetter k t t b b + +type Rxsetter1' k t b = Rxsetter1 k t t b b + --------------------------------------------------------------------- --- Setter1 +-- View --------------------------------------------------------------------- -type Setter1 s t a b = forall p. (Strong p, Traversing1 p, Mapping1 p) => Optic p s t a b +type View s a = forall p. (Strong p, CoercingR p) => Optic' p s a -type Resetter1 s t a b = forall p. (Closed p, Cotraversing1 p, Remapping1 p) => Optic p s t a b +type Review t b = forall p. (Closed p, CoercingL p) => Optic' p t b -type Setter1' s a = Setter1 s s a a +type Ixview k s a = forall p. (Strong p, CoercingR p) => Ixoptic' p k s a -type Resetter1' s a = Resetter1 s s a a +type Rxview k t b = forall p. (Closed p, CoercingL p) => Cxoptic' p k t b --------------------------------------------------------------------- -- 'Re' diff --git a/profunctor-optics/src/Data/Profunctor/Optic/View.hs b/profunctor-optics/src/Data/Profunctor/Optic/View.hs index 937f257..d6b85a5 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/View.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/View.hs @@ -4,16 +4,18 @@ {-# LANGUAGE TupleSections #-} {-# LANGUAGE FlexibleContexts #-} module Data.Profunctor.Optic.View ( - -- * Types + -- * View View , Review - -- * Constructors , to + , ixto , from + , rxfrom , cloneView , cloneReview -- * Optics , like + , ixlike , relike , toProduct , fromSum @@ -21,11 +23,17 @@ module Data.Profunctor.Optic.View ( , (^.) , view , views - , use - , uses + , (^%) + , viewWithKey + , viewsWithKey , (.^) , review , reviews + , reviewWithKey + , reviewsWithKey + -- * MonadState + , use + , uses , reuse , reuses ) where @@ -44,6 +52,7 @@ import Data.Profunctor.Optic.Fold -- >>> :set -XFlexibleContexts -- >>> :set -XRank2Types -- >>> import Data.Either +-- >>> import qualified Data.Map.Lazy as Map -- >>> import Control.Monad.State -- >>> import Control.Monad.Writer -- >>> :load Data.Profunctor.Optic @@ -137,8 +146,8 @@ like = to . const -- -- @ -- 'relike' a '.' 'relike' b ≡ 'relike' a --- 'relike' a '#' b ≡ a --- 'relike' a '#' b ≡ 'from' ('const' a) '#' b +-- 'relike' a '.^' b ≡ a +-- 'relike' a '.^' b ≡ 'from' ('const' a) '#' b -- @ -- relike :: t -> Review t b @@ -165,15 +174,43 @@ fromSum :: AReview t b1 -> AReview t b2 -> Review t (b1 + b2) fromSum l r = from (review l ||| review r) {-# INLINE fromSum #-} +--------------------------------------------------------------------- +-- Indexed optics +--------------------------------------------------------------------- + +-- | TODO: Document +-- +-- @since 0.0.3 +ixto :: (s -> (k , a)) -> Ixview k s a +ixto f = coercer . lmap (f . snd) +{-# INLINE ixto #-} + +-- | TODO: Document +-- +-- @since 0.0.3 +ixlike :: k -> a -> Ixview k s a +ixlike k a = ixto (const (k, a)) +{-# INLINE ixlike #-} + +-- | TODO: Document +-- +-- >>> cofoldsWithKey (rxfrom Map.mapWithKey # rxfrom Map.mapWithKey) (\k r a -> Map.singleton k (a + r)) 1.0 $ Map.fromList [("k",Map.fromList [("l",2.0)])] +-- fromList [("k",fromList [("l",fromList [("kl",3.0)])])] +-- +-- @since 0.0.3 +rxfrom :: ((k -> b) -> t) -> Rxview k t b +rxfrom f = coercel . rmap (\ib _ -> f ib) +{-# INLINE rxfrom #-} + --------------------------------------------------------------------- -- Operators --------------------------------------------------------------------- -infixl 8 ^. +infix 8 ^. -- | An infix alias for 'view'. -- --- Fixity and semantics are such that subsequent field accesses can be +-- Fiity and semantics are such that subsequent field accesses can be -- performed with ('Prelude..'). -- -- >>> ("hello","world") ^. second' @@ -216,32 +253,48 @@ view o = views o id -- 'Data.Foldable.foldMap' = 'views' 'folding'' -- @ -- --- >>> views both id (["foo"], ["bar", "baz"]) +-- >>> views bitraversed id (["foo"], ["bar", "baz"]) -- ["foo","bar","baz"] -- views :: MonadReader s m => AView r s a -> (a -> r) -> m r views o f = asks $ folds o f {-# INLINE views #-} --- | TODO: Document +infix 8 ^% + +-- | View the focus of an indexed optic along with its index. -- -use :: MonadState s m => AView a s a -> m a -use o = gets (view o) -{-# INLINE use #-} +-- /Note/: if the optic focuses on more than one element, then +-- the returned index will be a monoidal sum of all indices visited. +-- +-- >>> [("foo",41), ("bar",42), ("baz",43)] ^% ix "yo" traversed . ixfirst +-- (Just "yoyoyo","foobarbaz") +-- +-- @since 0.0.3 +(^%) :: Monoid k => s -> AIxview k s a -> (Maybe k, a) +(^%) = flip viewWithKey +{-# INLINE (^%) #-} --- | Use the target of a 'Lens', 'Data.Profunctor.Optic.Iso.Iso' or --- 'View' in the current state, or use a summary of a --- 'Data.Profunctor.Optic.Fold.Fold' or 'Data.Profunctor.Optic.Traversal.Traversal' that --- points to a monoidal value. +-- | A prefix alias for '^%'. -- --- >>> evalState (uses first' length) ("hello","world!") --- 5 +-- >>> viewWithKey ixfirst ("foo", 42) :: (Maybe (Sum Int), String) +-- (Just (Sum {getSum = 0}),"foo") -- -uses :: MonadState s m => AFold r s a -> (a -> r) -> m r -uses l f = gets (views l f) -{-# INLINE uses #-} +-- @since 0.0.3 +viewWithKey :: MonadReader s m => Monoid k => AIxview k s a -> m (Maybe k , a) +viewWithKey o = viewsWithKey o $ \k a -> (Just k, a) +{-# INLINE viewWithKey #-} -infixr 8 .^ +-- | Bring a function of the index and value of an indexed optic into the current environment. +-- +-- Use 'viewWithKey' if there is a need to disambiguate between 'mempty' as a miss vs. as a return value. +-- +-- @since 0.0.3 +viewsWithKey :: MonadReader s m => Monoid k => Ixoptic' (Star (Const r)) k s a -> (k -> a -> r) -> m r +viewsWithKey o f = asks $ foldsWithKey o f +{-# INLINE viewsWithKey #-} + +infix 8 .^ -- | An infix alias of 'review'. -- @@ -262,8 +315,8 @@ infixr 8 .^ -- >>> review left' 4 -- Left 4 -- -review :: MonadReader b m => AReview t b -> m t -review o = asks $ reviews o id +review :: AReview t b -> b -> t +review o = reviews o id {-# INLINE review #-} -- | Turn an optic around and look through the other end, applying a function. @@ -273,9 +326,8 @@ review o = asks $ reviews o id -- 'reviews' ('from' f) g ≡ g '.' f -- @ -- --- >>> reviews left' isRight "mustard" +-- >>> reviews left isRight "mustard" -- False --- -- >>> reviews (from succ) (*2) 3 -- 8 -- @@ -283,6 +335,43 @@ reviews :: AReview t b -> (t -> r) -> b -> r reviews o f = f . unTagged #. o .# Tagged {-# INLINE reviews #-} +-- | Bring a function of the index of a co-indexed optic into the current environment. +-- +-- @since 0.0.3 +reviewWithKey :: ARxview k t b -> b -> (k -> t) +reviewWithKey o = reviewsWithKey o id +{-# INLINE reviewWithKey #-} + +-- | Bring a continuation of the index of a co-indexed optic into the current environment. +-- +-- @ +-- reviewsWithKey :: ARxview k t b -> ((k -> t) -> r) -> b -> r +-- @ +-- +-- @since 0.0.3 +reviewsWithKey :: ARxview k t b -> ((k -> t) -> r) -> b -> r +reviewsWithKey o f = unwrap o f . const where unwrap o1 f1 = f1 . unTagged #. o1 .# Tagged +{-# INLINE reviewsWithKey #-} + +--------------------------------------------------------------------- +-- MonadState +--------------------------------------------------------------------- + +-- | TODO: Document +-- +use :: MonadState s m => AView a s a -> m a +use o = gets (view o) +{-# INLINE use #-} + +-- | Use the target of an optic in the current state. +-- +-- >>> evalState (uses first length) ("hello","world!") +-- 5 +-- +uses :: MonadState s m => AFold r s a -> (a -> r) -> m r +uses l f = gets (views l f) +{-# INLINE uses #-} + -- | Turn an optic around and 'use' a value (or the current environment) through it the other way. -- -- @ @@ -290,9 +379,8 @@ reviews o f = f . unTagged #. o .# Tagged -- 'reuse' '.' 'from' ≡ 'gets' -- @ -- --- >>> evalState (reuse left') 5 +-- >>> evalState (reuse left) 5 -- Left 5 --- -- >>> evalState (reuse (from succ)) 5 -- 6 -- @@ -307,7 +395,7 @@ reuse o = gets (unTagged #. o .# Tagged) -- 'reuses' ('from' f) g ≡ 'gets' (g '.' f) -- @ -- --- >>> evalState (reuses left' isLeft) (5 :: Int) +-- >>> evalState (reuses left isLeft) (5 :: Int) -- True -- reuses :: MonadState b m => AReview t b -> (t -> r) -> m r diff --git a/profunctor-optics/src/Data/Profunctor/Rep/Foldl.hs b/profunctor-optics/src/Data/Profunctor/Rep/Foldl.hs index 54d3af1..960a934 100644 --- a/profunctor-optics/src/Data/Profunctor/Rep/Foldl.hs +++ b/profunctor-optics/src/Data/Profunctor/Rep/Foldl.hs @@ -5,250 +5,345 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} -{-# LANGUAGE Safe #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Data.Profunctor.Rep.Foldl ( - -- * Foldl type L - , Fold (..) - , withFoldl + -- * Foldl + , Foldl (..) , run - , fold + , foldl + , withFoldl , prefix - , scan , prescan , postscan -- * Folds - , mconcat - , foldMap - - - , head - , last - , lastDef - --, lastN - , null - , length - , and - , or - , all - , any - --, sum - --, product - --, mean - --, variance - --, std - , maximum - , maximumBy - , minimum - , minimumBy - , elem - , notElem - , find - , Control.Foldl.index - , lookup - , elemIndex - , findIndex - - -- * Generic Folds - , genericLength - , genericIndex - - -- * Container folds , list , revList - , nub - , eqNub - , set - --, hashSet - , map - --, hashMap - --, vector - - -- * Utilities - -- $utilities - , purely - , purely_ - , _Fold1 - , premap - , prefilter - -- , predropWhile - --, drop - , Handler - , handles - , foldOver - , folded - , filtered - , groupBy - , either - --, nest - - -- * EndoM - , EndoM(..) -) where - -import Control.Foldl hiding (product, sum) + , mconcat + , stepMay + , stepDef + , headDef + , lastDef + , maximumDef + , maximumByDef + , minimumDef + , minimumByDef + ) where import Control.Applicative import Control.Monad.Fix (MonadFix(..)) import Control.Monad.Reader (MonadReader(..)) import Data.Distributive (Distributive (..)) import Data.Functor.Rep as Functor (Representable (..), askRep, localRep, mfixRep) -import Data.Profunctor (Costrong (..)) +import Data.List (mapAccumL) +import Data.Profunctor import Data.Profunctor.Closed (Closed (..)) import Data.Profunctor.Rep as Profunctor (Corepresentable (..), unfirstCorep, unsecondCorep) import Data.Profunctor.Sieve (Cosieve (..)) ---import Data.Semiring (type (-), Semiring, zero, one, (+), (*)) - +import Data.Strict.Tuple import Prelude as P hiding ( head, last, null, length, and, or, all, any, sum, product, maximum, minimum, mconcat, elem, notElem, lookup, map, either, drop, - Num(..), Fractional(..), foldMap + Num(..), Fractional(..), foldl ) +import qualified Data.Foldable as F +import qualified Data.Strict.Maybe as M' type L r a b = forall x. (x -> a -> x) -> x -> (x -> b) -> r --------------------------------------------------------------------- --- Fold +-- Foldl --------------------------------------------------------------------- ---data Fold a b = forall x. Fold (x -> a -> x) x (x -> b) +data Foldl a b = forall x. Foldl (x -> a -> x) x (x -> b) -run :: Fold a b -> a -> b -run (Fold h z k) t = k (h z t) +run :: Foldl a b -> a -> b +run (Foldl h z k) t = k (h z t) +{-# INLINABLE run #-} -prefix :: a -> Fold a b -> Fold a b -prefix a = flip run a . duplicate +foldl :: Foldable f => Foldl a b -> f a -> b +foldl (Foldl step begin done) as = F.foldr cons done as begin + where + cons a k x = k $! step x a +{-# INLINABLE foldl #-} -- | TODO: Document -- -withFoldl :: Fold a b -> L r a b -> r -withFoldl (Fold h z k) f = f h z k -{-# INLINE withFoldl #-} +withFoldl :: Foldl a b -> L r a b -> r +withFoldl (Foldl h z k) f = f h z k +{-# INLINABLE withFoldl #-} + +prefix :: a -> Foldl a b -> Foldl a b +prefix a = flip run a . duplicate +{-# INLINABLE prefix #-} {- +type IndexedFoldl i a b = Foldl (i, a) b + +ifoldl :: FoldableWithKey f => IndexedFoldl (Key f) a b -> f a -> b +ifoldl (Foldl step begin done) as = foldrWithKey cons done as begin + where + cons i a k x = k $! step x (i, a) + +{-| Convert a strict left 'Fold' into a scan + + >>> L.scan L.length [1..5] + [0,1,2,3,4,5] +-} +scan :: Foldl a b -> [a] -> [b] +scan (Foldl step begin done) as = foldr cons nil as begin + where + nil x = done x:[] + cons a k x = done x:(k $! step x a) +{-# INLINE scan #-} + + +-} + +-- | Convert a `Foldl` into a prescan for any `Traversable` type +-- +-- \"Prescan\" means that the lastDef element of the scan is not included +-- +prescan :: Traversable f => Foldl a b -> f a -> f b +prescan (Foldl step begin done) as = bs + where + step' x a = (x', b) + where + x' = step x a + b = done x + (_, bs) = mapAccumL step' begin as +{-# INLINE prescan #-} + +-- | Convert a `Foldl` into a postscan for any `Traversable` type +-- +-- \"Postscan\" means that the first element of the scan is not included +-- +postscan :: Traversable f => Foldl a b -> f a -> f b +postscan (Foldl step begin done) as = bs + where + step' x a = (x', b) + where + x' = step x a + b = done x' + (_, bs) = mapAccumL step' begin as +{-# INLINE postscan #-} + --------------------------------------------------------------------- -- Folds --------------------------------------------------------------------- +-- | Foldl all values into a list +list :: Foldl a [a] +list = Foldl (\x a -> x . (a:)) id ($ []) +{-# INLINABLE list #-} + +-- | Foldl all values into a list, in reverse order +revList :: Foldl a [a] +revList = Foldl (\x a -> a:x) [] id +{-# INLINABLE revList #-} + +-- | Convert a \"@mconcats@\" to a 'Fold' +mconcat :: Monoid m => (a -> m) -> (m -> b) -> Foldl a b +mconcat to = Foldl (\x a -> mappend x (to a)) mempty +{-# INLINABLE mconcat #-} + +-- | Return the result of a step function. +-- +-- Results in a 'Nothing' value for empty containers. +-- +stepMay :: (a -> a -> a) -> Foldl a (Maybe a) +stepMay step = Foldl step_ M'.Nothing lazy + where + step_ mx a = M'.Just (case mx of + M'.Nothing -> a + M'.Just x -> step x a) +{-# INLINABLE stepMay #-} + +stepDef :: a -> (a -> a -> a) -> Foldl a a +stepDef a step = maybe a id <$> stepMay step +{-# INLINABLE stepDef #-} +{- -- | Return the sum of all elements. -- -sum :: (Sum-Monoid) a => Fold a a +sum :: (Sum-Monoid) a => Foldl a a sum = sumWith id {-# INLINABLE sum #-} -- | Return the sum of all elements. -- -sumWith :: (Sum-Monoid) b => (a -> b) -> Fold a b -sumWith f = Fold (\x y -> x + f y) zero id +sumWith :: (Sum-Monoid) b => (a -> b) -> Foldl a b +sumWith f = Foldl (\x y -> x + f y) zero id {-# INLINABLE sumWith #-} -- | Return the product of all elements. -- -product :: (Product-Monoid) a => Fold a a +product :: (Product-Monoid) a => Foldl a a product = productWith id {-# INLINABLE product #-} -- | Return the product of all elements. -- -productWith :: (Product-Monoid) b => (a -> b) -> Fold a b -productWith f = Fold (\x y -> x * f y) one id +productWith :: (Product-Monoid) b => (a -> b) -> Foldl a b +productWith f = Foldl (\x y -> x * f y) one id {-# INLINABLE productWith #-} +-} --- | Return the maximum element of a collection. +-- | Return the first element of a collection. -- --- Returns /Nothing/ if the container is empty. +-- Returns a default if the container is empty. -- -maximumMay :: Ord a => Fold a (Maybe a) -maximumMay = _Fold1 max -{-# INLINABLE maximumMay #-} +headDef :: a -> Foldl a a +headDef a = stepDef a const +{-# INLINABLE headDef #-} --- | Return the maximum element of a collection. +-- | Return the last element of a collection. -- --- Returns a default value if the container is empty. +-- Returns a default if the container is empty. -- -maximumDef :: Ord a => a -> Fold a a -maximumDef a = fmap (maybe a id) maximumMay -{-# INLINABLE maximumDef #-} +lastDef :: a -> Foldl a a +lastDef a = stepDef a (flip const) +{-# INLINABLE lastDef #-} --- | Return the minimum element of a collection. +-- | Return the maximumDef element of a collection. -- --- Returns /Nothing/ if the container is empty. +-- Returns a default if the container is empty. -- -minimumMay :: Ord a => Fold a (Maybe a) -minimumMay = _Fold1 min -{-# INLINABLE minimumMay #-} +maximumDef :: Ord a => a ->Foldl a a +maximumDef a = stepDef a max +{-# INLINABLE maximumDef #-} --- | Return the minimum element of a collection. +-- | Return the maximumDef element of a collection wrt a comparator. +-- +-- Returns a default if the container is empty. +-- +maximumByDef :: (a -> a -> Ordering) -> a -> Foldl a a +maximumByDef cmp a = stepDef a max' + where + max' x y = case cmp x y of + GT -> x + _ -> y +{-# INLINABLE maximumByDef #-} + +-- | Return the minimumDef element of a collection. -- --- Returns a default value if the container is empty. +-- Returns a default if the container is empty. -- -minimumDef :: Ord a => a -> Fold a a -minimumDef a = fmap (maybe a id) minimumMay +minimumDef :: Ord a => a -> Foldl a a +minimumDef a = stepDef a min {-# INLINABLE minimumDef #-} --} + +-- | Return the minimumDef element of a collection wrt a comparator. +-- +-- Returns a default if the container is empty. +-- +minimumByDef :: (a -> a -> Ordering) -> a -> Foldl a a +minimumByDef cmp a = stepDef a min' + where + min' x y = case cmp x y of + GT -> y + _ -> x +{-# INLINABLE minimumByDef #-} --------------------------------------------------------------------- --- Orphan Fold instances +-- Instances --------------------------------------------------------------------- +-- | Convert 'Maybe'' to 'Maybe' +lazy :: M'.Maybe a -> Maybe a +lazy M'.Nothing = Nothing +lazy (M'.Just a) = Just a +{-# INLINABLE lazy #-} + -- Comonad instances -extract :: Fold a b -> b -extract (Fold _ z k) = k z +extract :: Foldl a b -> b +extract (Foldl _ z k) = k z -duplicate :: Fold a b -> Fold a (Fold a b) -duplicate (Fold h z k) = Fold h z (flip (Fold h) k) +duplicate :: Foldl a b -> Foldl a (Foldl a b) +duplicate (Foldl h z k) = Foldl h z (flip (Foldl h) k) ---extend :: (Fold a b -> c) -> Fold a b -> Fold a c ---extend f (Fold h z k) = Fold h z (f . flip (Fold h) k) +--extend :: (Foldl a b -> c) -> Foldl a b -> Foldl a c +--extend f (Foldl h z k) = Foldl h z (f . flip (Foldl h) k) -instance Distributive (Fold a) where +instance Functor (Foldl a) where + fmap f (Foldl step begin done) = Foldl step begin (f . done) + {-# INLINE fmap #-} + +instance Profunctor Foldl where + rmap = fmap + lmap f (Foldl step begin done) = Foldl step' begin done + where + step' x a = step x (f a) - distribute z = Fold (\fm a -> fmap (prefix a) fm) z (fmap extract) +instance Choice Foldl where + right' (Foldl step begin done) = Foldl (liftA2 step) (Right begin) (fmap done) + {-# INLINE right' #-} -instance Functor.Representable (Fold a) where +{- +instance Comonad (Foldl a) where + extract (Foldl _ begin done) = done begin + {-# INLINE extract #-} + + duplicate (Foldl step begin done) = Foldl step begin (\x -> Foldl step x done) + {-# INLINE duplicate #-} +-} - type Rep (Fold a) = [a] +instance Applicative (Foldl a) where + pure b = Foldl (\() _ -> ()) () (\() -> b) + {-# INLINE pure #-} + + (Foldl stepL beginL doneL) <*> (Foldl stepR beginR doneR) = + let step (xL :!: xR) a = (stepL xL a) :!: (stepR xR a) + begin = beginL :!: beginR + done (xL :!: xR) = doneL xL (doneR xR) + in Foldl step begin done + {-# INLINE (<*>) #-} + +instance Distributive (Foldl a) where + + distribute z = Foldl (\fm a -> fmap (prefix a) fm) z (fmap extract) + +instance Functor.Representable (Foldl a) where + + type Rep (Foldl a) = [a] index = cosieve tabulate = cotabulate -instance Cosieve Fold [] where +instance Cosieve Foldl [] where - cosieve (Fold k0 h0 z0) as0 = go k0 h0 z0 as0 + cosieve (Foldl k0 h0 z0) as0 = go k0 h0 z0 as0 where go _ z k [] = k z go h z k (a : as) = go h (h z a) k as -instance Closed Fold where +instance Closed Foldl where - closed (Fold h z k) = Fold (liftA2 h) (pure z) (\f x -> k (f x)) + closed (Foldl h z k) = Foldl (liftA2 h) (pure z) (\f x -> k (f x)) -instance Costrong Fold where +instance Costrong Foldl where unfirst = unfirstCorep unsecond = unsecondCorep -instance Corepresentable Fold where +instance Corepresentable Foldl where - type Corep Fold = [] + type Corep Foldl = [] - cotabulate f = Fold (flip (:)) [] (f . reverse) + cotabulate f = Foldl (flip (:)) [] (f . reverse) -instance Monad (Fold a) where +instance Monad (Foldl a) where - m >>= f = Fold (flip (:)) [] (\xs -> flip fold xs . f) <*> m + m >>= f = Foldl (flip (:)) [] (\xs -> flip foldl xs . f) <*> m -instance MonadReader [a] (Fold a) where +instance MonadReader [a] (Foldl a) where ask = askRep local = localRep -instance MonadFix (Fold a) where +instance MonadFix (Foldl a) where mfix = mfixRep - diff --git a/profunctor-optics/src/Data/Profunctor/Rep/Foldl1.hs b/profunctor-optics/src/Data/Profunctor/Rep/Foldl1.hs index 790f423..ac6b93d 100644 --- a/profunctor-optics/src/Data/Profunctor/Rep/Foldl1.hs +++ b/profunctor-optics/src/Data/Profunctor/Rep/Foldl1.hs @@ -6,12 +6,11 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE BangPatterns #-} -{-# LANGUAGE Safe #-} {-# LANGUAGE RankNTypes #-} module Data.Profunctor.Rep.Foldl1 ( - -- * Foldl1 type L1 + -- * Foldl1 , Foldl1 (..) , run1 , step @@ -23,17 +22,16 @@ module Data.Profunctor.Rep.Foldl1 ( , list1 , revList1 , sconcat - , foldMap1 --, sum1 --, sumWith1 --, prod1 --, prodWith1 - , head1 - , last1 - , maximum1 - , maximum1By - , minimum1 - , minimum1By + , head + , last + , maximum + , maximumBy + , minimum + , minimumBy -- * Nedl , Nedl(..) , nedl @@ -43,7 +41,6 @@ module Data.Profunctor.Rep.Foldl1 ( import Control.Applicative (liftA2) import Control.Arrow (Arrow (..), ArrowChoice(..)) import Control.Category (Category) -import Control.Foldl (Fold(..)) import Control.Monad.Fix (MonadFix(..)) import Control.Monad.Reader (MonadReader(..)) import Data.Distributive (Distributive (..)) @@ -53,16 +50,18 @@ import Data.Monoid import Data.List.NonEmpty as NonEmpty (NonEmpty (..), (<|), fromList) import Data.Profunctor import Data.Profunctor.Closed (Closed (..)) +import qualified Data.Profunctor.Rep.Foldl as L import Data.Profunctor.Rep as Profunctor (Corepresentable (..), unfirstCorep, unsecondCorep) import Data.Profunctor.Sieve (Cosieve (..)) --import Data.Semiring hiding (sum1, sumWith1, product1, productWith1, sum, sumWith, product, productWith) import qualified Control.Category as C ((.), id) import qualified Data.List.NonEmpty as NEL -import qualified Data.Semigroup.Foldable as F1 hiding (fold1, foldMap1) +import qualified Data.Semigroup.Foldable as F1 hiding (fold1) import Prelude as P hiding ( null, length, and, or, all, any, sum, foldl1, product, mconcat, elem , notElem, lookup, map, either, drop, Num(..), Fractional(..) + , minimum, maximum, last, head ) type L1 r a b = forall x. (x -> a -> x) -> (a -> x) -> (x -> b) -> r @@ -73,27 +72,33 @@ type L1 r a b = forall x. (x -> a -> x) -> (a -> x) -> (x -> b) -> r data Foldl1 a b = forall x. Foldl1 (x -> a -> x) (a -> x) (x -> b) --- | Lift a 'Fold' into a 'Foldl1'. +-- | Lift a 'Foldl' into a 'Foldl1'. -- -- All of the folds defined in 'Data.Profunctor.Rep.Foldl' may be run as 'Foldl1's. -- -step :: Fold a b -> Foldl1 a b -step (Fold h z k) = Foldl1 h (h z) k +step :: L.Foldl a b -> Foldl1 a b +step (L.Foldl h z k) = Foldl1 h (h z) k +{-# INLINABLE step #-} run1 :: Foldl1 a b -> a -> b run1 (Foldl1 _ z k) a = k (z a) +{-# INLINABLE run1 #-} foldl1 :: F1.Foldable1 f => Foldl1 a b -> f a -> b foldl1 f = cosieve f . F1.toNonEmpty +{-# INLINABLE foldl1 #-} withFoldl1 :: Foldl1 a b -> L1 r a b -> r withFoldl1 (Foldl1 h z k) f = f h z k +{-# INLINABLE withFoldl1 #-} prefix1 :: a -> Foldl1 a b -> Foldl1 a b prefix1 a (Foldl1 h z k) = Foldl1 h (h (z a)) k +{-# INLINABLE prefix1 #-} intersperse1 :: a -> Foldl1 a b -> Foldl1 a b intersperse1 a (Foldl1 h z k) = Foldl1 (\x b -> (h $! h x a) b) z k +{-# INLINABLE intersperse1 #-} --------------------------------------------------------------------- -- Non-empty folds @@ -117,16 +122,10 @@ revList1 = Foldl1 (\as a -> nedl a <> as) nedl runNedl -- | Fold all values within a container using a semigroup. -- -sconcat :: Semigroup a => Foldl1 a a -sconcat = Foldl1 (<>) id id +sconcat :: Semigroup s => (a -> s) -> (s -> b) -> Foldl1 a b +sconcat to = Foldl1 (\x a -> x <> (to a)) to {-# INLINABLE sconcat #-} --- | Fold all values within a container using a semigroup. --- -foldMap1 :: Semigroup s => (a -> s) -> (s -> b) -> Foldl1 a b -foldMap1 to = Foldl1 (\x a -> x <> (to a)) to -{-# INLINABLE foldMap1 #-} - {- -- | Return the sum of all elements in a non-empty container. -- @@ -155,47 +154,47 @@ prodWith1 f = Foldl1 (\x y -> x * f y) f id -- | Return the first element in a non-empty container. -- -head1 :: Foldl1 a a -head1 = Foldl1 const id id -{-# INLINABLE head1 #-} +head :: Foldl1 a a +head = Foldl1 const id id +{-# INLINABLE head #-} -- | Return the last1 element in a non-empty container. -- -last1 :: Foldl1 a a -last1 = Foldl1 (flip const) id id -{-# INLINABLE last1 #-} +last :: Foldl1 a a +last = Foldl1 (flip const) id id +{-# INLINABLE last #-} --- | Return the maximum1 element in a non-empty container. +-- | Return the maximum element in a non-empty container. -- -maximum1 :: Ord a => Foldl1 a a -maximum1 = Foldl1 max id id -{-# INLINABLE maximum1 #-} +maximum :: Ord a => Foldl1 a a +maximum = Foldl1 max id id +{-# INLINABLE maximum #-} --- | Return the maximum1 element with respect to a comparator. +-- | Return the maximum element with respect to a comparator. -- -maximum1By :: (a -> a -> Ordering) -> Foldl1 a a -maximum1By cmp = Foldl1 max' id id +maximumBy :: (a -> a -> Ordering) -> Foldl1 a a +maximumBy cmp = Foldl1 max' id id where max' x y = case cmp x y of GT -> x _ -> y -{-# INLINABLE maximum1By #-} +{-# INLINABLE maximumBy #-} --- | Return the minimum1 element in a non-empty container. +-- | Return the minimum element in a non-empty container. -- -minimum1 :: Ord a => Foldl1 a a -minimum1 = Foldl1 min id id -{-# INLINABLE minimum1 #-} +minimum :: Ord a => Foldl1 a a +minimum = Foldl1 min id id +{-# INLINABLE minimum #-} --- | Return the minimum1 element with respect to a comparator. +-- | Return the minimum element with respect to a comparator. -- -minimum1By :: (a -> a -> Ordering) -> Foldl1 a a -minimum1By cmp = Foldl1 min' id id +minimumBy :: (a -> a -> Ordering) -> Foldl1 a a +minimumBy cmp = Foldl1 min' id id where min' x y = case cmp x y of GT -> y _ -> x -{-# INLINABLE minimum1By #-} +{-# INLINABLE minimumBy #-} ------------------------------------------------------------------------------ -- Nedl diff --git a/profunctor-optics/src/Data/Tuple/Optic.hs b/profunctor-optics/src/Data/Tuple/Optic.hs index 4b7231d..a63261a 100644 --- a/profunctor-optics/src/Data/Tuple/Optic.hs +++ b/profunctor-optics/src/Data/Tuple/Optic.hs @@ -6,7 +6,7 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeFamilies #-} module Data.Tuple.Optic ( - curried + uncurried , swapped , associated , t21 diff --git a/stack.yaml b/stack.yaml index 78593d2..03285fc 100644 --- a/stack.yaml +++ b/stack.yaml @@ -6,11 +6,9 @@ packages: extra-deps: - profunctors-5.5.1@sha256:48a53d69f997693db6b3fd339e92edea7776420ace5101737f037b04306096a7 + - github: chessai/these-skinny + commit: 2f20555fcd06612af8c88ccaaa5610307f2427ec - github: cmk/lawz commit: 55c8dcdc6322f7c35c8568e1317afb5d35c3963d - github: cmk/coapplicative commit: 9ef7978e44887246cc8b7bb54c8b292c246130aa - - github: cmk/rings - commit: 8f933f7f1b41408c78360122632465533a6b5b70 - - github: cmk/magmas - commit: f86a7433ffa13407b939280523feb661143a800a