diff --git a/profunctor-optics/profunctor-optics.cabal b/profunctor-optics/profunctor-optics.cabal index e084b19..ec8211f 100644 --- a/profunctor-optics/profunctor-optics.cabal +++ b/profunctor-optics/profunctor-optics.cabal @@ -40,6 +40,7 @@ library Data.Profunctor.Optic Data.Profunctor.Optic.Types + Data.Profunctor.Optic.Module Data.Profunctor.Optic.Property Data.Profunctor.Optic.Carrier Data.Profunctor.Optic.Combinator diff --git a/profunctor-optics/src/Data/Profunctor/Optic.hs b/profunctor-optics/src/Data/Profunctor/Optic.hs index 22f74a9..fda6f7a 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic.hs @@ -7,6 +7,7 @@ {-# LANGUAGE TypeFamilies #-} module Data.Profunctor.Optic ( module Types + , module Module , module Carrier , module Operator , module Iso @@ -21,6 +22,7 @@ module Data.Profunctor.Optic ( ) where import Data.Profunctor.Optic.Types as Types +import Data.Profunctor.Optic.Module as Module import Data.Profunctor.Optic.Carrier as Carrier import Data.Profunctor.Optic.Combinator as Operator import Data.Profunctor.Optic.Iso as Iso diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Fold.hs b/profunctor-optics/src/Data/Profunctor/Optic/Fold.hs index 742e62c..93b3084 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Fold.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Fold.hs @@ -36,8 +36,7 @@ module Data.Profunctor.Optic.Fold ( , (^?) , preview , preuse - , is - , isnt + , fits , lists , (^..) , nelists @@ -60,8 +59,7 @@ module Data.Profunctor.Optic.Fold ( , endo , endoM , finds - , has - , hasnt + , exists , contains -- * Auxilliary Types , Nedl(..) @@ -128,7 +126,7 @@ fold0 f = to (\s -> maybe (Left s) Right (f s)) . right' infixl 3 `failing` --- | If the first 'Fold0' has no focus then try the second one. +-- | If the first 'Fold0' exists no focus then try the second one. -- failing :: AFold0 a s a -> AFold0 a s a -> Fold0 s a failing a b = fold0 $ \s -> maybe (preview b s) Just (preview a s) @@ -335,23 +333,14 @@ preuse :: MonadState s m => AFold0 a s a -> m (Maybe a) preuse o = State.gets $ preview o {-# INLINE preuse #-} --- | Check whether the optic is matched. +-- | Check whether the optic fits. -- --- >>> is just Nothing +-- >>> fits just Nothing -- False -- -is :: AFold0 a s a -> s -> Bool -is o s = isJust (preview o s) -{-# INLINE is #-} - --- | Check whether the optic isn't matched. --- --- >>> isnt just Nothing --- True --- -isnt :: AFold0 a s a -> s -> Bool -isnt o s = not (isJust (preview o s)) -{-# INLINE isnt #-} +fits :: AFold0 a s a -> s -> Bool +fits o s = isJust (preview o s) +{-# INLINE fits #-} -- | Collect the foci of an optic into a list. -- @@ -566,17 +555,11 @@ finds :: AFold ((Maybe-Endo) a) s a -> (a -> Bool) -> s -> Maybe a finds o f = foldsr o (\a y -> if f a then Just a else y) Nothing {-# INLINE finds #-} --- | Determine whether an optic has at least one focus. --- -has :: AFold (Additive Bool) s a -> s -> Bool -has o s = unAdditive $ withFold o (const $ Additive True) s -{-# INLINE has #-} - --- | Determine whether an optic does not have a focus. +-- | Determine whether an optic exists at least one focus. -- -hasnt :: AFold (Multiplicative Bool) s a -> s -> Bool -hasnt o s = unMultiplicative $ withFold o (const $ Multiplicative False) s -{-# INLINE hasnt #-} +exists :: AFold (Additive Bool) s a -> s -> Bool +exists o s = unAdditive $ withFold o (const $ Additive True) s +{-# INLINE exists #-} -- | Determine whether the targets of a `Fold` contain a given element. -- diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Lens.hs b/profunctor-optics/src/Data/Profunctor/Optic/Lens.hs index 50a8303..7695a5d 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Lens.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Lens.hs @@ -31,6 +31,7 @@ module Data.Profunctor.Optic.Lens ( , voided , represented , distributed + , distributed1 , endomorphed , precomposed , dotted @@ -104,7 +105,7 @@ lens sa sbt = dimap (id &&& sa) (uncurry sbt) . second' -- | Transform a Van Laarhoven lens into a profunctor lens. -- --- Compare 'Data.Profunctor.Optic.Grate.grateVl' and 'Data.Profunctor.Optic.Traversal.traversalVl'. +-- Compare 'grateVl' and 'Data.Profunctor.Optic.Traversal.traversalVl'. -- -- /Caution/: In order for the generated optic to be well-defined, -- you must ensure that the input satisfies the following properties: @@ -297,6 +298,12 @@ distributed :: Distributive f => Grate (f a) (f b) a b distributed = grate (`cotraverse` id) {-# INLINE distributed #-} +-- | Obtain a 'Grate' from a partially distributive functor. +-- +distributed1 :: Monoid (f a) => Distributive1 f => Grate (f a) (f b) a b +distributed1 = grate (`cotraverse1` id) +{-# INLINE distributed1 #-} + -- | Obtain a 'Grate' from an endomorphism. -- -- >>> flip appEndo 2 $ zipsWith2 endomorphed (+) (Endo (*3)) (Endo (*4)) diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Module.hs b/profunctor-optics/src/Data/Profunctor/Optic/Module.hs new file mode 100644 index 0000000..0005d53 --- /dev/null +++ b/profunctor-optics/src/Data/Profunctor/Optic/Module.hs @@ -0,0 +1,313 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE QuantifiedConstraints #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE QuantifiedConstraints #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE ExistentialQuantification #-} + +module Data.Profunctor.Optic.Module where + +import Data.Profunctor.Optic.Carrier +import Data.Profunctor.Optic.Combinator +import Data.Profunctor.Optic.Import +import Data.Profunctor.Optic.Iso +import Data.Profunctor.Optic.Lens +import Data.Profunctor.Optic.Prism +import Data.Profunctor.Optic.Types +import Data.Tuple.Optic + +import Data.Kind (Type) + +-- $setup +-- >>> :set -XNoOverloadedStrings +-- >>> :set -XTypeApplications +-- >>> :set -XFlexibleContexts +-- >>> :set -XTypeOperators +-- >>> :set -XRankNTypes +-- >>> :load Data.Profunctor.Optic + +--------------------------------------------------------------------- +-- Module +--------------------------------------------------------------------- + +type Is = Module Tagged + +type As = Module (+) + +type Has = Module (,) + +type With = Module (->) + +-- | A (right) Tambara module on the tensor /o/. +-- +class Module (o :: Type -> Type -> Type) a s where + optic :: FreeTambara o a a s s + +-- | Obtain an iso from an 'Is' instance. +-- +-- >>> view (is @Int @Int) 4 +-- 4 +-- +is :: Is a s => Iso' s a +is = lowerIso optic + +-- | Obtain a prism from an 'As' instance. +-- +-- >>> review (as @Int @Int) 4 +-- 4 +-- >>> Left "oops" ^? as @String @(String + Int) +-- Just "oops" +-- +as :: As a s => Prism' s a +as = handling' optic + +-- | Obtain a coprism from an 'As' instance. +-- +-- >>> 4 ^. coas @Int @(String + Int) +-- Right 4 +-- +coas :: As t b => Coprism' t b +coas = re $ handling' optic + +-- | Obtain a lens from a 'Has' instance. +-- +-- >>> (1,2,"foo") ^. has @String @(Int,Int,String) +-- "foo" +-- +has :: Has a s => Lens' s a +has = matching' optic + +-- | Obtain a colens from a 'Has' instance. +-- +-- >>> review (cohas @Int @(Int,String)) (2, "two") +-- 2 +-- >>> zipsWith2 (cohas @Int @Int) (+) 2 2 +-- 4 +-- +cohas :: Has t b => Colens' t b +cohas = re $ matching' optic + +-- | Obtain a grate from a 'With' instance. +-- +-- >>> review (with @Int @Int) 4 +-- 4 +-- >>> zipsWith2 (with @String @String) (++) "foo" "bar" +-- "foobar" +-- +with :: With a s => Grate' s a +with = inverting' optic + +-- | Obtain a traversal from a 'Has' instance. +-- +-- >>> foldsr (have @Int @Int @[]) (\i -> (show i ++)) [] [1..5] +-- "12345" +-- +have :: Has a s => Traversable f => Traversal' (f s) a +have = repn traverse . has + +-- | Obtain a non-empty traversal from a 'Has' instance. +-- +have1 :: Has a s => Traversable1 f => Traversal1' (f s) a +have1 = repn traverse1 . has + +-- | Obtain a cotraversal from a 'Has' instance. +-- +cohave :: Has t b => Distributive g => Cotraversal' (g t) b +cohave = corepn cotraverse . cohas + +-- | Obtain a partial cotraversal from a 'Has' instance. +-- +cohave1 :: Has t b => Distributive1 g => Cotraversal1' (g t) b +cohave1 = corepn cotraverse1 . cohas + +--------------------------------------------------------------------- +-- FreeTambara +--------------------------------------------------------------------- + +type FreeIso s t a b = FreeTambara Tagged a b s t + +type FreeIso' s a = FreeIso s s a a + +type FreeLens s t a b = FreeTambara (,) a b s t + +type FreeLens' s a = FreeLens s s a a + +type FreeGrate s t a b = FreeTambara (->) a b s t + +type FreeGrate' s a = FreeGrate s s a a + +type FreeColens s t a b = FreeTambara (,) t s b a + +type FreeColens' t b = FreeColens b b t t + +type FreePrism s t a b = FreeTambara (+) a b s t + +type FreePrism' s a = FreePrism s s a a + +type FreeCoprism s t a b = FreeTambara (+) t s b a + +type FreeCoprism' t b = FreeCoprism t t b b + +type FreeTambara' o a s = FreeTambara o a a s s + +data FreeTambara o a b s t = forall x. FreeTambara (s -> x `o` a) (x `o` b -> t) + +lowerIso :: FreeIso s t a b -> Iso s t a b +lowerIso (FreeTambara f g) = iso (unTagged . f) (g . Tagged) + +handling' :: FreePrism s t a b -> Prism s t a b +handling' (FreeTambara f g) = handling f g + +cohandling' :: FreeCoprism s t a b -> Coprism s t a b +cohandling' (FreeTambara f g) = cohandling g f + +matching' :: FreeLens s t a b -> Lens s t a b +matching' (FreeTambara f g) = matching f g + +comatching' :: FreeColens s t a b -> Colens s t a b +comatching' (FreeTambara f g) = comatching g f + +inverting' :: FreeGrate s t a b -> Grate s t a b +inverting' (FreeTambara l r) = dimap l r . closed + +liftIso :: Is a1 a2 => Iso' s a2 -> FreeIso' s a1 +liftIso o = withIso (o . is) $ \ba ab -> FreeTambara (Tagged . ba) (ab . unTagged) + +liftPrism :: As a1 a2 => Prism' s a2 -> FreePrism' s a1 +liftPrism o = withPrism o liftChoice + +liftCoprism :: As b1 b2 => Coprism' b2 t -> FreeCoprism' b1 t +liftCoprism o = withPrism (re o) liftChoice + +liftLens :: Has a1 a2 => Lens' s a2 -> FreeLens' s a1 +liftLens o = withLens o liftStrong + +liftColens :: Has b1 b2 => Colens' b2 t -> FreeColens' t b1 +liftColens o = withLens (re o) liftStrong + +liftGrate :: With a1 a2 => Grate' s a2 -> FreeGrate' s a1 +liftGrate o = withGrate (o . with) $ \sabt -> FreeTambara (curry eval) sabt + +liftChoice :: As a1 a2 => (s -> s + a2) -> (a2 -> s) -> FreeTambara' (+) a1 s +liftChoice ssb bs = withPrism as $ \bba ab -> + FreeTambara (first (either id bs) . eassocl . fmap bba . ssb) (either id (bs.ab) ) + +liftStrong :: Has a1 a2 => (s -> a2) -> (s -> a2 -> s) -> FreeTambara' (,) a1 s +liftStrong sb sbs = withLens has $ \ba bab -> + FreeTambara (\s -> (s, ba.sb $ s)) (\(s,a) -> sbs s (bab (sb s) a)) + +--------------------------------------------------------------------- +-- FreeTambara instances +--------------------------------------------------------------------- + +instance Profunctor (FreeTambara o a b) where + dimap f g (FreeTambara l r) = FreeTambara (l . f) (g . r) + +instance Strong (FreeTambara (,) a b) where + second' (FreeTambara l r) = FreeTambara l' r' where + l' (d,s) = ((d , fst (l s)), snd (l s)) + r' ((d,c),b) = (d,r (c,b)) + +instance Choice (FreeTambara (+) a b) where + right' (FreeTambara l r) = FreeTambara l' r' where + l' = (either (Left . Left) (either (Left . Right) Right . l)) + r' = (either (either Left (Right . r . Left)) (Right . r . Right)) + +instance Closed (FreeTambara (->) a b) where + closed (FreeTambara l r) = FreeTambara l' r' where + l' f (d , c) = l (f d) c + r' = (r .) . curry + +--------------------------------------------------------------------- +-- Is instances +--------------------------------------------------------------------- + +instance Module Tagged a a where + optic = FreeTambara Tagged unTagged + +--------------------------------------------------------------------- +-- As instances +--------------------------------------------------------------------- + +instance Module (+) a a where + optic = FreeTambara Right rgt' + +instance Module (+) a (a + b) where + optic = liftPrism left' + +instance Module (+) b (a + b) where + optic = liftPrism right' + +instance Module (+) a (Maybe a) where + optic = liftPrism just + +instance Module (+) () (Maybe a) where + optic = liftPrism nothing + +--------------------------------------------------------------------- +-- Has instances +--------------------------------------------------------------------- + +instance Module (,) a a where + optic = FreeTambara ((,) ()) snd + +instance Module (,) a (a , b) where + optic = liftLens t21 + +instance Module (,) b (a , b) where + optic = liftLens t22 + +instance Module (,) a (a,b,c) where + optic = liftLens t31 + +instance Module (,) b (a,b,c) where + optic = liftLens t32 + +instance Module (,) c (a,b,c) where + optic = liftLens t33 + +instance Module (,) a (a,b,c,d) where + optic = liftLens t41 + +instance Module (,) b (a,b,c,d) where + optic = liftLens t42 + +instance Module (,) c (a,b,c,d) where + optic = liftLens t43 + +instance Module (,) d (a,b,c,d) where + optic = liftLens t44 + +instance Module (,) a (a,b,c,d,e) where + optic = liftLens t51 + +instance Module (,) b (a,b,c,d,e) where + optic = liftLens t52 + +instance Module (,) c (a,b,c,d,e) where + optic = liftLens t53 + +instance Module (,) d (a,b,c,d,e) where + optic = liftLens t54 + +instance Module (,) e (a,b,c,d,e) where + optic = liftLens t55 + +--------------------------------------------------------------------- +-- With instances +--------------------------------------------------------------------- + +instance Module (->) a a where + optic = FreeTambara const ($ ()) + +instance Distributive f => Module (->) a (f a) where + optic = liftGrate distributed diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Prism.hs b/profunctor-optics/src/Data/Profunctor/Optic/Prism.hs index d98e267..f40cacd 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Prism.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Prism.hs @@ -17,7 +17,7 @@ module Data.Profunctor.Optic.Prism ( , clonePrism , coprism , coprism' - , rehandling + , cohandling , cloneCoprism -- * Optics , just @@ -132,8 +132,8 @@ coprism' tb bt = coprism tb $ \b -> maybe (Left b) Right (bt b) -- | Obtain a 'Coprism' from its free tensor representation. -- -rehandling :: (c + s -> a) -> (b -> c + t) -> Coprism s t a b -rehandling csa bct = unright . dimap csa bct +cohandling :: (c + s -> a) -> (b -> c + t) -> Coprism s t a b +cohandling csa bct = unright . dimap csa bct -- | TODO: Document -- diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Traversal.hs b/profunctor-optics/src/Data/Profunctor/Optic/Traversal.hs index b6850d1..d9dfff5 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Traversal.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Traversal.hs @@ -224,11 +224,12 @@ cotraversing sabt = corepn cotraverse . grate sabt -- | Obtain a 'Cotraversal' by embedding a reversed lens getter and setter into a 'Distributive' functor. -- -- @ --- 'withLens' ('re' o) 'cotraversing' ≡ 'cotraversed' . o +-- 'withColens' o 'retraversing' ≡ 'cotraversed' . o +-- 'withLens' ('re' o) $ 'flip' 'retraversing' ≡ 'cotraversed' . o -- @ -- -retraversing :: Distributive g => (b -> t) -> (b -> s -> a) -> Cotraversal (g s) (g t) a b -retraversing bsa bt = corepn cotraverse . (re $ lens bsa bt) +retraversing :: Distributive g => (b -> s -> a) -> (b -> t) -> Cotraversal (g s) (g t) a b +retraversing bsa bt = corepn cotraverse . colens bsa bt -- | Obtain a profunctor 'Cotraversal' from a Van Laarhoven 'Cotraversal'. -- @@ -316,11 +317,12 @@ cotraversing1 sabt = corepn cotraverse1 . grate sabt -- | Obtain a 'Cotraversal1' by embedding a reversed lens getter and setter into a 'Distributive1' functor. -- -- @ --- 'withLens' ('re' o) 'cotraversing' ≡ 'cotraversed' . o +-- 'withColens' o 'retraversing1' ≡ 'cotraversed1' . o +-- 'withLens' ('re' o) $ 'flip' 'retraversing1' ≡ 'cotraversed1' . o -- @ -- -retraversing1 :: Distributive1 g => (b -> t) -> (b -> s -> a) -> Cotraversal1 (g s) (g t) a b -retraversing1 bsa bt = corepn cotraverse1 . (re $ lens bsa bt) +retraversing1 :: Distributive1 g => (b -> s -> a) -> (b -> t) -> Cotraversal1 (g s) (g t) a b +retraversing1 bsa bt = corepn cotraverse1 . colens bsa bt -- | Obtain a profunctor 'Cotraversal1' from a Van Laarhoven 'Cotraversal1'. -- diff --git a/profunctor-optics/src/Data/Profunctor/Optic/Types.hs b/profunctor-optics/src/Data/Profunctor/Optic/Types.hs index 804c422..e9a41c9 100644 --- a/profunctor-optics/src/Data/Profunctor/Optic/Types.hs +++ b/profunctor-optics/src/Data/Profunctor/Optic/Types.hs @@ -158,7 +158,7 @@ type Grate s t a b = forall p. Closed p => Optic p s t a b type Lens' s a = Lens s s a a -type Colens' t b = Lens t t b b +type Colens' t b = Colens t t b b type Grate' s a = Grate s s a a diff --git a/profunctor-optics/test/doctest.hs b/profunctor-optics/test/doctest.hs index 1c9f7eb..37b358a 100644 --- a/profunctor-optics/test/doctest.hs +++ b/profunctor-optics/test/doctest.hs @@ -6,6 +6,7 @@ import Prelude (IO) main :: IO () main = doctest [ "-isrc" + , "src/Data/Profunctor/Optic/Module.hs" , "src/Data/Profunctor/Optic/Carrier.hs" , "src/Data/Profunctor/Optic/Combinator.hs" , "src/Data/Profunctor/Optic/Fold.hs"