Safe Haskell | None |
---|---|
Language | Haskell2010 |
IsomorphismClass
Contents
Description
UX
Essentially the whole API is just two functions: to
and from
. Both
perform a conversion between two types. The only difference between them
is in what the first type application parameter specifies. E.g.:
toText = to @Text
fromBuilder = from @Builder
The types are self-evident:
> :t to @Text to @Text :: IsomorphicTo Text b => b -> Text
> :t from @Builder from @Builder :: IsomorphicTo Builder b => Builder -> b
In other words to
and 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
combineEncodings ::ShortByteString
->ByteArray
->ByteString
-> [Word8] combineEncodings a b c =from
@Builder
$to
a <>to
b <>to
c
Which is equivalent to:
combineEncodings ::ShortByteString
->ByteArray
->ByteString
-> [Word8] combineEncodings a b c = LazyByteString.unpack
$ Builder.toLazyByteString
$ mconcat [ Builder.shortByteString
a, Builder.shortByteString
( let ByteArray.ByteArray
array = b in ShortByteString.SBS
array ), Builder.lazyByteString
c ]
Synopsis
- class IsomorphicTo b a => IsomorphicTo a b where
- to :: b -> a
- from :: IsomorphicTo a b => a -> b
- isomorphicToIso :: (IsomorphicTo a b, Profunctor p, Functor f) => p b (f b) -> p a (f a)
- isomorphicToProperties :: (IsomorphicTo a b, Eq a, Eq b, Arbitrary a, Show a, Arbitrary b, Show b) => Proxy a -> Proxy b -> [(String, Property)]
Typeclasses
class IsomorphicTo b a => IsomorphicTo a b where Source #
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 IsomorphicTo a b
as "B is isomorphic to A".
Laws
B is isomorphic to A if and only if there exists a conversion from B
to A (to
) and a conversion from A to B (from
) such that:
- For all values of B converting from B to A and then converting from A to B produces a value that is identical to the original.from
.to
=id
- For all values of A converting from A to B and then converting from B to A produces a value that is identical to the original.to
.from
=id
Testing
For testing whether your instances conform to these laws use isomorphicToProperties
.
Instance Definition
For each pair of isomorphic types (A and B) the compiler will require
you to define two instances, namely: IsomorphicTo A B
and IsomorphicTo B A
.
Instances
from :: IsomorphicTo a b => a -> b Source #
to
in reverse direction.
Particularly useful in combination with the TypeApplications
extension,
where it allows to specify the input type, e.g.:
fromText :: IsomorphicTo Text b => Text -> b fromText = from @Text
The first type application of the to
function on the other hand specifies
the output data type.
Optics
isomorphicToIso :: (IsomorphicTo a b, Profunctor p, Functor f) => p b (f b) -> p a (f a) Source #
Van-Laarhoven-style Isomorphism, compatible with the "lens" library.
Testing
isomorphicToProperties :: (IsomorphicTo a b, Eq a, Eq b, Arbitrary a, Show a, Arbitrary b, Show b) => Proxy a -> Proxy b -> [(String, Property)] Source #
Properties testing whether an instance satisfies the laws of IsomorphicTo
.
The instance is identified via the proxy types that you provide.
E.g., here's how you can integrate it into an Hspec test-suite:
spec = do describe "IsomorphicTo laws" do traverse_ (uncurry prop) (isomorphicToProperties @Int32 @Word32 Proxy Proxy)