lawful-conversions 0.1.6 → 0.1.6.1
raw patch · 5 files changed
+153/−170 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- lawful-conversions.cabal +9/−6
- src/library/LawfulConversions/Classes.hs +144/−5
- src/library/LawfulConversions/Classes/Is.hs +0/−60
- src/library/LawfulConversions/Classes/IsMany.hs +0/−48
- src/library/LawfulConversions/Classes/IsSome.hs +0/−51
lawful-conversions.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: lawful-conversions-version: 0.1.6+version: 0.1.6.1 synopsis: Lawful typeclasses for bidirectional conversion between types category: Conversion homepage: https://github.com/nikita-volkov/lawful-conversions@@ -52,11 +52,17 @@ = Prior work and acknowledgements - This library is a direct successor of the "[isomorphism-class](https://hackage.haskell.org/package/isomorphism-class)" library, expanding upon the patterns discovered there. It also shares some ideas with "[control-iso](https://hackage.haskell.org/package/control-iso)" and "[type-iso](https://hackage.haskell.org/package/type-iso)".+ This library is an offspring of the "[isomorphism-class](https://hackage.haskell.org/package/isomorphism-class)" library, expanding upon the patterns discovered there. Both libraries are maintained letting their designs compete. + Some ideas and concepts are also shared with the following libraries:++ - [control-iso](https://hackage.haskell.org/package/control-iso)+ - [type-iso](https://hackage.haskell.org/package/type-iso)+ - [injections](https://hackage.haskell.org/package/injections)+ source-repository head type: git- location: git://github.com/nikita-volkov/lawful-conversions.git+ location: https://github.com/nikita-volkov/lawful-conversions common base default-language: Haskell2010@@ -78,9 +84,6 @@ exposed-modules: LawfulConversions other-modules: LawfulConversions.Classes- LawfulConversions.Classes.Is- LawfulConversions.Classes.IsMany- LawfulConversions.Classes.IsSome LawfulConversions.Optics LawfulConversions.Prelude LawfulConversions.Properties
src/library/LawfulConversions/Classes.hs view
@@ -1,6 +1,145 @@-module LawfulConversions.Classes- ( module LawfulConversions.Classes.Is,- )-where+module LawfulConversions.Classes where -import LawfulConversions.Classes.Is+import LawfulConversions.Prelude++-- |+-- Evidence that all values of type @b@ form a subset of all values of type @a@.+--+-- [From Wikipedia](https://en.wikipedia.org/wiki/Subset):+--+-- In mathematics, a set A is a subset of a set B if all elements of A are also elements of B; B is then a superset of A. It is possible for A and B to be equal; if they are unequal, then A is a proper subset of B. The relationship of one set being a subset of another is called inclusion (or sometimes containment). A is a subset of B may also be expressed as B includes (or contains) A or A is included (or contained) in B. A k-subset is a subset with k elements.+--+-- === Laws+--+-- ==== 'to' is [injective](https://en.wikipedia.org/wiki/Injective_function)+--+-- For every two values of type @b@ that are not equal converting with 'to' produces values that are not equal as well:+--+-- > \(b1, b2) -> b1 == b2 || to @a b1 /= to @a b2+--+-- ==== 'maybeFrom' is a [partial inverse](https://en.wikipedia.org/wiki/Inverse_function#Partial_inverses) of 'to'+--+-- For all values of @b@ converting to @a@ and then attempting to convert back to @b@ always succeeds and produces a value that is equal to the original:+--+-- > \b -> maybeFrom (to @a b) == Just b+--+-- === Testing+--+-- For testing whether your instances conform to these laws use 'LawfulConversions.isSomeProperties'.+class IsSome a b where+ -- |+ -- Convert a value of a subset type to a superset type.+ to :: b -> a++ -- |+ -- [Partial inverse](https://en.wikipedia.org/wiki/Inverse_function#Partial_inverses) of 'to'.+ maybeFrom :: a -> Maybe b++ -- |+ -- Requires the presence of 'IsSome' in reverse direction.+ default maybeFrom :: (IsSome b a) => a -> Maybe b+ maybeFrom = Just . to++-- | Every type is isomorphic to itself.+instance IsSome a a where+ to = id+ maybeFrom = Just . id++-- | The empty set has no elements, and therefore is vacuously a subset of any set.+instance IsSome a Void where+ to = absurd+ maybeFrom = const Nothing++-- |+-- Lossy or canonicalizing conversion.+-- Captures mappings from multiple alternative inputs into one output.+--+-- E.g.,+--+-- - `ByteString` can be decoded into `Text` with UTF-8 leniently, replacing the invalid chars with a default char.+--+-- - `String` has a wider range of supported chars than `Text`, so some chars get replaced too.+--+-- === Laws+--+-- ==== 'from' is an [inverse](https://en.wikipedia.org/wiki/Inverse_function) of 'to'+--+-- > \b -> b == from (to @a b)+--+-- === Testing+--+-- For testing whether your instances conform to these laws use 'LawfulConversions.isManyProperties'.+class (IsSome a b) => IsMany a b where+ -- |+ -- Possibly lossy inverse of 'to'.+ -- [Surjection](https://en.wikipedia.org/wiki/Surjective_function) from @a@ to @b@.+ --+ -- Particularly useful in combination with the @TypeApplications@ extension,+ -- where it allows to specify the input type, e.g.:+ --+ -- > fromText :: IsMany Text b => Text -> b+ -- > fromText = from @Text+ --+ -- The first type application of the 'to' function on the other hand specifies+ -- the output data type.+ from :: a -> b++ -- |+ -- Requires the presence of 'IsSome' in reverse direction.+ default from :: (IsSome b a) => a -> b+ from = to++instance IsMany a a++-- | Bidirectional conversion between two types with no loss of information.+--+-- The bidirectionality is encoded via a recursive dependency with arguments+-- flipped.+--+-- You can read the signature @Is a b@ as \"/B/ is /A/\".+--+-- === Laws+--+-- ==== 'from' is an [inverse](https://en.wikipedia.org/wiki/Inverse_function) of 'to'+--+-- For all values of /b/ converting from /b/ to /a/ and then converting from /a/ to /b/ produces the original value:+--+-- > \b -> b == from (to @a b)+--+-- ==== 'to' is an [inverse](https://en.wikipedia.org/wiki/Inverse_function) of 'from'+--+-- For all values of /a/ converting from /a/ to /b/ and then converting from /b/ to /a/ produces the original value:+--+-- > \a -> a == to (from @a @b a)+--+-- === Testing+--+-- For testing whether your instances conform to these laws use 'LawfulConversions.isProperties'.+--+-- === Instance Definition+--+-- For each pair of isomorphic types (/A/ and /B/) the compiler will require you to define six instances, namely: @Is A B@ and @Is B A@, @IsMany A B@ and @IsMany B A@, @IsSome A B@ and @IsSome B A@.+--+-- Instances of @Is@ do not define any functions and serve merely as a statement that the laws are satisfied.+--+-- ==== Example: Lazy Text and Text+--+-- @+-- instance IsSome "Data.Text.Lazy.LazyText" "Data.Text.Text" where+-- to = LazyText.'Data.Text.Lazy.fromStrict'+--+-- instance IsSome "Data.Text.Text" "Data.Text.Lazy.LazyText" where+-- to = LazyText.'Data.Text.Lazy.toStrict'+--+-- instance IsMany "Data.Text.Lazy.LazyText" "Data.Text.Text"+--+-- instance IsMany "Data.Text.Text" "Data.Text.Lazy.LazyText"+--+-- instance Is "Data.Text.Lazy.LazyText" "Data.Text.Text"+--+-- instance Is "Data.Text.Text" "Data.Text.Lazy.LazyText"+-- @+class (IsMany a b, Is b a) => Is a b++-- | Any type is isomorphic to itself.+instance Is a a
− src/library/LawfulConversions/Classes/Is.hs
@@ -1,60 +0,0 @@-module LawfulConversions.Classes.Is- ( module LawfulConversions.Classes.Is,- module LawfulConversions.Classes.IsMany,- )-where--import LawfulConversions.Classes.IsMany---- | Bidirectional conversion between two types with no loss of information.------ The bidirectionality is encoded via a recursive dependency with arguments--- flipped.------ You can read the signature @Is a b@ as \"/B/ is /A/\".------ === Laws------ ==== 'from' is an [inverse](https://en.wikipedia.org/wiki/Inverse_function) of 'to'------ For all values of /b/ converting from /b/ to /a/ and then converting from /a/ to /b/ produces the original value:------ > \b -> b == from (to @a b)------ ==== 'to' is an [inverse](https://en.wikipedia.org/wiki/Inverse_function) of 'from'------ For all values of /a/ converting from /a/ to /b/ and then converting from /b/ to /a/ produces the original value:------ > \a -> a == to (from @a @b a)------ === Testing------ For testing whether your instances conform to these laws use 'LawfulConversions.isProperties'.------ === Instance Definition------ For each pair of isomorphic types (/A/ and /B/) the compiler will require you to define six instances, namely: @Is A B@ and @Is B A@, @IsMany A B@ and @IsMany B A@, @IsSome A B@ and @IsSome B A@.------ Instances of @Is@ do not define any functions and serve merely as a statement that the laws are satisfied.------ ==== Example: Lazy Text and Text------ @--- instance IsSome "Data.Text.Lazy.LazyText" "Data.Text.Text" where--- to = LazyText.'Data.Text.Lazy.fromStrict'------ instance IsSome "Data.Text.Text" "Data.Text.Lazy.LazyText" where--- to = LazyText.'Data.Text.Lazy.toStrict'------ instance IsMany "Data.Text.Lazy.LazyText" "Data.Text.Text"------ instance IsMany "Data.Text.Text" "Data.Text.Lazy.LazyText"------ instance Is "Data.Text.Lazy.LazyText" "Data.Text.Text"------ instance Is "Data.Text.Text" "Data.Text.Lazy.LazyText"--- @-class (IsMany a b, Is b a) => Is a b---- | Any type is isomorphic to itself.-instance Is a a
− src/library/LawfulConversions/Classes/IsMany.hs
@@ -1,48 +0,0 @@-module LawfulConversions.Classes.IsMany- ( module LawfulConversions.Classes.IsMany,- module LawfulConversions.Classes.IsSome,- )-where--import LawfulConversions.Classes.IsSome---- |--- Lossy or canonicalizing conversion.--- Captures mappings from multiple alternative inputs into one output.------ E.g.,------ - `ByteString` can be decoded into `Text` with UTF-8 leniently, replacing the invalid chars with a default char.------ - `String` has a wider range of supported chars than `Text`, so some chars get replaced too.------ === Laws------ ==== 'from' is an [inverse](https://en.wikipedia.org/wiki/Inverse_function) of 'to'------ > \b -> b == from (to @a b)------ === Testing------ For testing whether your instances conform to these laws use 'LawfulConversions.isManyProperties'.-class (IsSome a b) => IsMany a b where- -- |- -- Possibly lossy inverse of 'to'.- -- [Surjection](https://en.wikipedia.org/wiki/Surjective_function) from @a@ to @b@.- --- -- Particularly useful in combination with the @TypeApplications@ extension,- -- where it allows to specify the input type, e.g.:- --- -- > fromText :: IsMany Text b => Text -> b- -- > fromText = from @Text- --- -- The first type application of the 'to' function on the other hand specifies- -- the output data type.- from :: a -> b-- -- |- -- Requires the presence of 'IsSome' in reverse direction.- default from :: (IsSome b a) => a -> b- from = to--instance IsMany a a
− src/library/LawfulConversions/Classes/IsSome.hs
@@ -1,51 +0,0 @@-module LawfulConversions.Classes.IsSome where--import LawfulConversions.Prelude---- |--- Evidence that all values of type @b@ form a subset of all values of type @a@.------ [From Wikipedia](https://en.wikipedia.org/wiki/Subset):------ In mathematics, a set A is a subset of a set B if all elements of A are also elements of B; B is then a superset of A. It is possible for A and B to be equal; if they are unequal, then A is a proper subset of B. The relationship of one set being a subset of another is called inclusion (or sometimes containment). A is a subset of B may also be expressed as B includes (or contains) A or A is included (or contained) in B. A k-subset is a subset with k elements.------ === Laws------ ==== 'to' is [injective](https://en.wikipedia.org/wiki/Injective_function)------ For every two values of type @b@ that are not equal converting with 'to' produces values that are not equal as well:------ > \(b1, b2) -> b1 == b2 || to @a b1 /= to @a b2------ ==== 'maybeFrom' is a [partial inverse](https://en.wikipedia.org/wiki/Inverse_function#Partial_inverses) of 'to'------ For all values of @b@ converting to @a@ and then attempting to convert back to @b@ always succeeds and produces a value that is equal to the original:------ > \b -> maybeFrom (to @a b) == Just b------ === Testing------ For testing whether your instances conform to these laws use 'LawfulConversions.isSomeProperties'.-class IsSome a b where- -- |- -- Convert a value of a subset type to a superset type.- to :: b -> a-- -- |- -- [Partial inverse](https://en.wikipedia.org/wiki/Inverse_function#Partial_inverses) of 'to'.- maybeFrom :: a -> Maybe b-- -- |- -- Requires the presence of 'IsSome' in reverse direction.- default maybeFrom :: (IsSome b a) => a -> Maybe b- maybeFrom = Just . to---- | Every type is isomorphic to itself.-instance IsSome a a where- to = id- maybeFrom = Just . id---- | The empty set has no elements, and therefore is vacuously a subset of any set.-instance IsSome a Void where- to = absurd- maybeFrom = const Nothing