@@ -1,5 +1,7 @@
1{-# LANGUAGE Rank2Types #-}
1{-# LANGUAGE Rank2Types #-}
2{-# LANGUAGE TupleSections #-}
2{-# LANGUAGE TupleSections #-}
3+{-# LANGUAGE ViewPatterns #-}
4+{-# LANGUAGE PatternSynonyms #-}
3{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleContexts #-}
5Module : Control.Lens.SemiIso
7Module : Control.Lens.SemiIso
@@ -39,24 +41,19 @@ module Control.Lens.SemiIso (
41 ASemiIso',
43 ASemiIso',
43 -- * Constructing semi-isos.
48 -- * Constructing semi-isos.
46− -- * Transforming semi-isos.
55 -- * Consuming semi-isos.
51 -- * Consuming semi-isos.
60 -- * Common semi-isomorphisms and isomorphisms.
57 -- * Common semi-isomorphisms and isomorphisms.
@@ -64,6 +61,22 @@ module Control.Lens.SemiIso (
64+ -- * Semi-isos for numbers.
68+ -- * Transforming semi-isos.
67 -- * Folds.
80 -- * Folds.
@@ -79,6 +92,7 @@ module Control.Lens.SemiIso (
82import Control.Lens.Internal.SemiIso
96import Control.Lens.Internal.SemiIso
83import Control.Lens.Iso
97import Control.Lens.Iso
84import Data.Foldable
98import Data.Foldable
@@ -95,7 +109,8 @@ import Data.Traversable
96-- Every 'Prism' is a 'SemiIso'.
110-- Every 'Prism' is a 'SemiIso'.
97-- Every 'Iso' is a 'Prism'.
111-- Every 'Iso' is a 'Prism'.
98−type SemiIso s t a b = forall p f. (Exposed (Either String) p, Traversable f) => p a (f b) -> p s (f t)
112+type SemiIso s t a b = forall p f. (Exposed (Either String) p, Traversable f)
113+ => p a (f b) -> p s (f t)
100-- | Non-polymorphic variant of 'SemiIso'.
115-- | Non-polymorphic variant of 'SemiIso'.
101type SemiIso' s a = SemiIso s s a a
116type SemiIso' s a = SemiIso s s a a
@@ -106,11 +121,23 @@ type ASemiIso s t a b = Retail a b a (Identity b) -> Retail a b s (Identity t)
106-- | When you see this as an argument to a function, it expects a 'SemiIso''.
121-- | When you see this as an argument to a function, it expects a 'SemiIso''.
107type ASemiIso' s a = ASemiIso s s a a
122type ASemiIso' s a = ASemiIso s s a a
124+-- | A nice pattern synonym for SemiIso's. Gives you the two functions, just like
125+-- 'viewSemiIso' or 'fromSemiIso'.
126+pattern SemiIso sa bt <- (viewSemiIso -> (sa, bt))
109-- | Constructs a semi isomorphism from a pair of functions that can
128-- | Constructs a semi isomorphism from a pair of functions that can
110-- fail with an error message.
129-- fail with an error message.
111semiIso :: (s -> Either String a) -> (b -> Either String t) -> SemiIso s t a b
130semiIso :: (s -> Either String a) -> (b -> Either String t) -> SemiIso s t a b
112semiIso sa bt = merge . dimap sa (sequenceA . fmap bt) . expose
131semiIso sa bt = merge . dimap sa (sequenceA . fmap bt) . expose
133+-- | Applies the 'SemiIso'.
134+apply :: ASemiIso s t a b -> s -> Either String a
135+apply (SemiIso sa _) = sa
137+-- | Applies the 'SemiIso' in the opposite direction.
138+unapply :: ASemiIso s t a b -> b -> Either String t
139+unapply (SemiIso _ bt) = bt
114-- | Extracts the two functions that characterize the 'SemiIso'.
141-- | Extracts the two functions that characterize the 'SemiIso'.
115withSemiIso :: ASemiIso s t a b
142withSemiIso :: ASemiIso s t a b
116 -> ((s -> Either String a) -> (b -> Either String t) -> r)
143 -> ((s -> Either String a) -> (b -> Either String t) -> r)
@@ -118,49 +145,9 @@ withSemiIso :: ASemiIso s t a b
118withSemiIso ai k = case ai (Retail Right (Right . Identity)) of
145withSemiIso ai k = case ai (Retail Right (Right . Identity)) of
119 Retail sa bt -> k sa (rmap (runIdentity . sequenceA) bt)
146 Retail sa bt -> k sa (rmap (runIdentity . sequenceA) bt)
121−-- | Transforms the semi-iso so that applying it in both directions never fails,
148+-- | Extracts the two functions that characterize the 'SemiIso'.
122−-- but instead catches any errors and returns them as an @Either String a@.
123−attempt :: ASemiIso s t a b -> SemiIso s (Either String t) (Either String a) b
124−attempt = attemptAp . attemptUn
126−-- | Transforms the semi-iso so that applying it in direction (->) never fails,
127−-- but instead catches any errors and returns them as an @Either String a@.
128−attemptAp :: ASemiIso s t a b -> SemiIso s t (Either String a) b
129−attemptAp ai = withSemiIso ai $ \l r -> semiIso (Right . l) r
131−-- | Transforms the semi-iso so that applying it in direction (<-) never fails,
132−-- but instead catches any errors and returns them as an @Either String a@.
133−attemptUn :: ASemiIso s t a b -> SemiIso s (Either String t) a b
134−attemptUn ai = withSemiIso ai $ \l r -> semiIso l (Right . r)
136−discard :: Either a b -> Maybe b
137−discard = either (const Nothing) Just
139−-- | Transforms the semi-iso like 'attempt', but ignores the error message.
140−attempt_ :: ASemiIso s t a b -> SemiIso s (Maybe t) (Maybe a) b
141−attempt_ ai = rmap (fmap discard) . attempt ai . lmap discard
143−-- | Transforms the semi-iso like 'attemptAp', but ignores the error message.
145−-- Very useful when you want to bifold using a prism.
146−attemptAp_ :: ASemiIso s t a b -> SemiIso s t (Maybe a) b
147−attemptAp_ ai = attemptAp ai . lmap discard
149−-- | Transforms the semi-iso like 'attemptUn', but ignores the error message.
150−attemptUn_ :: ASemiIso s t a b -> SemiIso s (Maybe t) a b
151−attemptUn_ ai = rmap (fmap discard) . attemptUn ai
153−-- | Applies the 'SemiIso'.
154−apply :: ASemiIso s t a b -> s -> Either String a
149+viewSemiIso :: ASemiIso s t a b -> (s -> Either String a, b -> Either String t)
155−apply ai = withSemiIso ai $ \l _ -> l
157−-- | Applies the 'SemiIso' in the opposite direction.
158−unapply :: ASemiIso s t a b -> b -> Either String t
159−unapply ai = withSemiIso ai $ \_ r -> r
150+viewSemiIso ai = withSemiIso ai (,)
161−-- | Reverses a 'SemiIso'.
162−fromSemi :: ASemiIso s t a b -> SemiIso b a t s
163−fromSemi ai = withSemiIso ai $ \l r -> semiIso r l
165-- | A trivial isomorphism between a and (a, ()).
152-- | A trivial isomorphism between a and (a, ()).
166unit :: Iso' a (a, ())
153unit :: Iso' a (a, ())
@@ -192,6 +179,65 @@ exact x = semiIso f g
192 g y | x == y = Right ()
179 g y | x == y = Right ()
193 | otherwise = Left "exact: not equal"
180 | otherwise = Left "exact: not equal"
182+_Negative :: Num a => SemiIso' a a
183+_Negative = undefined
185+_Positive :: Num a => SemiIso' a a
186+_Positive = undefined
188+-- | Reverses a 'SemiIso'.
189+rev :: ASemiIso s t a b -> SemiIso b a t s
190+rev ai = withSemiIso ai $ \l r -> semiIso r l
192+-- | A product of SemiIso's.
193+prod :: ASemiIso s t a b -> ASemiIso s' t' a' b'
194+ -> SemiIso (s, s') (t, t') (a, a') (b, b')
195+prod (SemiIso sa bt) (SemiIso sa' bt') = semiIso
196+ (runKleisli (Kleisli sa *** Kleisli sa'))
197+ (runKleisli (Kleisli bt *** Kleisli bt'))
199+-- | In the non-polymorphic case uses an @SemiIso a ()@ to construct a
200+-- @SemiIso (a, b) b@, i.e. eliminates the first pair element.
201+elimFirst :: ASemiIso s' t' () () -> SemiIso (s', t) (t', t) t t
202+elimFirst ai = swapped . elimSecond ai
204+-- | In the non-polymorphic case uses an @SemiIso b ()@ to construct a
205+-- @SemiIso (a, b) a@, i.e. eliminates the second pair element.
206+elimSecond :: ASemiIso s' t' () () -> SemiIso (t, s') (t, t') t t
207+elimSecond ai = prod id ai . rev unit
209+-- | Transforms the semi-iso so that applying it in both directions never fails,
210+-- but instead catches any errors and returns them as an @Either String a@.
211+attempt :: ASemiIso s t a b -> SemiIso s (Either String t) (Either String a) b
212+attempt = attemptAp . attemptUn
214+-- | Transforms the semi-iso so that applying it in direction (->) never fails,
215+-- but instead catches any errors and returns them as an @Either String a@.
216+attemptAp :: ASemiIso s t a b -> SemiIso s t (Either String a) b
217+attemptAp (SemiIso sa bt) = semiIso (Right . sa) bt
219+-- | Transforms the semi-iso so that applying it in direction (<-) never fails,
220+-- but instead catches any errors and returns them as an @Either String a@.
221+attemptUn :: ASemiIso s t a b -> SemiIso s (Either String t) a b
222+attemptUn (SemiIso sa bt) = semiIso sa (Right . bt)
224+discard :: Either a b -> Maybe b
225+discard = either (const Nothing) Just
227+-- | Transforms the semi-iso like 'attempt', but ignores the error message.
228+attempt_ :: ASemiIso s t a b -> SemiIso s (Maybe t) (Maybe a) b
229+attempt_ ai = rmap (fmap discard) . attempt ai . lmap discard
231+-- | Transforms the semi-iso like 'attemptAp', but ignores the error message.
233+-- Very useful when you want to bifold using a prism.
234+attemptAp_ :: ASemiIso s t a b -> SemiIso s t (Maybe a) b
235+attemptAp_ ai = attemptAp ai . lmap discard
237+-- | Transforms the semi-iso like 'attemptUn', but ignores the error message.
238+attemptUn_ :: ASemiIso s t a b -> SemiIso s (Maybe t) a b
239+attemptUn_ ai = rmap (fmap discard) . attemptUn ai
195-- | Monadic counterpart of 'foldl1' (or non-empty list counterpart of 'foldlM').
241-- | Monadic counterpart of 'foldl1' (or non-empty list counterpart of 'foldlM').
196foldlM1 :: Monad m => (a -> a -> m a) -> [a] -> m a
242foldlM1 :: Monad m => (a -> a -> m a) -> [a] -> m a
197foldlM1 f (x:xs) = foldlM f x xs
243foldlM1 f (x:xs) = foldlM f x xs