cabal-version: 3.0 name: lawful-conversions version: 0.1.1 x-revision: 2 synopsis: Lawful typeclasses for bidirectional conversion between types description: Lawful typeclasses capturing three main patterns of bidirectional mapping. The typeclasses form a layered hierarchy with ascending strictness of laws. 1. `LawfulConversions.IsSome`: Smart constructor 2. `LawfulConversions.IsMany`: Lossy conversion 3. `LawfulConversions.Is`: Isomorphism or lossless conversion = The conversion problem Have you ever looked for a @toString@ function? How often do you import @Data.Text.Lazy@ only to call its 'Data.Text.Lazy.fromStrict'? How about importing @Data.ByteString.Builder@ only to call its 'Data.ByteString.Builder.toLazyByteString' and then importing @Data.ByteString.Lazy@ only to call its 'Data.ByteString.Lazy.toStrict'? Those all are instances of one pattern. They are conversions between representations of the same information. Codebases that don't attempt to abstract over this pattern tend to be sprawling with this type of boilerplate. It's noise to the codereader, it's a burden to the implementor and the maintainer. = Why another conversion library? Many libraries exist that approach the conversion problem. However most of them provide lawless typeclasses leaving it up to the author of the instance to define what makes a proper conversion. This results in inconsistencies across instances, their behaviour not being evident to the user and no way to check whether an instance is correct. This library tackles this problem with a lawful typeclass hierarchy, making it evident what any of its instances do and it provides property-tests for you to validate your instances. = Conversions The main part of the API is two functions: 'LawfulConversions.to' and 'LawfulConversions.from'. Both perform a conversion between two types. The main difference between them is in what the first type application parameter specifies. E.g.: > toString = to @String > fromText = from @Text The types should be self-evident: > > :t to @String > to @String :: IsSome String b => b -> String > > :t from @Text > from @Text :: IsMany Text b => Text -> b In other words 'LawfulConversions.to' and 'LawfulConversions.from' let you explicitly specify either the source or the target type of a conversion when you need to help the type inferencer or the reader. == Examples @ renderNameAndHeight :: 'Text' -> 'Int' -> 'Text' renderNameAndHeight name height = 'LawfulConversions.from' @'Data.Text.Encoding.StrictTextBuilder' $ "Height of " <> 'LawfulConversions.to' name <> " is " <> 'LawfulConversions.to' (show height) @ @ combineEncodings :: 'Data.ByteString.Short.ShortByteString' -> 'Data.Primitive.ByteArray' -> ['Word8'] -> 'Data.ByteString.Lazy.ByteString' combineEncodings a b c = 'LawfulConversions.from' @'Data.ByteString.Builder.Builder' $ 'LawfulConversions.to' a <> 'LawfulConversions.to' b <> 'LawfulConversions.to' c @ = Partial conversions This library also captures the pattern of smart constructors via the 'LawfulConversions.IsSome' class, which associates a total 'LawfulConversions.to' conversion with its partial inverse 'LawfulConversions.maybeFrom'. This captures the codec relationship between types. E.g., - Every 'Int16' can be losslessly converted into 'Int32', but not every 'Int32' can be losslessly converted into 'Int16'. - Every 'Text' can be converted into 'ByteString' via UTF-8 encoding, but not every 'ByteString' forms a valid UTF-8 sequence. - Every URL can be uniquely represented as 'Text', but most 'Text's are not URLs unfortunately. category: Conversion homepage: https://github.com/nikita-volkov/lawful-conversions bug-reports: https://github.com/nikita-volkov/lawful-conversions/issues author: Nikita Volkov maintainer: Nikita Volkov copyright: (c) 2022 Nikita Volkov license: MIT license-file: LICENSE extra-source-files: CHANGELOG.md source-repository head type: git location: git://github.com/nikita-volkov/lawful-conversions.git common language-settings default-language: Haskell2010 default-extensions: BlockArguments DefaultSignatures FlexibleContexts FlexibleInstances MagicHash MultiParamTypeClasses NoImplicitPrelude ScopedTypeVariables TypeApplications UndecidableSuperClasses library import: language-settings hs-source-dirs: src/library exposed-modules: LawfulConversions other-modules: LawfulConversions.Classes LawfulConversions.Classes.Is LawfulConversions.Classes.IsMany LawfulConversions.Classes.IsSome LawfulConversions.Optics LawfulConversions.Prelude LawfulConversions.Properties LawfulConversions.Proxies LawfulConversions.Proxies.ViaIsSome LawfulConversions.Relations LawfulConversions.Relations.BoxedVectorAndList LawfulConversions.Relations.BoxedVectorAndSeq LawfulConversions.Relations.ByteArrayAndByteString LawfulConversions.Relations.ByteArrayAndLazyByteString LawfulConversions.Relations.ByteArrayAndLazyByteStringBuilder LawfulConversions.Relations.ByteArrayAndShortByteString LawfulConversions.Relations.ByteArrayAndTextArray LawfulConversions.Relations.ByteArrayAndWord8List LawfulConversions.Relations.ByteStringAndLazyByteString LawfulConversions.Relations.ByteStringAndLazyByteStringBuilder LawfulConversions.Relations.ByteStringAndShortByteString LawfulConversions.Relations.ByteStringAndText LawfulConversions.Relations.ByteStringAndTextArray LawfulConversions.Relations.ByteStringAndWord8List LawfulConversions.Relations.Int16AndWord16 LawfulConversions.Relations.Int32AndWord32 LawfulConversions.Relations.Int64AndWord64 LawfulConversions.Relations.Int8AndWord8 LawfulConversions.Relations.IntAndWord LawfulConversions.Relations.IntMapAndMapOfInt LawfulConversions.Relations.IntSetAndSetOfInt LawfulConversions.Relations.LazyByteStringAndLazyByteStringBuilder LawfulConversions.Relations.LazyByteStringAndLazyText LawfulConversions.Relations.LazyByteStringAndShortByteString LawfulConversions.Relations.LazyByteStringAndTextArray LawfulConversions.Relations.LazyByteStringAndWord8List LawfulConversions.Relations.LazyByteStringBuilderAndShortByteString LawfulConversions.Relations.LazyByteStringBuilderAndTextArray LawfulConversions.Relations.LazyByteStringBuilderAndWord8List LawfulConversions.Relations.LazyTextAndLazyTextBuilder LawfulConversions.Relations.LazyTextAndStrictTextBuilder LawfulConversions.Relations.LazyTextAndString LawfulConversions.Relations.LazyTextAndText LawfulConversions.Relations.LazyTextBuilderAndStrictTextBuilder LawfulConversions.Relations.LazyTextBuilderAndString LawfulConversions.Relations.LazyTextBuilderAndText LawfulConversions.Relations.ListAndSeq LawfulConversions.Relations.ShortByteStringAndTextArray LawfulConversions.Relations.ShortByteStringAndWord8List LawfulConversions.Relations.StrictTextBuilderAndString LawfulConversions.Relations.StrictTextBuilderAndText LawfulConversions.Relations.StringAndText LawfulConversions.Relations.TextArrayAndWord8List LawfulConversions.TextCompat.Array build-depends: QuickCheck >=2.13 && <3, base >=4.12 && <5, bytestring >=0.10 && <0.13, containers >=0.6 && <0.8, hashable >=1 && <2, primitive >=0.7 && <0.10, profunctors >=5 && <6, text >=1.2 && <2.2, unordered-containers >=0.2 && <0.3, vector >=0.12 && <0.14, test-suite test import: language-settings type: exitcode-stdio-1.0 hs-source-dirs: src/test main-is: Main.hs other-modules: Test.ExtraInstances build-depends: QuickCheck >=2.13 && <3, bytestring >=0.11.1.0 && <0.13, lawful-conversions, primitive >=0.7 && <0.10, quickcheck-instances >=0.3.32 && <0.4, rebase >=1.15 && <2, tasty >=1.2.3 && <2, tasty-quickcheck >=0.10.1 && <0.11, text >=1.2 && <3,