ascii-superset 1.2.1.0 → 1.2.2.0
raw patch · 5 files changed
+109/−73 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ ASCII.CaseRefinement: instance ASCII.Superset.CharSuperset char => ASCII.Superset.ToChar (ASCII.CaseRefinement.ASCII'case letterCase char)
+ ASCII.CaseRefinement: instance ASCII.Superset.ToCaselessChar char => ASCII.Superset.ToCaselessChar (ASCII.CaseRefinement.ASCII'case letterCase char)
+ ASCII.CaseRefinement: instance ASCII.Superset.ToCaselessString string => ASCII.Superset.ToCaselessString (ASCII.CaseRefinement.ASCII'case letterCase string)
+ ASCII.CaseRefinement: instance ASCII.Superset.ToString string => ASCII.Superset.ToString (ASCII.CaseRefinement.ASCII'case letterCase string)
Files
- ascii-superset.cabal +4/−4
- changelog.md +9/−0
- library/ASCII/CaseRefinement.hs +52/−37
- library/ASCII/Lift.hs +13/−11
- library/ASCII/Refinement.hs +31/−21
ascii-superset.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 name: ascii-superset-version: 1.2.1.0+version: 1.2.2.0 synopsis: Representing ASCII with refined supersets category: Data, Text @@ -46,7 +46,7 @@ TypeApplications build-depends:- ascii-case ^>= 1.0.1+ , ascii-case ^>= 1.0.1 , ascii-caseless ^>= 0.0.0 , ascii-char ^>= 1.0 , base ^>= 4.14 || ^>= 4.15 || ^>= 4.16 || ^>= 4.17@@ -65,7 +65,7 @@ StandaloneDeriving build-depends:- bytestring ^>= 0.10.12 || ^>= 0.11+ , bytestring ^>= 0.10.12 || ^>= 0.11 , hashable ^>= 1.3.5 || ^>= 1.4 exposed-modules:@@ -87,5 +87,5 @@ TemplateHaskell build-depends:- ascii-superset+ , ascii-superset , hspec ^>= 2.8.5 || ^>= 2.9 || ^>= 2.10
changelog.md view
@@ -1,3 +1,12 @@+### 1.2.2.0 (2023-01-05)++`ASCII'case` now has instances for:++* `ToCaselessChar`+* `ToChar`+* `ToCaselessString`+* `ToString`+ ### 1.2.1.0 (2023-01-05) Add `instance FromChar CaselessChar`
library/ASCII/CaseRefinement.hs view
@@ -18,7 +18,7 @@ import qualified ASCII.Case as Case import qualified ASCII.Caseless as Caseless import qualified ASCII.Char as ASCII-import qualified ASCII.Superset as Superset+import qualified ASCII.Superset as S import {-# source #-} qualified ASCII.Refinement as Refinement import Control.Monad (guard)@@ -40,9 +40,9 @@ import qualified Data.Bool as Bool import qualified Data.List as List -{- | This type constructor indicates that a value from some ASCII superset is-valid ASCII, and also that any letters belong to a particular 'Case' indicated-by the @letterCase@ type parameter.+{-| Indicates that a value from some ASCII superset is valid ASCII, and also+ that any letters belong to a particular 'Case' indicated by the @letterCase@+ type parameter The @superset@ type parameter is the ASCII superset, which should be a type with an instance of either 'CharSuperset' or 'StringSuperset'.@@ -84,8 +84,26 @@ showList x = showString "asciiCaseUnsafe " . showList (List.map lift x) +instance S.ToCaselessChar char => S.ToCaselessChar (ASCII'case letterCase char) where+ isAsciiCaselessChar _ = Bool.True+ toCaselessCharUnsafe = S.toCaselessCharUnsafe . lift++instance S.CharSuperset char => S.ToChar (ASCII'case letterCase char) where+ isAsciiChar _ = Bool.True+ toCharUnsafe = S.toCharUnsafe . lift++instance S.ToCaselessString string => S.ToCaselessString (ASCII'case letterCase string) where+ isAsciiCaselessString _ = Bool.True+ toCaselessCharListUnsafe = S.toCaselessCharListUnsafe . lift+ toCaselessCharListSub = S.toCaselessCharListSub . lift++instance S.ToString string => S.ToString (ASCII'case letterCase string) where+ isAsciiString _ = Bool.True+ toCharListUnsafe = S.toCharListUnsafe . lift+ toCharListSub = S.toCharListUnsafe . lift+ {-| Change the type of an ASCII superset value that is known to be valid ASCII where-letters are restricted to the 'Case' designated by the @letterCase@ type variable.+ letters are restricted to the 'Case' designated by the @letterCase@ type variable This is "unsafe" because this assertion is unchecked, so this function is capable of producing an invalid 'ASCII'case' value. -}@@ -97,10 +115,7 @@ {- $aliases The 'ASCII'upper' and 'ASCII'lower' type aliases exist primarily so that you can-use 'ASCII'case' without the DataKinds language extension.---}-+use 'ASCII'case' without the DataKinds language extension. -} type ASCII'upper superset = ASCII'case 'UpperCase superset type ASCII'lower superset = ASCII'case 'LowerCase superset@@ -114,104 +129,104 @@ --- {-| Return 'Just' an 'ASCII'case' character if the input is an ASCII character-in the proper case, or 'Nothing' otherwise -}+ in the proper case, or 'Nothing' otherwise -} validateChar :: forall letterCase superset. KnownCase letterCase => CharSuperset superset => superset {- ^ Character which may or may not be in the ASCII character set; if a letter, may be in any case -} -> Maybe (ASCII'case letterCase superset) validateChar x = do- c <- Superset.toCharMaybe x+ c <- S.toCharMaybe x guard (Bool.not (Case.isCase (Case.opposite (theCase @letterCase)) c)) Just (asciiCaseUnsafe x) -{- | Return an 'ASCII'case' character if the input is an ASCII character in the-proper case, or 'ASCII.Substitute' otherwise -}+{-| Return an 'ASCII'case' character if the input is an ASCII character in the+ proper case, or 'ASCII.Substitute' otherwise -} substituteChar :: forall letterCase superset. KnownCase letterCase => CharSuperset superset => superset -> ASCII'case letterCase superset substituteChar x = case validateChar x of- Nothing -> asciiCaseUnsafe (Superset.fromChar ASCII.Substitute)+ Nothing -> asciiCaseUnsafe (S.fromChar ASCII.Substitute) Just c -> c {-| Lift a 'CaselessChar' into a superset type, wrapped in the 'ASCII'case'-refinement to save the evidence that it is ASCII in a particular case -}+ refinement to save the evidence that it is ASCII in a particular case -} fromCaselessChar :: forall letterCase superset. KnownCase letterCase => CharSuperset superset => CaselessChar -- ^ Character which, if it is a letter, does not have a specified case -> ASCII'case letterCase superset-fromCaselessChar = asciiCaseUnsafe . Superset.fromChar . Caseless.toCase (theCase @letterCase)+fromCaselessChar = asciiCaseUnsafe . S.fromChar . Caseless.toCase (theCase @letterCase) {-| Given a character from some type that is known to represent an ASCII-character in a particular case, obtain the caseless ASCII character it-represents -}+ character in a particular case, obtain the caseless ASCII character+ it represents -} toCaselessChar :: CharSuperset superset => ASCII'case letterCase superset {- ^ Character that is known to be ASCII, and in the particular case if it is a letter -} -> CaselessChar-toCaselessChar = Caseless.disregardCase . Superset.toCharUnsafe . lift+toCaselessChar = Caseless.disregardCase . S.toCharUnsafe . lift {-| Given a character from a larger set that is known to represent an ASCII-character, manipulate it as if it were an ASCII character -}+ character, manipulate it as if it were an ASCII character -} asCaselessChar :: forall letterCase superset. KnownCase letterCase => CharSuperset superset => (CaselessChar -> CaselessChar) -- ^ Case-insensitive function over ASCII characters -> ASCII'case letterCase superset {- ^ Character that is known to be ASCII, and in the particular case if it is a letter -} -> ASCII'case letterCase superset-asCaselessChar f = asciiCaseUnsafe . Superset.asCharUnsafe g . lift+asCaselessChar f = asciiCaseUnsafe . S.asCharUnsafe g . lift where g = Caseless.toCase (theCase @letterCase) . f . Caseless.assumeCaseUnsafe (theCase @letterCase) {-| Given an ASCII superset character that is known to be valid ASCII,-refine it further by converting it to a particular letter case -}+ refine it further by converting it to a particular letter case -} refineCharToCase :: forall letterCase char. KnownCase letterCase => CharSuperset char => ASCII char -> ASCII'case letterCase char-refineCharToCase = asciiCaseUnsafe . Superset.toCaseChar (theCase @letterCase) . Refinement.lift+refineCharToCase = asciiCaseUnsafe . S.toCaseChar (theCase @letterCase) . Refinement.lift --- {-| Return 'Just' an 'ASCII'case' string if the input consists entirely of ASCII-characters in the proper case, or 'Nothing' otherwise -}+ characters in the proper case, or 'Nothing' otherwise -} validateString :: forall letterCase superset. KnownCase letterCase => StringSuperset superset => superset -- ^ String which may or may not be valid ASCII, where letters may be in any case -> Maybe (ASCII'case letterCase superset) validateString x = do- s <- Superset.toCharListMaybe x+ s <- S.toCharListMaybe x guard (Bool.not (any (Case.isCase (Case.opposite (theCase @letterCase))) s)) Just (asciiCaseUnsafe x) {-| Lift a list of 'CaselessChar' into a superset string type, wrapped in the-'ASCII'case' refinement to save the evidence that all of the characters in the-string are ASCII in a particular case. -}+ 'ASCII'case' refinement to save the evidence that all of the characters in+ the string are ASCII in a particular case -} fromCaselessCharList :: forall letterCase superset. KnownCase letterCase => StringSuperset superset => [CaselessChar] -- ^ Case-insensitive ASCII string represented as a list of caseless characters -> ASCII'case letterCase superset-fromCaselessCharList = asciiCaseUnsafe . Superset.fromCharList . List.map (Caseless.toCase (theCase @letterCase))+fromCaselessCharList = asciiCaseUnsafe . S.fromCharList . List.map (Caseless.toCase (theCase @letterCase)) -{-| Given a string from some type that is known to represent only ASCII-characters in a particular case, obtain the caseless characters it represents -}+{-| Given a string from some type that is known to represent only ASCII characters+ in a particular case, obtain the caseless characters it represents -} toCaselessCharList :: forall letterCase superset. KnownCase letterCase => StringSuperset superset => ASCII'case letterCase superset -- ^ String that is known to be valid ASCII in a particular case -> [CaselessChar]-toCaselessCharList = List.map (Caseless.assumeCaseUnsafe (theCase @letterCase)) . Superset.toCharListUnsafe . lift+toCaselessCharList = List.map (Caseless.assumeCaseUnsafe (theCase @letterCase)) . S.toCharListUnsafe . lift {-| Forces a string from a larger character set into cased ASCII by using the-'ASCII.Substitute' character in place of any unacceptable characters. -}+ 'ASCII.Substitute' character in place of any unacceptable characters -} substituteString :: forall letterCase superset. KnownCase letterCase => StringSuperset superset => superset -- ^ String which may or may not be valid ASCII, where letters may be in any case -> ASCII'case letterCase superset-substituteString = asciiCaseUnsafe . Superset.fromCharList . List.map f . Superset.toCharListSub+substituteString = asciiCaseUnsafe . S.fromCharList . List.map f . S.toCharListSub where f x = if Case.isCase (Case.opposite (theCase @letterCase)) x then ASCII.Substitute else x {-| Given a string from a larger set that is known to consist entirely of ASCII-characters in a particular case, map over the characters in the string as if-they were caseless ASCII characters -}+ characters in a particular case, map over the characters in the string as if+ they were caseless ASCII characters -} mapChars :: forall letterCase superset. KnownCase letterCase => StringSuperset superset => (CaselessChar -> CaselessChar) -- ^ Case-insensitive function over ASCII characters -> ASCII'case letterCase superset -- ^ String that is known to be valid ASCII in a particular case -> ASCII'case letterCase superset-mapChars f = asciiCaseUnsafe . Superset.mapCharsUnsafe g . lift+mapChars f = asciiCaseUnsafe . S.mapCharsUnsafe g . lift where g = Caseless.toCase (theCase @letterCase) . f . Caseless.assumeCaseUnsafe (theCase @letterCase) @@ -219,4 +234,4 @@ refine it further by converting it to a particular letter case -} refineStringToCase :: forall letterCase char. KnownCase letterCase => StringSuperset char => ASCII char -> ASCII'case letterCase char-refineStringToCase = asciiCaseUnsafe . Superset.toCaseString (theCase @letterCase) . Refinement.lift+refineStringToCase = asciiCaseUnsafe . S.toCaseString (theCase @letterCase) . Refinement.lift
library/ASCII/Lift.hs view
@@ -1,3 +1,10 @@+{-|++@+(lift CapitalLetterA :: Word8) == 65++(lift [CapitalLetterH, SmallLetterI, ExclamationMark] :: Text) == "Hi!"+@ -} module ASCII.Lift (Lift (..)) where import ASCII.Case (Case (..))@@ -6,11 +13,12 @@ import ASCII.Refinement (ASCII) import ASCII.Superset (CharSuperset, StringSuperset) -import qualified ASCII.Refinement as Refinement-import qualified ASCII.Superset as Superset import qualified ASCII.CaseRefinement as CaseRefinement+import qualified ASCII.Refinement as Refinement+import qualified ASCII.Superset as S import qualified Prelude+ import Data.Function ((.)) {-| Embedding of one character set within another@@ -19,13 +27,7 @@ ASCII, some subset of ASCII, or some superset of ASCII. -} class Lift subset superset where - {-| Converts from a smaller to a larger type.-- >>> lift CapitalLetterA :: Word8- 65-- >>> lift [CapitalLetterH,SmallLetterI,ExclamationMark] :: Text- "Hi!"+ {-| Converts from a smaller to a larger type Due to the highly polymorphic nature of the 'lift' function, often it must used with an explicit type signature or type@@ -43,9 +45,9 @@ {-| 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 = Superset.fromChar+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 = Superset.fromCharList+instance StringSuperset superset => Lift [Char] superset where lift = S.fromCharList
library/ASCII/Refinement.hs view
@@ -26,10 +26,12 @@ import qualified Data.Bool as Bool import qualified Text.Show as Show -{-| 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'.+{-| 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. -}@@ -102,9 +104,11 @@ asciiUnsafe = ASCII_Unsafe {-|->>> map validateChar [-1, 65, 97, 128] :: [Maybe (ASCII Int)]-[Nothing,Just (asciiUnsafe 65),Just (asciiUnsafe 97),Nothing]--}++@+(map validateChar [-1, 65, 97, 128] :: [Maybe (ASCII Int)])+ == [Nothing, Just (asciiUnsafe 65), Just (asciiUnsafe 97), Nothing]+@ -} validateChar :: S.CharSuperset superset => superset -> Maybe (ASCII superset) validateChar x = if S.isAsciiChar x then Just (asciiUnsafe x) else Nothing @@ -118,35 +122,41 @@ toChar = S.toCharUnsafe . lift {-|->>> fromCharList [CapitalLetterH,SmallLetterI,ExclamationMark] :: ASCII Text-asciiUnsafe "Hi!"--}++@+fromCharList [CapitalLetterH, SmallLetterI, ExclamationMark]+ == (asciiUnsafe "Hi!" :: ASCII Text)+@ -} fromCharList :: S.StringSuperset superset => [ASCII.Char] -> ASCII superset fromCharList = asciiUnsafe . S.fromCharList {-|->>> toCharList (substituteString "Piñata" :: ASCII Text)-[CapitalLetterP,SmallLetterI,Substitute,SmallLetterA,SmallLetterT,SmallLetterA]--}++@+toCharList (substituteString \"Piñata" :: ASCII Text) ==+ [CapitalLetterP, SmallLetterI, Substitute, SmallLetterA, SmallLetterT, SmallLetterA]+@ -} toCharList :: S.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+ 'ASCII.Substitute' character in place of any non-ASCII characters ->>> substituteString "Cristóbal" :: ASCII Text-asciiUnsafe "Crist\SUBbal"--}+@+(substituteString \"Cristóbal" :: ASCII Text) == asciiUnsafe "Crist\SUBbal"+@ -} substituteString :: S.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]--}+@+(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 :: S.StringSuperset superset => superset -> Maybe (ASCII superset) validateString x = if S.isAsciiString x then Just (asciiUnsafe x) else Nothing