packages feed

semi-iso (empty) → 0.1.0.0

raw patch · 6 files changed

+310/−0 lines, 6 filesdep +basedep +lensdep +profunctorssetup-changed

Dependencies added: base, lens, profunctors, transformers

Files

+ Control/Lens/Internal/SemiIso.hs view
@@ -0,0 +1,39 @@+{- |+Module      :  Data.Lens.Internal.SemiIso+Description :  Internals of a SemiIso.+Copyright   :  (c) Paweł Nowak+License     :  MIT++Maintainer  :  Paweł Nowak <pawel834@gmail.com>+Stability   :  experimental+-}+module Control.Lens.Internal.SemiIso where++import Control.Monad+import Data.Profunctor++-- | Type used internally to access 'SemiIso'.+--+-- Continues the naming tradition of @lens@.+data Barter s t a b = Barter (a -> Either String s) (t -> Either String b)++instance Profunctor (Barter s t) where+    lmap f (Barter l r) = Barter (l . f) r+    rmap f (Barter l r) = Barter l (fmap f . r)++instance Choice (Barter s t) where+    left' (Barter as st) = Barter +        (either as (\_ -> Left "partial iso failed")) (fmap Left . st)+    right' (Barter as st) = Barter +        (either (\_ -> Left "partial iso failed") as) (fmap Right . st)++-- | Provides a profunctor the ability to fail with an error message.+--+-- This class could use some laws. It is certainly a bit ad-hoc.+class Profunctor p => Failure p where+    tie :: p a (Either String b) -> p a b+    attach :: p a b -> p (Either String a) b++instance Failure (Barter s t) where+    tie (Barter f g) = Barter f (join . g)+    attach (Barter f g) = Barter (>>= f) g
+ Control/Lens/SemiIso.hs view
@@ -0,0 +1,123 @@+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TupleSections #-}+{- |+Module      :  Data.Lens.SemiIso+Description :  Semi-isomorphisms.+Copyright   :  (c) Paweł Nowak+License     :  MIT++Maintainer  :  Paweł Nowak <pawel834@gmail.com>+Stability   :  experimental++Semi-isomorphisms were motivated by reversible parsing/pretty printing. For example+we can map a number 12 to a string "12" (and the other way around). But the isomorphism+is partial - we cannot map the string "forty-two" to a number.++Another example: when parsing a list of numbers like "12_53___42" we want to skip underscores+between numbers (and forget about them). During pretty printing we have to decide how many+underscores should we insert between numbers. Let's say we insert a single underscore. But now+@prettyPrint (parse "12_53___42") = "12_53_42"@ and not "12_53___42". We have to weaken+isomorphism laws to allow such semi-iso. Notice that++> parse (prettyPrint (parse "12_53___42"))       = parse "12_53___42"+> prettyPrint (parse (prettyPrint [12, 53, 42])) = prettyPrint [12, 53, 42]++Our semi-isomorphisms will obey weakened laws:++> apply i   >=> unapply i >=> apply i   = apply i+> unapply i >=> apply i   >=> unapply i = unapply i++When you see an "Either String a", the String is usually an error message.++Disclaimer: the name "semi-isomorphism" is fictitious and made up for this library. +Any resemblance to known mathematical objects of the same name is purely coincidental.+-}+module Control.Lens.SemiIso (+    -- * Semi-isomorphism types.+    SemiIso,+    SemiIso',+    ASemiIso,+    ASemiIso',+    +    -- * Constructing semi-isos.+    semiIso,++    -- * Consuming semi-isos.+    withSemiIso,+    fromSemi,+    apply,+    unapply,+    +    -- * Common semi-isomorphisms and isomorphisms.+    unit,+    swapped,+    associated,+    constant+    ) where++import Control.Lens.Internal.SemiIso+import Control.Lens.Iso+import Data.Functor.Identity+import Data.Traversable++-- | A semi-isomorphism is a partial isomorphism with weakened laws.+-- +-- Should satisfy laws:+-- +-- > apply i   >=> unapply i >=> apply i   = apply i+-- > unapply i >=> apply i   >=> unapply i = unapply i+--+-- Every 'Prism' is a 'SemiIso'.+-- Every 'Iso' is a 'Prism'.+type SemiIso s t a b = forall p f. (Failure p, Traversable f) => p a (f b) -> p s (f t)++-- | Non-polymorphic variant of 'SemiIso'.+type SemiIso' s a = SemiIso s s a a++-- | When you see this as an argument to a function, it expects a 'SemiIso'.+type ASemiIso s t a b = Barter a b a (Identity b) -> Barter a b s (Identity t)++-- | When you see this as an argument to a function, it expects a 'SemiIso''.+type ASemiIso' s a = ASemiIso s s a a++-- | Constructs a semi isomorphism from a pair of functions that can+-- fail with an error message.+semiIso :: (s -> Either String a) -> (b -> Either String t) -> SemiIso s t a b+semiIso sa bt = tie . dimap sa (sequenceA . fmap bt) . attach++-- | Extracts the two functions that characterize the 'SemiIso'.+withSemiIso :: ASemiIso s t a b +            -> ((s -> Either String a) -> (b -> Either String t) -> r) +            -> r+withSemiIso ai k = case ai (Barter Right (Right . Identity)) of+                        Barter sa bt -> k sa (rmap (runIdentity . sequenceA) bt)++-- | Applies the 'SemiIso'.+apply :: ASemiIso s t a b -> s -> Either String a+apply ai = withSemiIso ai $ \l _ -> l++-- | Applies the 'SemiIso' in the opposite direction.+unapply :: ASemiIso s t a b -> b -> Either String t+unapply ai = withSemiIso ai $ \_ r -> r++-- | Reverses a 'SemiIso'.+fromSemi :: ASemiIso s t a b -> SemiIso b a t s+fromSemi ai = withSemiIso ai $ \l r -> semiIso r l++-- | A trivial isomorphism between a and (a, ()).+unit :: Iso' a (a, ())+unit = iso (, ()) fst++-- | Products are associative.+associated :: Iso' (a, (b, c)) ((a, b), c)+associated = iso (\(a, (b, c)) -> ((a, b), c)) (\((a, b), c) -> (a, (b, c)))++-- | \-> Always returns the argument.+--+-- \<- Filters out all values not equal to the argument.+constant :: Eq a => a -> SemiIso' () a+constant x = semiIso f g+  where+    f _ = Right x+    g y | x == y    = Right ()+        | otherwise = Left "constant: not equal"
+ Data/SemiIsoFunctor.hs view
@@ -0,0 +1,103 @@+{- |+Module      :  Data.SemiIsoFunctor+Description :  Functors from the category of semi-isomoprihsms to Hask.+Copyright   :  (c) Paweł Nowak+License     :  MIT++Maintainer  :  Paweł Nowak <pawel834@gmail.com>+Stability   :  experimental++Defines a functor from the category of semi-isomoprihsms to Hask.++The most interesting property of that class is that it can be+instantiated by both covariant (like Parser) and contravariant (like Printer) +functors. Therefore it can be used as a common interface to unify+parsing and pretty printing.++Operator names are up to bikeshedding :)+-}+module Data.SemiIsoFunctor where++import Control.Lens.Cons+import Control.Lens.Empty+import Control.Lens.SemiIso++infixl 3 /|/+infixl 4 /$/+infixl 5 /*/, /*, */++-- | A functor from the category of semi-isomorphisms to Hask.+--+-- It is both covariant and contravariant in its single arugment.+--+-- The contravariant map is used by default to provide compatibility with +-- Prisms (otherwise you would have to reverse them in most cases).+--+-- Instances should satisfy laws:+-- +-- > simap id      = id+-- > simap (f . g) = simap g . simap f+-- > simap         = simapCo . fromSemi+-- > simapCo       = simap   . fromSemi+class SemiIsoFunctor f where+    -- | The contravariant map.+    simap :: ASemiIso' a b -> f b -> f a+    simap = simapCo . fromSemi+    +    -- | The covariant map.+    simapCo :: ASemiIso' a b -> f a -> f b+    simapCo = simap . fromSemi+    +    {-# MINIMAL simap | simapCo #-}++-- | A infix operator for 'simap'.+(/$/) :: SemiIsoFunctor f => ASemiIso' a b -> f b -> f a+(/$/) = simap++-- | Equivalent of 'Applicative' for 'SemiIsoFunctor'.+--+-- However, this class implements uncurried application, unlike +-- 'Control.Applicative' which gives you curried application.+--+-- Instances should satisfy laws:+-- +-- > TODO (they should be fine)+class SemiIsoFunctor f => SemiIsoApply f where+    sipure :: ASemiIso' a () -> f a+    (/*/) :: f a -> f b -> f (a, b)++    (/*)  :: f a -> f () -> f a+    f /* g = unit /$/ f /*/ g++    (*/)  :: f () -> f b -> f b+    f */ g = unit . swapped /$/ f /*/ g++    {-# MINIMAL sipure, (/*/) #-}++-- | Equivalent of 'Alternative' for 'SemiIsoFunctor'.+--+-- @f a@ should form a monoid with identity 'siempty' and binary+-- operation '/|/'.+class SemiIsoApply f => SemiIsoAlternative f where+    siempty :: f a+    (/|/) :: f a -> f a -> f a++    sisome :: f a -> f [a]+    sisome v = _Cons /$/ v /*/ simany v++    simany :: f a -> f [a]+    simany v = sisome v /|/ sipure _Empty++    {-# MINIMAL siempty, (/|/) #-}++-- | Equivalent of 'sequence'.+--+-- Note that it is not possible to write sequence_, because+-- you cannot void a SemiIsoFunctor.+sisequence :: SemiIsoApply f => [f a] -> f [a]+sisequence [] = sipure _Empty+sisequence (x:xs) = _Cons /$/ x /*/ sisequence xs++-- | Equivalent of 'replicateM'.+sireplicate :: SemiIsoApply f => Int -> f a -> f [a]+sireplicate n f = sisequence (replicate n f)
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2014 Paweł Nowak++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ semi-iso.cabal view
@@ -0,0 +1,23 @@+name:                semi-iso+version:             0.1.0.0+synopsis:            Weakened partial isomorphisms that work with lenses.+description:         Semi-isomorphisms are partial isomorphisms with weakened iso laws.+                     And they work with Iso and Prism from @lens@!+                     .+                     See first "Control.Lens.SemiIso" for semi-isomoprhisms.+                     After that look at "Data.SemiIsoFunctor".+license:             MIT+license-file:        LICENSE+author:              Paweł Nowak+maintainer:          Paweł Nowak <pawel834@gmail.com>+copyright:           Paweł Nowak 2014+category:            Control, Data+build-type:          Simple+cabal-version:       >=1.10++library+  exposed-modules:     Control.Lens.SemiIso+                       Control.Lens.Internal.SemiIso+                       Data.SemiIsoFunctor+  build-depends:       base >=4.7 && <4.8, profunctors, transformers, lens+  default-language:    Haskell2010