git.iko.soy/semi-iso-optics
on master help sign in
Version 0.1.0.0.
commitbadcce21bcbe982f2f693915f9d699db4a16cd8f authorPaweł Nowak <pawel834@gmail.com> dateWed 29 Oct 2014, 11:17 (12 years ago)
7 files changed+357
.gitignore+47 Control/Lens/Internal/SemiIso.hs+39 Control/Lens/SemiIso.hs+123 Data/SemiIsoFunctor.hs+103 LICENSE+20 Setup.hs+2 semi-iso.cabal+23
.gitignorenew file+47
@@ -0,0 +1,47 @@
1+# Created by https://www.gitignore.io
2+
3+### Haskell ###
4+dist
5+cabal-dev
6+*.o
7+*.hi
8+*.chi
9+*.chs.h
10+.virtualenv
11+.hsenv
12+.cabal-sandbox/
13+cabal.sandbox.config
14+cabal.config
15+
16+
17+### Emacs ###
18+# -*- mode: gitignore; -*-
19+*~
20+\#*\#
21+/.emacs.desktop
22+/.emacs.desktop.lock
23+*.elc
24+auto-save-list
25+tramp
26+.\#*
27+
28+# Org-mode
29+.org-id-locations
30+*_archive
31+
32+# flymake-mode
33+*_flymake.*
34+
35+# eshell files
36+/eshell/history
37+/eshell/lastdir
38+
39+# elpa packages
40+/elpa/
41+
42+# reftex files
43+*.rel
44+
45+# AUCTeX auto folder
46+/auto/
47+
Control/Lens/Internal/SemiIso.hsnew file+39
@@ -0,0 +1,39 @@
1+{- |
2+Module : Data.Lens.Internal.SemiIso
3+Description : Internals of a SemiIso.
4+Copyright : (c) Paweł Nowak
5+License : MIT
6+
7+Maintainer : Paweł Nowak <pawel834@gmail.com>
8+Stability : experimental
9+-}
10+module Control.Lens.Internal.SemiIso where
11+
12+import Control.Monad
13+import Data.Profunctor
14+
15+-- | Type used internally to access 'SemiIso'.
16+--
17+-- Continues the naming tradition of @lens@.
18+data Barter s t a b = Barter (a -> Either String s) (t -> Either String b)
19+
20+instance Profunctor (Barter s t) where
21+ lmap f (Barter l r) = Barter (l . f) r
22+ rmap f (Barter l r) = Barter l (fmap f . r)
23+
24+instance Choice (Barter s t) where
25+ left' (Barter as st) = Barter
26+ (either as (\_ -> Left "partial iso failed")) (fmap Left . st)
27+ right' (Barter as st) = Barter
28+ (either (\_ -> Left "partial iso failed") as) (fmap Right . st)
29+
30+-- | Provides a profunctor the ability to fail with an error message.
31+--
32+-- This class could use some laws. It is certainly a bit ad-hoc.
33+class Profunctor p => Failure p where
34+ tie :: p a (Either String b) -> p a b
35+ attach :: p a b -> p (Either String a) b
36+
37+instance Failure (Barter s t) where
38+ tie (Barter f g) = Barter f (join . g)
39+ attach (Barter f g) = Barter (>>= f) g
Control/Lens/SemiIso.hsnew file+123
@@ -0,0 +1,123 @@
1+{-# LANGUAGE Rank2Types #-}
2+{-# LANGUAGE TupleSections #-}
3+{- |
4+Module : Data.Lens.SemiIso
5+Description : Semi-isomorphisms.
6+Copyright : (c) Paweł Nowak
7+License : MIT
8+
9+Maintainer : Paweł Nowak <pawel834@gmail.com>
10+Stability : experimental
11+
12+Semi-isomorphisms were motivated by reversible parsing/pretty printing. For example
13+we can map a number 12 to a string "12" (and the other way around). But the isomorphism
14+is partial - we cannot map the string "forty-two" to a number.
15+
16+Another example: when parsing a list of numbers like "12_53___42" we want to skip underscores
17+between numbers (and forget about them). During pretty printing we have to decide how many
18+underscores should we insert between numbers. Let's say we insert a single underscore. But now
19+@prettyPrint (parse "12_53___42") = "12_53_42"@ and not "12_53___42". We have to weaken
20+isomorphism laws to allow such semi-iso. Notice that
21+
22+> parse (prettyPrint (parse "12_53___42")) = parse "12_53___42"
23+> prettyPrint (parse (prettyPrint [12, 53, 42])) = prettyPrint [12, 53, 42]
24+
25+Our semi-isomorphisms will obey weakened laws:
26+
27+> apply i >=> unapply i >=> apply i = apply i
28+> unapply i >=> apply i >=> unapply i = unapply i
29+
30+When you see an "Either String a", the String is usually an error message.
31+
32+Disclaimer: the name "semi-isomorphism" is fictitious and made up for this library.
33+Any resemblance to known mathematical objects of the same name is purely coincidental.
34+-}
35+module Control.Lens.SemiIso (
36+ -- * Semi-isomorphism types.
37+ SemiIso,
38+ SemiIso',
39+ ASemiIso,
40+ ASemiIso',
41+
42+ -- * Constructing semi-isos.
43+ semiIso,
44+
45+ -- * Consuming semi-isos.
46+ withSemiIso,
47+ fromSemi,
48+ apply,
49+ unapply,
50+
51+ -- * Common semi-isomorphisms and isomorphisms.
52+ unit,
53+ swapped,
54+ associated,
55+ constant
56+ ) where
57+
58+import Control.Lens.Internal.SemiIso
59+import Control.Lens.Iso
60+import Data.Functor.Identity
61+import Data.Traversable
62+
63+-- | A semi-isomorphism is a partial isomorphism with weakened laws.
64+--
65+-- Should satisfy laws:
66+--
67+-- > apply i >=> unapply i >=> apply i = apply i
68+-- > unapply i >=> apply i >=> unapply i = unapply i
69+--
70+-- Every 'Prism' is a 'SemiIso'.
71+-- Every 'Iso' is a 'Prism'.
72+type SemiIso s t a b = forall p f. (Failure p, Traversable f) => p a (f b) -> p s (f t)
73+
74+-- | Non-polymorphic variant of 'SemiIso'.
75+type SemiIso' s a = SemiIso s s a a
76+
77+-- | When you see this as an argument to a function, it expects a 'SemiIso'.
78+type ASemiIso s t a b = Barter a b a (Identity b) -> Barter a b s (Identity t)
79+
80+-- | When you see this as an argument to a function, it expects a 'SemiIso''.
81+type ASemiIso' s a = ASemiIso s s a a
82+
83+-- | Constructs a semi isomorphism from a pair of functions that can
84+-- fail with an error message.
85+semiIso :: (s -> Either String a) -> (b -> Either String t) -> SemiIso s t a b
86+semiIso sa bt = tie . dimap sa (sequenceA . fmap bt) . attach
87+
88+-- | Extracts the two functions that characterize the 'SemiIso'.
89+withSemiIso :: ASemiIso s t a b
90+ -> ((s -> Either String a) -> (b -> Either String t) -> r)
91+ -> r
92+withSemiIso ai k = case ai (Barter Right (Right . Identity)) of
93+ Barter sa bt -> k sa (rmap (runIdentity . sequenceA) bt)
94+
95+-- | Applies the 'SemiIso'.
96+apply :: ASemiIso s t a b -> s -> Either String a
97+apply ai = withSemiIso ai $ \l _ -> l
98+
99+-- | Applies the 'SemiIso' in the opposite direction.
100+unapply :: ASemiIso s t a b -> b -> Either String t
101+unapply ai = withSemiIso ai $ \_ r -> r
102+
103+-- | Reverses a 'SemiIso'.
104+fromSemi :: ASemiIso s t a b -> SemiIso b a t s
105+fromSemi ai = withSemiIso ai $ \l r -> semiIso r l
106+
107+-- | A trivial isomorphism between a and (a, ()).
108+unit :: Iso' a (a, ())
109+unit = iso (, ()) fst
110+
111+-- | Products are associative.
112+associated :: Iso' (a, (b, c)) ((a, b), c)
113+associated = iso (\(a, (b, c)) -> ((a, b), c)) (\((a, b), c) -> (a, (b, c)))
114+
115+-- | \-> Always returns the argument.
116+--
117+-- \<- Filters out all values not equal to the argument.
118+constant :: Eq a => a -> SemiIso' () a
119+constant x = semiIso f g
120+ where
121+ f _ = Right x
122+ g y | x == y = Right ()
123+ | otherwise = Left "constant: not equal"
Data/SemiIsoFunctor.hsnew file+103
@@ -0,0 +1,103 @@
1+{- |
2+Module : Data.SemiIsoFunctor
3+Description : Functors from the category of semi-isomoprihsms to Hask.
4+Copyright : (c) Paweł Nowak
5+License : MIT
6+
7+Maintainer : Paweł Nowak <pawel834@gmail.com>
8+Stability : experimental
9+
10+Defines a functor from the category of semi-isomoprihsms to Hask.
11+
12+The most interesting property of that class is that it can be
13+instantiated by both covariant (like Parser) and contravariant (like Printer)
14+functors. Therefore it can be used as a common interface to unify
15+parsing and pretty printing.
16+
17+Operator names are up to bikeshedding :)
18+-}
19+module Data.SemiIsoFunctor where
20+
21+import Control.Lens.Cons
22+import Control.Lens.Empty
23+import Control.Lens.SemiIso
24+
25+infixl 3 /|/
26+infixl 4 /$/
27+infixl 5 /*/, /*, */
28+
29+-- | A functor from the category of semi-isomorphisms to Hask.
30+--
31+-- It is both covariant and contravariant in its single arugment.
32+--
33+-- The contravariant map is used by default to provide compatibility with
34+-- Prisms (otherwise you would have to reverse them in most cases).
35+--
36+-- Instances should satisfy laws:
37+--
38+-- > simap id = id
39+-- > simap (f . g) = simap g . simap f
40+-- > simap = simapCo . fromSemi
41+-- > simapCo = simap . fromSemi
42+class SemiIsoFunctor f where
43+ -- | The contravariant map.
44+ simap :: ASemiIso' a b -> f b -> f a
45+ simap = simapCo . fromSemi
46+
47+ -- | The covariant map.
48+ simapCo :: ASemiIso' a b -> f a -> f b
49+ simapCo = simap . fromSemi
50+
51+ {-# MINIMAL simap | simapCo #-}
52+
53+-- | A infix operator for 'simap'.
54+(/$/) :: SemiIsoFunctor f => ASemiIso' a b -> f b -> f a
55+(/$/) = simap
56+
57+-- | Equivalent of 'Applicative' for 'SemiIsoFunctor'.
58+--
59+-- However, this class implements uncurried application, unlike
60+-- 'Control.Applicative' which gives you curried application.
61+--
62+-- Instances should satisfy laws:
63+--
64+-- > TODO (they should be fine)
65+class SemiIsoFunctor f => SemiIsoApply f where
66+ sipure :: ASemiIso' a () -> f a
67+ (/*/) :: f a -> f b -> f (a, b)
68+
69+ (/*) :: f a -> f () -> f a
70+ f /* g = unit /$/ f /*/ g
71+
72+ (*/) :: f () -> f b -> f b
73+ f */ g = unit . swapped /$/ f /*/ g
74+
75+ {-# MINIMAL sipure, (/*/) #-}
76+
77+-- | Equivalent of 'Alternative' for 'SemiIsoFunctor'.
78+--
79+-- @f a@ should form a monoid with identity 'siempty' and binary
80+-- operation '/|/'.
81+class SemiIsoApply f => SemiIsoAlternative f where
82+ siempty :: f a
83+ (/|/) :: f a -> f a -> f a
84+
85+ sisome :: f a -> f [a]
86+ sisome v = _Cons /$/ v /*/ simany v
87+
88+ simany :: f a -> f [a]
89+ simany v = sisome v /|/ sipure _Empty
90+
91+ {-# MINIMAL siempty, (/|/) #-}
92+
93+-- | Equivalent of 'sequence'.
94+--
95+-- Note that it is not possible to write sequence_, because
96+-- you cannot void a SemiIsoFunctor.
97+sisequence :: SemiIsoApply f => [f a] -> f [a]
98+sisequence [] = sipure _Empty
99+sisequence (x:xs) = _Cons /$/ x /*/ sisequence xs
100+
101+-- | Equivalent of 'replicateM'.
102+sireplicate :: SemiIsoApply f => Int -> f a -> f [a]
103+sireplicate n f = sisequence (replicate n f)
LICENSEnew file+20
@@ -0,0 +1,20 @@
1+Copyright (c) 2014 Paweł Nowak
2+
3+Permission is hereby granted, free of charge, to any person obtaining
4+a copy of this software and associated documentation files (the
5+"Software"), to deal in the Software without restriction, including
6+without limitation the rights to use, copy, modify, merge, publish,
7+distribute, sublicense, and/or sell copies of the Software, and to
8+permit persons to whom the Software is furnished to do so, subject to
9+the following conditions:
10+
11+The above copyright notice and this permission notice shall be included
12+in all copies or substantial portions of the Software.
13+
14+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Setup.hsnew file+2
@@ -0,0 +1,2 @@
1+import Distribution.Simple
2+main = defaultMain
semi-iso.cabalnew file+23
@@ -0,0 +1,23 @@
1+name: semi-iso
2+version: 0.1.0.0
3+synopsis: Weakened partial isomorphisms that work with lenses.
4+description: Semi-isomorphisms are partial isomorphisms with weakened iso laws.
5+ And they work with Iso and Prism from @lens@!
6+ .
7+ See first "Control.Lens.SemiIso" for semi-isomoprhisms.
8+ After that look at "Data.SemiIsoFunctor".
9+license: MIT
10+license-file: LICENSE
11+author: Paweł Nowak
12+maintainer: Paweł Nowak <pawel834@gmail.com>
13+copyright: Paweł Nowak 2014
14+category: Control, Data
15+build-type: Simple
16+cabal-version: >=1.10
17+
18+library
19+ exposed-modules: Control.Lens.SemiIso
20+ Control.Lens.Internal.SemiIso
21+ Data.SemiIsoFunctor
22+ build-depends: base >=4.7 && <4.8, profunctors, transformers, lens
23+ default-language: Haskell2010