Safe Haskell | Safe |
---|---|
Language | Haskell2010 |
type-encoding overview examples.
This library is concerned with 3 main operations done on strings: encoding, decoding, and recovery. Examples in this module cover all of these base cases.
This module uses encoding instances found in
Synopsis
- helloB64 :: Enc '["enc-B64"] () ByteString
- helloB64B64 :: Enc '["enc-B64", "enc-B64"] () ByteString
- helloB64Decoded :: ByteString
- helloB64Recovered :: Either RecreateEx (Enc '["enc-B64"] () ByteString)
- helloB64B64PartDecode :: Enc '["enc-B64"] () ByteString
- helloB64B64Decoded :: ByteString
- helloB64B64RecoveredErr :: Either RecreateEx (Enc '["enc-B64", "enc-B64"] () ByteString)
- helloUPP :: Enc '["do-UPPER"] () Text
- helloTitleRev :: Enc '["do-reverse", "do-Title"] () Text
- newtype Config = Config {}
- exampleConf :: Config
- helloTitle :: Enc '["do-Title"] Config Text
- helloRevLimit :: Enc '["do-size-limit", "do-reverse", "do-Title"] Config Text
- helloLimitB64 :: Enc '["enc-B64", "do-size-limit"] Config ByteString
- helloRevLimitParDec :: Enc '["do-size-limit"] Config ByteString
- helloAscii :: Either EncodeEx (Enc '["r-ASCII"] () ByteString)
- helloAsciiB64 :: Either EncodeEx (Enc '["enc-B64", "r-ASCII"] () ByteString)
- helloAsciiB64PartDec :: Either EncodeEx (Enc '["r-ASCII"] () ByteString)
Documentation
>>>
:set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XTypeApplications
>>>
import Data.Functor.Identity
This module contains some ghci friendly values to play with.
Each value is documented in a doctest style by including an equivalent ghci ready expression. These documents generate a test suite for this library as well.
Basics
helloB64 :: Enc '["enc-B64"] () ByteString Source #
"Hello World" encoded as Base64
>>>
helloB64
UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ="
>>>
displ helloB64
"Enc '[enc-B64] () (ByteString SGVsbG8gV29ybGQ=)"
>>>
encodeAll . toEncoding () $ "Hello World" :: Enc '["enc-B64"] () B.ByteString
UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ="
helloB64B64 :: Enc '["enc-B64", "enc-B64"] () ByteString Source #
"Hello World" double-Base64 encoded. Notice the same code used as in single encoding, the game is played at type level.
>>>
encodeAll . toEncoding () $ "Hello World" :: Enc '["enc-B64","enc-B64"] () B.ByteString
UnsafeMkEnc Proxy () "U0dWc2JHOGdWMjl5YkdRPQ=="
>>>
displ helloB64B64
"Enc '[enc-B64,enc-B64] () (ByteString U0dWc2JHOGdWMjl5YkdRPQ==)"
An alternative version of the above code is this:
>>>
fmap displ . runEncodings' @'["enc-B64","enc-B64"] @'["enc-B64","enc-B64"] @Identity encodings . toEncoding () $ ("Hello World" :: B.ByteString)
Identity "Enc '[enc-B64,enc-B64] () (ByteString U0dWc2JHOGdWMjl5YkdRPQ==)"
This is how typed-encoding works, the Data.TypedEncoding.Common.Class.Encode.EncodeAll
constraint can be used to get access to list to encodings required by the symbol annotation.
runEncodings'
executes all the necessary transformations.
Similar story is true for decoding and validation. In these examples we will use shortcut combinators.
helloB64Decoded :: ByteString Source #
Previous text decoded from Base64
>>>
fromEncoding . decodeAll $ helloB64
"Hello World"
helloB64Recovered :: Either RecreateEx (Enc '["enc-B64"] () ByteString) Source #
recreateFAll
allows for recovering data at program boundaries (for example, when parsing JSON input).
It makes sure that the content satisfies specified encodings.
>>>
recreateFAll . toEncoding () $ "SGVsbG8gV29ybGQ=" :: Either RecreateEx (Enc '["enc-B64"] () B.ByteString)
Right (UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ=")
>>>
recreateFAll . toEncoding () $ "SGVsbG8gV29ybGQ" :: Either RecreateEx (Enc '["enc-B64"] () B.ByteString)
Left (RecreateEx "enc-B64" ("invalid padding"))
The above example start by placing payload in zero-encoded Enc '[] ()
type and then apply recreateFAll
this is a good way to recreate encoded type if encoding is known.
If is it not, UncheckedEnc
type can be used.
(See ToEncString
for better example).
This module is concerned only with the first approach.
>>>
let unchecked = toUncheckedEnc ["enc-B64"] () ("SGVsbG8gV29ybGQ=" :: T.Text)
>>>
check @'["enc-B64"] @(Either RecreateEx) unchecked
Just (Right (UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ="))
helloB64B64PartDecode :: Enc '["enc-B64"] () ByteString Source #
Double Base64 encoded "Hello World" with one layer of encoding removed
>>>
decodePart @'["enc-B64"] $ helloB64B64 :: Enc '["enc-B64"] () B.ByteString
UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ="
>>>
helloB64B64PartDecode == helloB64
True
decodePart
is a convenience function that simply replies decoding above
first "enc-B64"
>>>
above @'["enc-B64"] @'["enc-B64"] @'[] decodeAll $ helloB64B64
UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ="
helloB64B64Decoded :: ByteString Source #
helloB64B64
all the way to ByteString
Notice a similar polymorphism in decoding.
>>>
fromEncoding . decodeAll $ helloB64B64 :: B.ByteString
"Hello World"
We can also decode all the parts:
>>>
fromEncoding . decodePart @'["enc-B64","enc-B64"] $ helloB64B64
"Hello World"
helloB64B64RecoveredErr :: Either RecreateEx (Enc '["enc-B64", "enc-B64"] () ByteString) Source #
what happens when we try to recover encoded once text to Enc '["enc-B64", "enc-B64"]
.
Again, notice the same expression is used as in previous recovery.
>>>
recreateFAll . toEncoding () $ "SGVsbG8gV29ybGQ=" :: Either RecreateEx (Enc '["enc-B64", "enc-B64"] () B.ByteString)
Left (RecreateEx "enc-B64" ("invalid padding"))
"do-" Encodings
helloUPP :: Enc '["do-UPPER"] () Text Source #
"do-UPPER" (from Sample
module) encoding applied to "Hello World"
Notice a namespace thing going on, "enc-" is encoding, "do-" is some transformation. These are typically not reversible, some could be recoverable.
The same code is used as in "enc-" examples to encode (now transform).
>>>
encodeAll . toEncoding () $ "Hello World" :: Enc '["do-UPPER"] () T.Text
UnsafeMkEnc Proxy () "HELLO WORLD"
helloTitleRev :: Enc '["do-reverse", "do-Title"] () Text Source #
Sample compound transformation
>>>
encodeAll . toEncoding () $ "HeLLo world" :: Enc '["do-reverse", "do-Title"] () T.Text
UnsafeMkEnc Proxy () "dlroW olleH"
Configuration
Example configuration
exampleConf :: Config Source #
helloRevLimit :: Enc '["do-size-limit", "do-reverse", "do-Title"] Config Text Source #
Configuration can be used to impact the encoding process.
So far we had used ()
as configuration of all encodings.
But since both "do-reverse", "do-Title" are polymorphic in
configuration we can also do this:
>>>
encodeAll . toEncoding exampleConf $ "HeLLo world" :: Enc '["do-reverse", "do-Title"] Config T.Text
UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW olleH"
>>>
encodeAll . toEncoding exampleConf $ "HeLlo world" :: Enc '["do-size-limit", "do-reverse", "do-Title"] Config T.Text
UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW ol"
Instead, encode previously defined helloTitle
by reversing it and adding size limit
>>>
encodePart @'["do-size-limit", "do-reverse"] helloTitle :: Enc '["do-size-limit", "do-reverse", "do-Title"] Config T.Text
UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW ol"
encodePart
is simply encodeAll played above "do-Title" encoding:
>>>
above @'["do-Title"] @'[] @'["do-size-limit", "do-reverse"] encodeAll helloTitle
UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW ol"
helloLimitB64 :: Enc '["enc-B64", "do-size-limit"] Config ByteString Source #
helloRevLimitParDec :: Enc '["do-size-limit"] Config ByteString Source #
... and we unwrap the B64 part only
>>>
decodePart @'["enc-B64"] $ helloLimitB64
UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "HeLlo wo"
"r-" encodings section
helloAscii :: Either EncodeEx (Enc '["r-ASCII"] () ByteString) Source #
ASCII char set
ByteStrings are sequences of Bytes (Word8
). The type
is very permissive, it may contain binary data such as jpeg picture.
"r-ASCII" encoding acts as partial identity function
it does not change any bytes in bytestring but it fails if a byte
is outside of ASCII range (in Either
monad).
Note naming thing: "r-" is partial identity ("r-" is from restriction).
>>>
encodeFAll . toEncoding () $ "HeLlo world" :: Either EncodeEx (Enc '["r-ASCII"] () B.ByteString)
Right (UnsafeMkEnc Proxy () "HeLlo world")
helloAsciiB64 :: Either EncodeEx (Enc '["enc-B64", "r-ASCII"] () ByteString) Source #
Arguably the type we used for helloB64 was too permissive. a better version is here:
>>>
encodeFAll . toEncoding () $ "Hello World" :: Either EncodeEx (Enc '["enc-B64", "r-ASCII"] () B.ByteString)
Right (UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ=")
helloAsciiB64PartDec :: Either EncodeEx (Enc '["r-ASCII"] () ByteString) Source #
>>>
decodePart @'["enc-B64"] <$> helloAsciiB64
Right (UnsafeMkEnc Proxy () "Hello World")