packages feed

ascii-superset 1.0.1.4 → 1.0.1.6

raw patch · 11 files changed

+502/−441 lines, 11 filesdep +ascii-supersetdep ~ascii-chardep ~basedep ~textPVP ok

version bump matches the API change (PVP)

Dependencies added: ascii-superset

Dependency ranges changed: ascii-char, base, text

API changes (from Hackage documentation)

Files

− ASCII/Isomorphism.hs
@@ -1,28 +0,0 @@-module ASCII.Isomorphism ( CharIso (..), asChar, StringIso (..) ) where--import ASCII.Char     ( Char )-import ASCII.Superset ( CharSuperset (..), StringSuperset (..) )-import Data.Function  ( id, (.) )-import Data.List      ( map )--class CharSuperset char => CharIso char-  where-    toChar :: char -> Char--asChar :: CharIso char => (Char -> Char) -> char -> char-asChar f = fromChar . f . toChar--class StringSuperset string => StringIso string-  where-    toCharList :: string -> [Char]-    mapChars :: (Char -> Char) -> string -> string---- | 'Char' is trivially isomorphic to itself. (This instance is uninteresting.)-instance CharIso Char-  where-    toChar = id--instance CharIso char => StringIso [char]-  where-    toCharList = map toChar-    mapChars f = map (asChar f)
− ASCII/Lift.hs
@@ -1,49 +0,0 @@-{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}--module ASCII.Lift ( Lift (..) ) where--import ASCII.Char       ( Char )-import ASCII.Refinement ( ASCII )-import ASCII.Superset   ( CharSuperset, StringSuperset )--import qualified ASCII.Refinement as R-import qualified ASCII.Superset as S--import qualified Prelude--{- $setup-->>> import ASCII.Char (Char (..))->>> import Data.Text (Text)->>> import Data.Word (Word8)---}--class Lift ascii superset-  where--    {- | Converts from ASCII to any larger type.--    >>> lift CapitalLetterA :: Word8-    65--    >>> lift [CapitalLetterH,SmallLetterI,ExclamationMark] :: Text-    "Hi!"--    Due to the highly polymorphic nature of the 'lift' function, often it must used with an explicit type signature or type application to avoid any type ambiguity.--    -}--    lift :: ascii -> superset---- | A value from an ASCII superset that has been refined by the 'ASCII' type constructor may be lifted back into the superset by unwrapping it from the 'ASCII' type.--instance Lift (ASCII superset) superset where lift = R.lift---- | An ASCII 'Char' may be 'lift'ed into any larger character set (a 'CharSuperset'); for example, 'lift' can convert an ASCII character into a value of the standard 'Prelude.Char' type in "Prelude".--instance CharSuperset superset => Lift Char superset where lift = S.fromChar---- | An ASCII 'Char' list may be 'lift'ed into a string of any larger character set (a 'StringSuperset'); for example, 'lift' can convert a list of ASCII characters into a value of the standard 'Prelude.String' type in "Prelude".--instance StringSuperset superset => Lift [Char] superset where lift = S.fromCharList
− ASCII/Refinement.hs
@@ -1,161 +0,0 @@-module ASCII.Refinement-  (-    {- * ASCII type constructor -} ASCII, lift, asciiUnsafe,-    {- * Character functions -} validateChar, fromChar, toChar, substituteChar, asChar,-    {- * String functions -} validateString, fromCharList, toCharList, substituteString, mapChars-  )-  where--import qualified ASCII.Char as ASCII-import qualified ASCII.Superset as S-import qualified ASCII.Isomorphism as I--import ASCII.Superset    ( CharSuperset, StringSuperset )-import Data.Bool         ( Bool (..) )-import Data.Data         ( Data )-import Data.Eq           ( Eq )-import Data.Function     ( (.), ($), id )-import Data.Hashable     ( Hashable )-import Data.List         ( map )-import Data.Maybe        ( Maybe (..) )-import Data.Monoid       ( Monoid )-import Data.Ord          ( Ord, (>) )-import Data.Semigroup    ( Semigroup )-import GHC.Generics      ( Generic )-import Prelude           ( succ )-import Text.Show         ( Show, showString, showsPrec, showParen, showList )--{- $setup-->>> :set -XOverloadedStrings->>> import ASCII.Char (Char (..))->>> import Data.List (map)->>> import Data.Int (Int)->>> import Data.String (String)->>> import Data.Text (Text)---}--{- | This type constructor indicates that a value from some ASCII superset is valid ASCII. The type parameter is the ASCII superset, which should be a type with an instance of either 'CharSuperset' or 'StringSuperset'.--For example, whereas a 'Data.Text.Text' value may contain a combination of ASCII and non-ASCII characters, a value of type @'ASCII' 'Data.Text.Text'@ may contain only ASCII characters.---}--newtype ASCII superset = ASCII_Unsafe { lift :: superset }--deriving stock instance Eq superset => Eq (ASCII superset)--deriving stock instance Ord superset => Ord (ASCII superset)--deriving newtype instance Hashable superset => Hashable (ASCII superset)--deriving newtype instance Semigroup superset => Semigroup (ASCII superset)--deriving newtype instance Monoid superset => Monoid (ASCII superset)--deriving stock instance Data superset => Data (ASCII superset)--deriving stock instance Generic (ASCII superset)--instance Show superset => Show (ASCII superset)-  where-    showsPrec d x = showParen (d > app_prec) $-        showString "asciiUnsafe " . showsPrec (succ app_prec) (lift x)-      where app_prec = 10--    showList x = showString "asciiUnsafe " . showList (map lift x)--instance CharSuperset char => CharSuperset (ASCII char)-  where-    isAsciiChar _ = True-    fromChar = asciiUnsafe . S.fromChar-    toCharUnsafe = S.toCharUnsafe . lift--instance CharSuperset char => I.CharIso (ASCII char)-  where-    toChar = S.toCharUnsafe--instance StringSuperset string => StringSuperset (ASCII string)-  where-    isAsciiString _ = True-    fromCharList = asciiUnsafe . S.fromCharList-    toCharListUnsafe = S.toCharListUnsafe . lift-    toCharListSub = S.toCharListUnsafe . lift-    substituteString = id--instance StringSuperset string => I.StringIso (ASCII string)-  where-    toCharList = S.toCharListUnsafe-    mapChars = S.mapCharsUnsafe--asciiUnsafe :: superset -> ASCII superset-asciiUnsafe = ASCII_Unsafe--{- |-->>> map validateChar [-1, 65, 97, 128] :: [Maybe (ASCII Int)]-[Nothing,Just (asciiUnsafe 65),Just (asciiUnsafe 97),Nothing]---}--validateChar :: CharSuperset superset => superset -> Maybe (ASCII superset)-validateChar x = if S.isAsciiChar x then Just (asciiUnsafe x) else Nothing--substituteChar :: CharSuperset superset => superset -> ASCII superset-substituteChar x = if S.isAsciiChar x then asciiUnsafe x else fromChar ASCII.Substitute--fromChar :: CharSuperset superset => ASCII.Char -> ASCII superset-fromChar = asciiUnsafe . S.fromChar--toChar :: CharSuperset superset => ASCII superset -> ASCII.Char-toChar = S.toCharUnsafe . lift--{- |-->>> fromCharList [CapitalLetterH,SmallLetterI,ExclamationMark] :: ASCII Text-asciiUnsafe "Hi!"---}--fromCharList :: StringSuperset superset => [ASCII.Char] -> ASCII superset-fromCharList = asciiUnsafe . S.fromCharList--{- |-->>> toCharList (substituteString "Piñata" :: ASCII Text)-[CapitalLetterP,SmallLetterI,Substitute,SmallLetterA,SmallLetterT,SmallLetterA]---}--toCharList :: StringSuperset superset => ASCII superset -> [ASCII.Char]-toCharList = S.toCharListUnsafe . lift--{- | Forces a string from a larger character set into ASCII by using the 'ASCII.Substitute' character in place of any non-ASCII characters.-->>> substituteString "Cristóbal" :: ASCII Text-asciiUnsafe "Crist\SUBbal"---}--substituteString :: StringSuperset superset => superset -> ASCII superset-substituteString = asciiUnsafe . S.substituteString--{- |-->>> map validateString ["Hello", "Cristóbal"] :: [Maybe (ASCII Text)]-[Just (asciiUnsafe "Hello"),Nothing]-->>> map validateString ["Hello", "Cristóbal"] :: [Maybe (ASCII String)]-[Just (asciiUnsafe "Hello"),Nothing]---}--validateString :: StringSuperset superset => superset -> Maybe (ASCII superset)-validateString x = if S.isAsciiString x then Just (asciiUnsafe x) else Nothing--asChar :: CharSuperset superset => (ASCII.Char -> ASCII.Char) -> ASCII superset -> ASCII superset-asChar f = asciiUnsafe . S.asCharUnsafe f . lift--mapChars :: StringSuperset superset => (ASCII.Char -> ASCII.Char) -> ASCII superset -> ASCII superset-mapChars f = asciiUnsafe . S.mapCharsUnsafe f . lift
− ASCII/Superset.hs
@@ -1,199 +0,0 @@-module ASCII.Superset (--    {- * Characters -}-    {- ** Class -} CharSuperset (..),-    {- ** Functions -} asCharUnsafe, toCharMaybe, toCharOrFail, toCharSub, substituteChar, convertCharMaybe, convertCharOrFail,--    {- * Strings -}-    {- ** Class -} StringSuperset (..),-    {- ** Functions -} toCharListMaybe, toCharListOrFail, convertStringMaybe, convertStringOrFail--    ) where--import Control.Monad      ( return )-import Control.Monad.Fail ( MonadFail (fail) )-import Data.Bool          ( Bool, (&&) )-import Data.Function      ( (.), id )-import Data.Functor       ( fmap )-import Data.Ord           ( (<=), (>=) )-import Data.Maybe         ( Maybe (..) )--import qualified ASCII.Char               as  ASCII-import qualified Data.Bool                as  Bool-import qualified Data.ByteString          as  BS-import qualified Data.ByteString.Lazy     as  LBS-import qualified Data.ByteString.Builder  as  BSB-import qualified Data.Char                as  Unicode-import qualified Data.Int                 as  Int-import qualified Data.List                as  List-import qualified Data.Text                as  T-import qualified Data.Text.Lazy           as  LT-import qualified Data.Text.Lazy.Builder   as  TB-import qualified Data.Word                as  Word-import qualified Numeric.Natural          as  Nat-import qualified Prelude------  Char  -----class CharSuperset char-  where--    isAsciiChar :: char -> Bool--    fromChar :: ASCII.Char -> char--    toCharUnsafe :: char -> ASCII.Char--asCharUnsafe :: CharSuperset char => (ASCII.Char -> ASCII.Char) -> char -> char-asCharUnsafe f = fromChar . f . toCharUnsafe--toCharMaybe :: CharSuperset char => char -> Maybe ASCII.Char-toCharMaybe = toCharOrFail--toCharOrFail :: (CharSuperset char, MonadFail context) => char -> context ASCII.Char-toCharOrFail x = if isAsciiChar x then return (toCharUnsafe x) else fail "Not an ASCII character"--toCharSub :: CharSuperset char => char -> ASCII.Char-toCharSub x = if isAsciiChar x then toCharUnsafe x else ASCII.Substitute--substituteChar :: CharSuperset char => char -> char-substituteChar x = if isAsciiChar x then x else fromChar ASCII.Substitute---- | Convert from one ASCII-superset character type to another via the ASCII 'ASCII.Char' type. Fails as 'Nothing' if the input is outside the ASCII character set.-convertCharMaybe :: (CharSuperset char1, CharSuperset char2) => char1 -> Maybe char2-convertCharMaybe = convertCharOrFail---- | Convert from one ASCII-superset character type to another via the ASCII 'ASCII.Char' type. Fails with 'fail' if the input is outside the ASCII character set.-convertCharOrFail :: (CharSuperset char1, CharSuperset char2, MonadFail context) => char1 -> context char2-convertCharOrFail = fmap fromChar . toCharOrFail------  String  -----class StringSuperset string-  where--    isAsciiString :: string -> Bool--    fromCharList :: [ASCII.Char] -> string--    toCharListUnsafe :: string -> [ASCII.Char]--    toCharListSub :: string -> [ASCII.Char]--    substituteString :: string -> string--    mapCharsUnsafe :: (ASCII.Char -> ASCII.Char) -> string -> string-    mapCharsUnsafe f = fromCharList  . List.map f . toCharListUnsafe--toCharListMaybe :: StringSuperset string => string -> Maybe [ASCII.Char]-toCharListMaybe = toCharListOrFail--toCharListOrFail :: (StringSuperset string, MonadFail context) => string -> context [ASCII.Char]-toCharListOrFail x = if isAsciiString x then return (toCharListUnsafe x) else fail "String contains non-ASCII characters"---- | Convert from one ASCII-superset string type to another by converting each character of the input string to an ASCII 'ASCII.Char', and then converting the ASCII character list to the desired output type. Fails as 'Nothing' if the input contains any character that is outside the ASCII character set.-convertStringMaybe :: (StringSuperset string1, StringSuperset string2) => string1 -> Maybe string2-convertStringMaybe = convertStringOrFail---- | Convert from one ASCII-superset string type to another by converting each character of the input string to an ASCII 'ASCII.Char', and then converting the ASCII character list to the desired output type. Fails with 'fail' if the input contains any character that is outside the ASCII character set.-convertStringOrFail :: (StringSuperset string1, StringSuperset string2, MonadFail context) => string1 -> context string2-convertStringOrFail = fmap fromCharList . toCharListOrFail------  Instances  ------- | 'ASCII.Char' is trivially a superset of itself. (This instance is uninteresting.)--instance CharSuperset ASCII.Char-  where-    isAsciiChar _ = Bool.True-    fromChar = id-    toCharUnsafe = id--instance CharSuperset Unicode.Char-  where-    isAsciiChar = (<= '\DEL')-    fromChar = Unicode.chr . ASCII.toInt-    toCharUnsafe = ASCII.fromIntUnsafe . Unicode.ord--instance CharSuperset Nat.Natural-  where-    isAsciiChar = (<= 127)-    fromChar = Prelude.fromIntegral . ASCII.toInt-    toCharUnsafe = ASCII.fromIntUnsafe . Prelude.fromIntegral--instance CharSuperset Int.Int-  where-    isAsciiChar x = (x >= 0) && (x <= 127)-    fromChar = ASCII.toInt-    toCharUnsafe = ASCII.fromIntUnsafe--instance CharSuperset Word.Word8-  where-    isAsciiChar = (<= 127)-    fromChar = Prelude.fromIntegral . ASCII.toInt-    toCharUnsafe = ASCII.fromIntUnsafe . Prelude.fromIntegral--instance CharSuperset char => StringSuperset [char]-  where-    isAsciiString = List.all isAsciiChar-    fromCharList = List.map fromChar-    toCharListUnsafe = List.map toCharUnsafe-    toCharListSub = List.map toCharSub-    substituteString = List.map substituteChar--instance StringSuperset T.Text-  where-    isAsciiString = T.all isAsciiChar-    fromCharList = T.pack . fromCharList-    toCharListUnsafe = toCharListUnsafe . T.unpack-    toCharListSub = toCharListSub . T.unpack-    substituteString = T.map substituteChar-    mapCharsUnsafe f = T.map (asCharUnsafe f)--instance StringSuperset LT.Text-  where-    isAsciiString = LT.all isAsciiChar-    fromCharList = LT.pack . fromCharList-    toCharListUnsafe = toCharListUnsafe . LT.unpack-    toCharListSub = toCharListSub . LT.unpack-    substituteString = LT.map substituteChar-    mapCharsUnsafe f = LT.map (asCharUnsafe f)--instance StringSuperset TB.Builder-  where-    isAsciiString = isAsciiString . TB.toLazyText-    fromCharList = TB.fromString . fromCharList-    toCharListUnsafe = toCharListUnsafe . TB.toLazyText-    toCharListSub = toCharListSub . TB.toLazyText-    substituteString = TB.fromLazyText . substituteString . TB.toLazyText-    mapCharsUnsafe f = TB.fromLazyText . mapCharsUnsafe f . TB.toLazyText--instance StringSuperset BS.ByteString-  where-    isAsciiString = BS.all isAsciiChar-    fromCharList = BS.pack . fromCharList-    toCharListUnsafe = toCharListUnsafe . BS.unpack-    toCharListSub = toCharListSub . BS.unpack-    substituteString = BS.map substituteChar-    mapCharsUnsafe f = BS.map (asCharUnsafe f)--instance StringSuperset LBS.ByteString-  where-    isAsciiString = LBS.all isAsciiChar-    fromCharList = LBS.pack . fromCharList-    toCharListUnsafe = toCharListUnsafe . LBS.unpack-    toCharListSub = toCharListSub . LBS.unpack-    substituteString = LBS.map substituteChar-    mapCharsUnsafe f = LBS.map (asCharUnsafe f)--instance StringSuperset BSB.Builder-  where-    isAsciiString = isAsciiString . BSB.toLazyByteString-    fromCharList = BSB.lazyByteString . fromCharList-    toCharListUnsafe = toCharListUnsafe . BSB.toLazyByteString-    toCharListSub = toCharListSub . BSB.toLazyByteString-    substituteString = BSB.lazyByteString . substituteString . BSB.toLazyByteString-    mapCharsUnsafe f = BSB.lazyByteString . mapCharsUnsafe f . BSB.toLazyByteString
ascii-superset.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.0  name: ascii-superset-version: 1.0.1.4+version: 1.0.1.6 synopsis: Representing ASCII with refined supersets category: Data, Text @@ -18,8 +18,6 @@  build-type: Simple -tested-with: GHC == 9.0.1, GHC == 8.10.1, GHC == 8.8.1, GHC == 8.6.5, GHC == 8.4.3- extra-source-files: changelog.txt  source-repository head@@ -35,15 +33,27 @@     default-extensions: DeriveDataTypeable     default-extensions: DeriveGeneric     ghc-options: -Wall -fno-warn-unused-imports+    hs-source-dirs: library      build-depends: ascii-char ^>= 1.0      build-depends: base       >= 4.11 && < 4.16     build-depends: bytestring ^>= 0.10 || ^>= 0.11     build-depends: hashable   >= 1.2  && < 1.4-    build-depends: text       ^>= 1.2+    build-depends: text       ^>= 1.2.3      exposed-modules: ASCII.Superset     exposed-modules: ASCII.Isomorphism     exposed-modules: ASCII.Refinement     exposed-modules: ASCII.Lift++test-suite test+    type: exitcode-stdio-1.0+    default-language: Haskell2010+    default-extensions: NoImplicitPrelude+    default-extensions: OverloadedStrings+    default-extensions: QuasiQuotes+    ghc-options: -Wall+    hs-source-dirs: test+    main-is: test.hs+    build-depends: ascii-char, ascii-superset, base, text
changelog.txt view
@@ -15,3 +15,8 @@ 1.0.1.2 - 2021-02-09 - Support bytestring-0.11  1.0.1.4 - 2021-02-10 - Support GHC 9.0++1.0.1.6 - 2021-09-26++    - Add a test suite+    - Raise 'text' lower bound to 1.2.3
+ library/ASCII/Isomorphism.hs view
@@ -0,0 +1,28 @@+module ASCII.Isomorphism ( CharIso (..), asChar, StringIso (..) ) where++import ASCII.Char     ( Char )+import ASCII.Superset ( CharSuperset (..), StringSuperset (..) )+import Data.Function  ( id, (.) )+import Data.List      ( map )++class CharSuperset char => CharIso char+  where+    toChar :: char -> Char++asChar :: CharIso char => (Char -> Char) -> char -> char+asChar f = fromChar . f . toChar++class StringSuperset string => StringIso string+  where+    toCharList :: string -> [Char]+    mapChars :: (Char -> Char) -> string -> string++-- | 'Char' is trivially isomorphic to itself. (This instance is uninteresting.)+instance CharIso Char+  where+    toChar = id++instance CharIso char => StringIso [char]+  where+    toCharList = map toChar+    mapChars f = map (asChar f)
+ library/ASCII/Lift.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}++module ASCII.Lift ( Lift (..) ) where++import ASCII.Char       ( Char )+import ASCII.Refinement ( ASCII )+import ASCII.Superset   ( CharSuperset, StringSuperset )++import qualified ASCII.Refinement as R+import qualified ASCII.Superset as S++import qualified Prelude++class Lift ascii superset+  where++    {- | Converts from ASCII to any larger type.++    >>> lift CapitalLetterA :: Word8+    65++    >>> lift [CapitalLetterH,SmallLetterI,ExclamationMark] :: Text+    "Hi!"++    Due to the highly polymorphic nature of the 'lift' function, often it must used with an explicit type signature or type application to avoid any type ambiguity.++    -}++    lift :: ascii -> superset++-- | A value from an ASCII superset that has been refined by the 'ASCII' type constructor may be lifted back into the superset by unwrapping it from the 'ASCII' type.++instance Lift (ASCII superset) superset where lift = R.lift++-- | An ASCII 'Char' may be 'lift'ed into any larger character set (a 'CharSuperset'); for example, 'lift' can convert an ASCII character into a value of the standard 'Prelude.Char' type in "Prelude".++instance CharSuperset superset => Lift Char superset where lift = S.fromChar++-- | An ASCII 'Char' list may be 'lift'ed into a string of any larger character set (a 'StringSuperset'); for example, 'lift' can convert a list of ASCII characters into a value of the standard 'Prelude.String' type in "Prelude".++instance StringSuperset superset => Lift [Char] superset where lift = S.fromCharList
+ library/ASCII/Refinement.hs view
@@ -0,0 +1,150 @@+module ASCII.Refinement+  (+    {- * ASCII type constructor -} ASCII, lift, asciiUnsafe,+    {- * Character functions -} validateChar, fromChar, toChar, substituteChar, asChar,+    {- * String functions -} validateString, fromCharList, toCharList, substituteString, mapChars+  )+  where++import qualified ASCII.Char as ASCII+import qualified ASCII.Superset as S+import qualified ASCII.Isomorphism as I++import ASCII.Superset    ( CharSuperset, StringSuperset )+import Data.Bool         ( Bool (..) )+import Data.Data         ( Data )+import Data.Eq           ( Eq )+import Data.Function     ( (.), ($), id )+import Data.Hashable     ( Hashable )+import Data.List         ( map )+import Data.Maybe        ( Maybe (..) )+import Data.Monoid       ( Monoid )+import Data.Ord          ( Ord, (>) )+import Data.Semigroup    ( Semigroup )+import GHC.Generics      ( Generic )+import Prelude           ( succ )+import Text.Show         ( Show, showString, showsPrec, showParen, showList )++{- | This type constructor indicates that a value from some ASCII superset is valid ASCII. The type parameter is the ASCII superset, which should be a type with an instance of either 'CharSuperset' or 'StringSuperset'.++For example, whereas a 'Data.Text.Text' value may contain a combination of ASCII and non-ASCII characters, a value of type @'ASCII' 'Data.Text.Text'@ may contain only ASCII characters.++-}++newtype ASCII superset = ASCII_Unsafe { lift :: superset }++deriving stock instance Eq superset => Eq (ASCII superset)++deriving stock instance Ord superset => Ord (ASCII superset)++deriving newtype instance Hashable superset => Hashable (ASCII superset)++deriving newtype instance Semigroup superset => Semigroup (ASCII superset)++deriving newtype instance Monoid superset => Monoid (ASCII superset)++deriving stock instance Data superset => Data (ASCII superset)++deriving stock instance Generic (ASCII superset)++instance Show superset => Show (ASCII superset)+  where+    showsPrec d x = showParen (d > app_prec) $+        showString "asciiUnsafe " . showsPrec (succ app_prec) (lift x)+      where app_prec = 10++    showList x = showString "asciiUnsafe " . showList (map lift x)++instance CharSuperset char => CharSuperset (ASCII char)+  where+    isAsciiChar _ = True+    fromChar = asciiUnsafe . S.fromChar+    toCharUnsafe = S.toCharUnsafe . lift++instance CharSuperset char => I.CharIso (ASCII char)+  where+    toChar = S.toCharUnsafe++instance StringSuperset string => StringSuperset (ASCII string)+  where+    isAsciiString _ = True+    fromCharList = asciiUnsafe . S.fromCharList+    toCharListUnsafe = S.toCharListUnsafe . lift+    toCharListSub = S.toCharListUnsafe . lift+    substituteString = id++instance StringSuperset string => I.StringIso (ASCII string)+  where+    toCharList = S.toCharListUnsafe+    mapChars = S.mapCharsUnsafe++asciiUnsafe :: superset -> ASCII superset+asciiUnsafe = ASCII_Unsafe++{- |++>>> map validateChar [-1, 65, 97, 128] :: [Maybe (ASCII Int)]+[Nothing,Just (asciiUnsafe 65),Just (asciiUnsafe 97),Nothing]++-}++validateChar :: CharSuperset superset => superset -> Maybe (ASCII superset)+validateChar x = if S.isAsciiChar x then Just (asciiUnsafe x) else Nothing++substituteChar :: CharSuperset superset => superset -> ASCII superset+substituteChar x = if S.isAsciiChar x then asciiUnsafe x else fromChar ASCII.Substitute++fromChar :: CharSuperset superset => ASCII.Char -> ASCII superset+fromChar = asciiUnsafe . S.fromChar++toChar :: CharSuperset superset => ASCII superset -> ASCII.Char+toChar = S.toCharUnsafe . lift++{- |++>>> fromCharList [CapitalLetterH,SmallLetterI,ExclamationMark] :: ASCII Text+asciiUnsafe "Hi!"++-}++fromCharList :: StringSuperset superset => [ASCII.Char] -> ASCII superset+fromCharList = asciiUnsafe . S.fromCharList++{- |++>>> toCharList (substituteString "Piñata" :: ASCII Text)+[CapitalLetterP,SmallLetterI,Substitute,SmallLetterA,SmallLetterT,SmallLetterA]++-}++toCharList :: StringSuperset superset => ASCII superset -> [ASCII.Char]+toCharList = S.toCharListUnsafe . lift++{- | Forces a string from a larger character set into ASCII by using the 'ASCII.Substitute' character in place of any non-ASCII characters.++>>> substituteString "Cristóbal" :: ASCII Text+asciiUnsafe "Crist\SUBbal"++-}++substituteString :: StringSuperset superset => superset -> ASCII superset+substituteString = asciiUnsafe . S.substituteString++{- |++>>> map validateString ["Hello", "Cristóbal"] :: [Maybe (ASCII Text)]+[Just (asciiUnsafe "Hello"),Nothing]++>>> map validateString ["Hello", "Cristóbal"] :: [Maybe (ASCII String)]+[Just (asciiUnsafe "Hello"),Nothing]++-}++validateString :: StringSuperset superset => superset -> Maybe (ASCII superset)+validateString x = if S.isAsciiString x then Just (asciiUnsafe x) else Nothing++asChar :: CharSuperset superset => (ASCII.Char -> ASCII.Char) -> ASCII superset -> ASCII superset+asChar f = asciiUnsafe . S.asCharUnsafe f . lift++mapChars :: StringSuperset superset => (ASCII.Char -> ASCII.Char) -> ASCII superset -> ASCII superset+mapChars f = asciiUnsafe . S.mapCharsUnsafe f . lift
+ library/ASCII/Superset.hs view
@@ -0,0 +1,199 @@+module ASCII.Superset (++    {- * Characters -}+    {- ** Class -} CharSuperset (..),+    {- ** Functions -} asCharUnsafe, toCharMaybe, toCharOrFail, toCharSub, substituteChar, convertCharMaybe, convertCharOrFail,++    {- * Strings -}+    {- ** Class -} StringSuperset (..),+    {- ** Functions -} toCharListMaybe, toCharListOrFail, convertStringMaybe, convertStringOrFail++    ) where++import Control.Monad      ( return )+import Control.Monad.Fail ( MonadFail (fail) )+import Data.Bool          ( Bool, (&&) )+import Data.Function      ( (.), id )+import Data.Functor       ( fmap )+import Data.Ord           ( (<=), (>=) )+import Data.Maybe         ( Maybe (..) )++import qualified ASCII.Char               as  ASCII+import qualified Data.Bool                as  Bool+import qualified Data.ByteString          as  BS+import qualified Data.ByteString.Lazy     as  LBS+import qualified Data.ByteString.Builder  as  BSB+import qualified Data.Char                as  Unicode+import qualified Data.Int                 as  Int+import qualified Data.List                as  List+import qualified Data.Text                as  T+import qualified Data.Text.Lazy           as  LT+import qualified Data.Text.Lazy.Builder   as  TB+import qualified Data.Word                as  Word+import qualified Numeric.Natural          as  Nat+import qualified Prelude+++---  Char  ---++class CharSuperset char+  where++    isAsciiChar :: char -> Bool++    fromChar :: ASCII.Char -> char++    toCharUnsafe :: char -> ASCII.Char++asCharUnsafe :: CharSuperset char => (ASCII.Char -> ASCII.Char) -> char -> char+asCharUnsafe f = fromChar . f . toCharUnsafe++toCharMaybe :: CharSuperset char => char -> Maybe ASCII.Char+toCharMaybe = toCharOrFail++toCharOrFail :: (CharSuperset char, MonadFail context) => char -> context ASCII.Char+toCharOrFail x = if isAsciiChar x then return (toCharUnsafe x) else fail "Not an ASCII character"++toCharSub :: CharSuperset char => char -> ASCII.Char+toCharSub x = if isAsciiChar x then toCharUnsafe x else ASCII.Substitute++substituteChar :: CharSuperset char => char -> char+substituteChar x = if isAsciiChar x then x else fromChar ASCII.Substitute++-- | Convert from one ASCII-superset character type to another via the ASCII 'ASCII.Char' type. Fails as 'Nothing' if the input is outside the ASCII character set.+convertCharMaybe :: (CharSuperset char1, CharSuperset char2) => char1 -> Maybe char2+convertCharMaybe = convertCharOrFail++-- | Convert from one ASCII-superset character type to another via the ASCII 'ASCII.Char' type. Fails with 'fail' if the input is outside the ASCII character set.+convertCharOrFail :: (CharSuperset char1, CharSuperset char2, MonadFail context) => char1 -> context char2+convertCharOrFail = fmap fromChar . toCharOrFail+++---  String  ---++class StringSuperset string+  where++    isAsciiString :: string -> Bool++    fromCharList :: [ASCII.Char] -> string++    toCharListUnsafe :: string -> [ASCII.Char]++    toCharListSub :: string -> [ASCII.Char]++    substituteString :: string -> string++    mapCharsUnsafe :: (ASCII.Char -> ASCII.Char) -> string -> string+    mapCharsUnsafe f = fromCharList  . List.map f . toCharListUnsafe++toCharListMaybe :: StringSuperset string => string -> Maybe [ASCII.Char]+toCharListMaybe = toCharListOrFail++toCharListOrFail :: (StringSuperset string, MonadFail context) => string -> context [ASCII.Char]+toCharListOrFail x = if isAsciiString x then return (toCharListUnsafe x) else fail "String contains non-ASCII characters"++-- | Convert from one ASCII-superset string type to another by converting each character of the input string to an ASCII 'ASCII.Char', and then converting the ASCII character list to the desired output type. Fails as 'Nothing' if the input contains any character that is outside the ASCII character set.+convertStringMaybe :: (StringSuperset string1, StringSuperset string2) => string1 -> Maybe string2+convertStringMaybe = convertStringOrFail++-- | Convert from one ASCII-superset string type to another by converting each character of the input string to an ASCII 'ASCII.Char', and then converting the ASCII character list to the desired output type. Fails with 'fail' if the input contains any character that is outside the ASCII character set.+convertStringOrFail :: (StringSuperset string1, StringSuperset string2, MonadFail context) => string1 -> context string2+convertStringOrFail = fmap fromCharList . toCharListOrFail+++---  Instances  ---++-- | 'ASCII.Char' is trivially a superset of itself. (This instance is uninteresting.)++instance CharSuperset ASCII.Char+  where+    isAsciiChar _ = Bool.True+    fromChar = id+    toCharUnsafe = id++instance CharSuperset Unicode.Char+  where+    isAsciiChar = (<= '\DEL')+    fromChar = Unicode.chr . ASCII.toInt+    toCharUnsafe = ASCII.fromIntUnsafe . Unicode.ord++instance CharSuperset Nat.Natural+  where+    isAsciiChar = (<= 127)+    fromChar = Prelude.fromIntegral . ASCII.toInt+    toCharUnsafe = ASCII.fromIntUnsafe . Prelude.fromIntegral++instance CharSuperset Int.Int+  where+    isAsciiChar x = (x >= 0) && (x <= 127)+    fromChar = ASCII.toInt+    toCharUnsafe = ASCII.fromIntUnsafe++instance CharSuperset Word.Word8+  where+    isAsciiChar = (<= 127)+    fromChar = Prelude.fromIntegral . ASCII.toInt+    toCharUnsafe = ASCII.fromIntUnsafe . Prelude.fromIntegral++instance CharSuperset char => StringSuperset [char]+  where+    isAsciiString = List.all isAsciiChar+    fromCharList = List.map fromChar+    toCharListUnsafe = List.map toCharUnsafe+    toCharListSub = List.map toCharSub+    substituteString = List.map substituteChar++instance StringSuperset T.Text+  where+    isAsciiString = T.all isAsciiChar+    fromCharList = T.pack . fromCharList+    toCharListUnsafe = toCharListUnsafe . T.unpack+    toCharListSub = toCharListSub . T.unpack+    substituteString = T.map substituteChar+    mapCharsUnsafe f = T.map (asCharUnsafe f)++instance StringSuperset LT.Text+  where+    isAsciiString = LT.all isAsciiChar+    fromCharList = LT.pack . fromCharList+    toCharListUnsafe = toCharListUnsafe . LT.unpack+    toCharListSub = toCharListSub . LT.unpack+    substituteString = LT.map substituteChar+    mapCharsUnsafe f = LT.map (asCharUnsafe f)++instance StringSuperset TB.Builder+  where+    isAsciiString = isAsciiString . TB.toLazyText+    fromCharList = TB.fromString . fromCharList+    toCharListUnsafe = toCharListUnsafe . TB.toLazyText+    toCharListSub = toCharListSub . TB.toLazyText+    substituteString = TB.fromLazyText . substituteString . TB.toLazyText+    mapCharsUnsafe f = TB.fromLazyText . mapCharsUnsafe f . TB.toLazyText++instance StringSuperset BS.ByteString+  where+    isAsciiString = BS.all isAsciiChar+    fromCharList = BS.pack . fromCharList+    toCharListUnsafe = toCharListUnsafe . BS.unpack+    toCharListSub = toCharListSub . BS.unpack+    substituteString = BS.map substituteChar+    mapCharsUnsafe f = BS.map (asCharUnsafe f)++instance StringSuperset LBS.ByteString+  where+    isAsciiString = LBS.all isAsciiChar+    fromCharList = LBS.pack . fromCharList+    toCharListUnsafe = toCharListUnsafe . LBS.unpack+    toCharListSub = toCharListSub . LBS.unpack+    substituteString = LBS.map substituteChar+    mapCharsUnsafe f = LBS.map (asCharUnsafe f)++instance StringSuperset BSB.Builder+  where+    isAsciiString = isAsciiString . BSB.toLazyByteString+    fromCharList = BSB.lazyByteString . fromCharList+    toCharListUnsafe = toCharListUnsafe . BSB.toLazyByteString+    toCharListSub = toCharListSub . BSB.toLazyByteString+    substituteString = BSB.lazyByteString . substituteString . BSB.toLazyByteString+    mapCharsUnsafe f = BSB.lazyByteString . mapCharsUnsafe f . BSB.toLazyByteString
+ test/test.hs view
@@ -0,0 +1,65 @@+module Main where++import ASCII.Refinement (ASCII, asciiUnsafe)++import qualified ASCII.Lift as Lift+import qualified ASCII.Refinement as Refinement++import ASCII.Char (Char (..))++import Control.Applicative (Applicative (..))+import Control.Monad (Monad (..))+import Data.Bool (Bool (..))+import Data.Eq (Eq ((==)))+import Data.Function ((.), ($))+import Data.Functor (Functor (..))+import Data.Int (Int)+import Data.List (intercalate, map, null)+import Data.Maybe (Maybe (..))+import Data.Semigroup ((<>))+import Data.String (String)+import Data.Text (Text)+import Data.Word (Word8)+import Numeric.Natural (Natural)+import System.Exit (die)+import System.IO (IO, putStrLn)+import Text.Show (show)++main :: IO ()+main = dieIfFailures $ do++    test 1 $ (Lift.lift CapitalLetterA :: Word8) == 65+    test 2 $ (Lift.lift [CapitalLetterH,SmallLetterI,ExclamationMark] :: Text) == "Hi!"++    test 3 $ (map Refinement.validateChar [-1, 65, 97, 128] :: [Maybe (ASCII Int)]) == [Nothing, Just (asciiUnsafe 65), Just (asciiUnsafe 97), Nothing]+    test 4 $ (Refinement.fromCharList [CapitalLetterH,SmallLetterI,ExclamationMark] :: ASCII Text) == asciiUnsafe "Hi!"+    test 5 $ Refinement.toCharList (Refinement.substituteString "Piñata" :: ASCII Text) == [CapitalLetterP, SmallLetterI, Substitute, SmallLetterA, SmallLetterT, SmallLetterA]+    test 6 $ (Refinement.substituteString "Cristóbal" :: ASCII Text) == asciiUnsafe "Crist\SUBbal"+    test 7 $ (map Refinement.validateString ["Hello", "Cristóbal"] :: [Maybe (ASCII Text)]) == [Just (asciiUnsafe "Hello"), Nothing]+    test 8 $ (map Refinement.validateString ["Hello", "Cristóbal"] :: [Maybe (ASCII String)]) == [Just (asciiUnsafe "Hello"), Nothing]++dieIfFailures :: Failures a -> IO a+dieIfFailures (Failures fs x) =+    if null fs+        then do putStrLn "💯"; return x+        else die $ intercalate " " (map (("🔥" <> ) . show) fs)++type TestNumber = Natural++test :: TestNumber -> Bool -> Failures ()+test n t = Failures (if t then [] else [n]) ()++data Failures a = Failures [TestNumber] a++instance Functor Failures+  where+    fmap f (Failures a x) = Failures a (f x)++instance Applicative Failures+  where+    pure x = Failures [] x+    Failures a f <*> Failures b x = Failures (a <> b) (f x)++instance Monad Failures+  where+    Failures a x >>= f = let Failures b y = f x in Failures (a <> b) y