ttc 0.3.0.0 → 0.4.0.0
raw patch · 7 files changed
+433/−47 lines, 7 filesdep ~template-haskellPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: template-haskell
API changes (from Hackage documentation)
+ Data.TTC: renderBSB :: Render a => a -> Builder
+ Data.TTC: renderSBS :: Render a => a -> ShortByteString
+ Data.TTC: renderTLB :: Render a => a -> Builder
- Data.TTC: parseEnum' :: (Bounded a, Enum a, Render a, Textual t) => String -> Bool -> Bool -> t -> Either String a
+ Data.TTC: parseEnum' :: (Bounded a, Enum a, Render a, Textual t, Textual e) => String -> Bool -> Bool -> t -> Either e a
Files
- CHANGELOG.md +16/−0
- LICENSE +1/−1
- README.md +4/−4
- src/Data/TTC.hs +375/−34
- src/Data/TTC/Instances.hs +2/−2
- test/Data/TTC/Test.hs +19/−0
- ttc.cabal +16/−6
CHANGELOG.md view
@@ -24,6 +24,22 @@ [KaC]: <https://keepachangelog.com/en/1.0.0/> +## 0.4.0.0 (2021-03-27)++### Breaking++* Add support for GHC 9+* Add `renderTLB`, `renderBSB`, and `renderSBS` functions+* Use `Textual` error messages for `parseEnum'`++### Non-Breaking++* Add `@since` annotations+* Rename Git default branch to `main`+* Use GitHub Actions instead of Travis CI+* Add Cabal tests to GitHub Actions+* Add [stan](https://hackage.haskell.org/package/stan) static analysis+ ## 0.3.0.0 (2020-11-03) ### Breaking
LICENSE view
@@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2019-2020 Travis Cardwell+Copyright (c) 2019-2021 Travis Cardwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal
README.md view
@@ -1,6 +1,6 @@ # TTC: Textual Type Classes -[](https://travis-ci.com/ExtremaIS/ttc-haskell)+[](https://github.com/ExtremaIS/ttc-haskell/actions) [](https://hackage.haskell.org/package/ttc) [](https://stackage.org/package/ttc) [](https://stackage.org/nightly/package/ttc)@@ -100,7 +100,7 @@ ```haskell instance TTC.Parse Username where- parse = TTC.asT $ \t-> first TTC.fromS $ do+ parse = TTC.asT $ \t -> first TTC.fromS $ do unless (T.all isAsciiLower t) $ Left "username has invalid character(s)" let len = T.length t when (len < 3) $ Left "username has fewer than 3 characters"@@ -212,7 +212,7 @@ * Hackage: <https://hackage.haskell.org/package/ttc> * Stackage: <https://stackage.org/package/ttc> * GitHub: <https://github.com/ExtremaIS/ttc-haskell>-* Travis CI: <https://travis-ci.com/ExtremaIS/ttc-haskell>+* GitHub Actions CI: <https://github.com/ExtremaIS/ttc-haskell/actions> ### Dependencies @@ -223,7 +223,7 @@ ### Releases -All releases are tagged in the `master` branch. Release tags are signed using+All releases are tagged in the `main` branch. Release tags are signed using the [`security@extrema.is` GPG key](http://keys.gnupg.net/pks/lookup?op=vindex&fingerprint=on&search=0x1D484E4B4705FADF).
src/Data/TTC.hs view
@@ -2,7 +2,7 @@ -- | -- Module : Data.TTC -- Description : textual type classes--- Copyright : Copyright (c) 2019-2020 Travis Cardwell+-- Copyright : Copyright (c) 2019-2021 Travis Cardwell -- License : MIT -- -- TTC, an initialism of /Textual Type Classes/, is a library that provides@@ -16,9 +16,14 @@ -- @ ------------------------------------------------------------------------------ +{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TemplateHaskell #-} +#if __GLASGOW_HASKELL__ >= 900+{-# LANGUAGE ExplicitForAll #-}+#endif+ module Data.TTC ( -- * Textual Textual@@ -61,6 +66,9 @@ , renderTL , renderBS , renderBSL+ , renderTLB+ , renderBSB+ , renderSBS -- ** Render Utilities , renderWithShow -- * Parse@@ -97,6 +105,7 @@ , readsEnum , readsWithParse -- ** Constant Validation+ -- $ParseValid , valid , validOf , mkValid@@ -153,23 +162,37 @@ -- writing instances. Adding support for additional data types would require -- changing the class definition itself. This is the price paid for having -- only one type variable instead of two.+--+-- @since 0.1.0.0 class Textual t where -- | Convert to a 'String'+ --+ -- @since 0.1.0.0 toS :: t -> String -- | Convert to strict 'T.Text'+ --+ -- @since 0.1.0.0 toT :: t -> T.Text -- | Convert to lazy 'TL.Text'+ --+ -- @since 0.1.0.0 toTL :: t -> TL.Text -- | Convert to a strict 'BS.ByteString'+ --+ -- @since 0.1.0.0 toBS :: t -> BS.ByteString -- | Convert to a lazy 'BS.ByteString'+ --+ -- @since 0.1.0.0 toBSL :: t -> BSL.ByteString -- | Convert between any supported textual data types+ --+ -- @since 0.1.0.0 convert :: Textual t' => t' -> t instance Textual String where@@ -242,6 +265,7 @@ {-# INLINE toBSL #-} {-# INLINE convert #-} +------------------------------------------------------------------------------ -- $TextualTo -- -- These functions are equivalent to 'convert', but they specify the type@@ -255,30 +279,41 @@ -- in cases where the type is ambiguous. -- | Convert from a 'String'+--+-- @since 0.1.0.0 fromS :: Textual t => String -> t fromS = convert {-# INLINE fromS #-} -- | Convert from strict 'T.Text'+--+-- @since 0.1.0.0 fromT :: Textual t => T.Text -> t fromT = convert {-# INLINE fromT #-} -- | Convert from lazy 'TL.Text'+--+-- @since 0.1.0.0 fromTL :: Textual t => TL.Text -> t fromTL = convert {-# INLINE fromTL #-} -- | Convert from a strict 'BS.ByteString'+--+-- @since 0.1.0.0 fromBS :: Textual t => BS.ByteString -> t fromBS = convert {-# INLINE fromBS #-} -- | Convert from a lazy 'BSL.ByteString'+--+-- @since 0.1.0.0 fromBSL :: Textual t => BSL.ByteString -> t fromBSL = convert {-# INLINE fromBSL #-} +------------------------------------------------------------------------------ -- $TextualAs -- -- These functions are used to convert a 'Textual' argument of a function to a@@ -286,30 +321,41 @@ -- definitions. -- | Convert an argument to a 'String'+--+-- @since 0.1.0.0 asS :: Textual t => (String -> a) -> t -> a asS f = f . convert {-# INLINE asS #-} -- | Convert an argument to strict 'T.Text'+--+-- @since 0.1.0.0 asT :: Textual t => (T.Text -> a) -> t -> a asT f = f . convert {-# INLINE asT #-} -- | Convert an argument to lazy 'TL.Text'+--+-- @since 0.1.0.0 asTL :: Textual t => (TL.Text -> a) -> t -> a asTL f = f . convert {-# INLINE asTL #-} -- | Convert an argument to a strict 'BS.ByteString'+--+-- @since 0.1.0.0 asBS :: Textual t => (BS.ByteString -> a) -> t -> a asBS f = f . convert {-# INLINE asBS #-} -- | Convert an argument to a lazy 'BSL.ByteString'+--+-- @since 0.1.0.0 asBSL :: Textual t => (BSL.ByteString -> a) -> t -> a asBSL f = f . convert {-# INLINE asBSL #-} +------------------------------------------------------------------------------ -- $TextualOther -- -- These functions are used to convert to/from the following other textual@@ -320,26 +366,38 @@ -- * 'SBS.ShortByteString' (@SBS@) -- | Convert to a @Text@ 'TLB.Builder'+--+-- @since 0.1.0.0 toTLB :: Textual t => t -> TLB.Builder toTLB = TLB.fromLazyText . convert -- | Convert from a @Text@ 'TLB.Builder'+--+-- @since 0.1.0.0 fromTLB :: Textual t => TLB.Builder -> t fromTLB = convert . TLB.toLazyText -- | Convert to a @ByteString@ 'BSB.Builder'+--+-- @since 0.1.0.0 toBSB :: Textual t => t -> BSB.Builder toBSB = BSB.lazyByteString . convert -- | Convert from a @ByteString@ 'BSB.Builder'+--+-- @since 0.1.0.0 fromBSB :: Textual t => BSB.Builder -> t fromBSB = convert . BSB.toLazyByteString -- | Convert to a 'SBS.ShortByteString'+--+-- @since 0.1.0.0 toSBS :: Textual t => t -> SBS.ShortByteString toSBS = SBS.toShort . convert -- | Convert from a 'SBS.ShortByteString'+--+-- @since 0.1.0.0 fromSBS :: Textual t => SBS.ShortByteString -> t fromSBS = convert . SBS.fromShort @@ -353,9 +411,12 @@ -- basic data types are available in "Data.TTC.Instances". -- -- See the @uname@ and @prompt@ example programs in the @examples@ directory.+--+-- @since 0.1.0.0 class Render a where render :: Textual t => a -> t +------------------------------------------------------------------------------ -- $RenderSpecific -- -- These functions are equivalent to 'render', but they specify the type being@@ -363,33 +424,67 @@ -- where the type is ambiguous. -- | Render to a 'String'+--+-- @since 0.1.0.0 renderS :: Render a => a -> String renderS = render {-# INLINE renderS #-} -- | Render to strict 'T.Text'+--+-- @since 0.1.0.0 renderT :: Render a => a -> T.Text renderT = render {-# INLINE renderT #-} -- | Render to lazy 'TL.Text'+--+-- @since 0.1.0.0 renderTL :: Render a => a -> TL.Text renderTL = render {-# INLINE renderTL #-} -- | Render to a strict 'BS.ByteString'+--+-- @since 0.1.0.0 renderBS :: Render a => a -> BS.ByteString renderBS = render {-# INLINE renderBS #-} -- | Render to a lazy 'BSL.ByteString'+--+-- @since 0.1.0.0 renderBSL :: Render a => a -> BSL.ByteString renderBSL = render {-# INLINE renderBSL #-} +-- | Render to a @Text@ 'TLB.Builder'+--+-- @since 0.4.0.0+renderTLB :: Render a => a -> TLB.Builder+renderTLB = TLB.fromLazyText . renderTL+{-# INLINE renderTLB #-}++-- | Render to a @ByteString@ 'BSB.Builder'+--+-- @since 0.4.0.0+renderBSB :: Render a => a -> BSB.Builder+renderBSB = BSB.lazyByteString . renderBSL+{-# INLINE renderBSB #-}++-- | Render to a 'SBS.ShortByteString'+--+-- @since 0.4.0.0+renderSBS :: Render a => a -> SBS.ShortByteString+renderSBS = SBS.toShort . renderBS+{-# INLINE renderSBS #-}++------------------------------------------------------------------------------ -- $RenderUtils -- | Render a value to a textual data type using the 'Show' instance+--+-- @since 0.1.0.0 renderWithShow :: (Show a, Textual t) => a -> t renderWithShow = convert . show {-# INLINE renderWithShow #-}@@ -404,15 +499,20 @@ -- basic data types are available in "Data.TTC.Instances". -- -- See the @uname@ and @prompt@ example programs in the @examples@ directory.+--+-- @since 0.3.0.0 class Parse a where parse :: (Textual t, Textual e) => t -> Either e a -- This function is equivalent to 'parse' with the error type fixed to -- 'String', used internally when the error is ignored.+--+-- @since 0.3.0.0 parse' :: (Parse a, Textual t) => t -> Either String a parse' = parse {-# INLINE parse' #-} +------------------------------------------------------------------------------ -- $ParseSpecific -- -- These functions are equivalent to 'parse', but they specify the type being@@ -420,30 +520,41 @@ -- where the type is ambiguous. -- | Parse from a 'String'+--+-- @since 0.3.0.0 parseS :: (Parse a, Textual e) => String -> Either e a parseS = parse {-# INLINE parseS #-} -- | Parse from strict 'T.Text'+--+-- @since 0.3.0.0 parseT :: (Parse a, Textual e) => T.Text -> Either e a parseT = parse {-# INLINE parseT #-} -- | Parse from lazy 'TL.Text'+--+-- @since 0.3.0.0 parseTL :: (Parse a, Textual e) => TL.Text -> Either e a parseTL = parse {-# INLINE parseTL #-} -- | Parse from a strict 'BS.ByteString'+--+-- @since 0.3.0.0 parseBS :: (Parse a, Textual e) => BS.ByteString -> Either e a parseBS = parse {-# INLINE parseBS #-} -- | Parse from a lazy 'BSL.ByteString'+--+-- @since 0.3.0.0 parseBSL :: (Parse a, Textual e) => BSL.ByteString -> Either e a parseBSL = parse {-# INLINE parseBSL #-} +------------------------------------------------------------------------------ -- $ParseMaybe -- -- The 'parseMaybe' function parses to a 'Maybe' type instead of an 'Either'@@ -452,35 +563,48 @@ -- annotations in cases where the type is ambiguous. -- | Parse to a 'Maybe' type+--+-- @since 0.3.0.0 parseMaybe :: (Parse a, Textual t) => t -> Maybe a parseMaybe = either (const Nothing) Just . parse' {-# INLINE parseMaybe #-} -- | Parse from a 'String' to a 'Maybe' type+--+-- @since 0.3.0.0 parseMaybeS :: Parse a => String -> Maybe a parseMaybeS = parseMaybe {-# INLINE parseMaybeS #-} -- | Parse from strict 'T.Text' to a 'Maybe' type+--+-- @since 0.3.0.0 parseMaybeT :: Parse a => T.Text -> Maybe a parseMaybeT = parseMaybe {-# INLINE parseMaybeT #-} -- | Parse from lazy 'TL.Text' to a 'Maybe' type+--+-- @since 0.3.0.0 parseMaybeTL :: Parse a => TL.Text -> Maybe a parseMaybeTL = parseMaybe {-# INLINE parseMaybeTL #-} -- | Parse from a strict 'BS.ByteString' to a 'Maybe' type+--+-- @since 0.3.0.0 parseMaybeBS :: Parse a => BS.ByteString -> Maybe a parseMaybeBS = parseMaybe {-# INLINE parseMaybeBS #-} -- | Parse from a lazy 'BSL.ByteString' to a 'Maybe' type+--+-- @since 0.3.0.0 parseMaybeBSL :: Parse a => BSL.ByteString -> Maybe a parseMaybeBSL = parseMaybe {-# INLINE parseMaybeBSL #-} +------------------------------------------------------------------------------ -- $ParseUnsafe -- -- The 'parseUnsafe' function raises an exception on error instead of using an@@ -490,35 +614,48 @@ -- in cases where the type is ambiguous. -- | Unsafely parse+--+-- @since 0.1.0.0 parseUnsafe :: (Parse a, Textual t) => t -> a parseUnsafe = either (error . ("parseUnsafe: " ++)) id . parse {-# INLINE parseUnsafe #-} -- | Unsafely parse to a 'String'+--+-- @since 0.1.0.0 parseUnsafeS :: Parse a => String -> a parseUnsafeS = parseUnsafe {-# INLINE parseUnsafeS #-} -- | Unsafely parse to strict 'T.Text'+--+-- @since 0.1.0.0 parseUnsafeT :: Parse a => T.Text -> a parseUnsafeT = parseUnsafe {-# INLINE parseUnsafeT #-} -- | Unsafely parse to lazy 'TL.Text'+--+-- @since 0.1.0.0 parseUnsafeTL :: Parse a => TL.Text -> a parseUnsafeTL = parseUnsafe {-# INLINE parseUnsafeTL #-} -- | Unsafely parse to a strict 'BS.ByteString'+--+-- @since 0.1.0.0 parseUnsafeBS :: Parse a => BS.ByteString -> a parseUnsafeBS = parseUnsafe {-# INLINE parseUnsafeBS #-} -- | Unsafely parse to a lazy 'BSL.ByteString'+--+-- @since 0.1.0.0 parseUnsafeBSL :: Parse a => BSL.ByteString -> a parseUnsafeBSL = parseUnsafe {-# INLINE parseUnsafeBSL #-} +------------------------------------------------------------------------------ -- $ParseUtils -- | Parse a value in an enumeration@@ -527,6 +664,8 @@ -- the implementation uses a linear algorithm. -- -- See the @enum@ example program in the @examples@ directory.+--+-- @since 0.1.0.0 parseEnum :: (Bounded a, Enum a, Render a, Textual t) => Bool -- ^ case-insensitive when 'True'@@ -540,7 +679,7 @@ in case [v | v <- [minBound ..], t' `match` norm (render v)] of [v] -> Right v [] -> Left invalidError- _ -> Left ambiguousError+ _vs -> Left ambiguousError where norm :: T.Text -> T.Text norm = if allowCI then T.toLower else id@@ -548,24 +687,31 @@ match :: T.Text -> T.Text -> Bool match = if allowPrefix then T.isPrefixOf else (==) --- | Parse a value in an enumeration, with 'String' error messages+-- | Parse a value in an enumeration, with 'Textual' error messages -- -- The following English error messages are returned: -- -- * \"invalid {name}\" when there are no matches -- * \"ambiguous {name}\" when there is more than one match+--+-- @since 0.4.0.0 parseEnum'- :: (Bounded a, Enum a, Render a, Textual t)- => String -- ^ name to include in error messages- -> Bool -- ^ case-insensitive when 'True'- -> Bool -- ^ accept unique prefixes when 'True'- -> t -- ^ textual input to parse- -> Either String a -- ^ error or parsed value+ :: (Bounded a, Enum a, Render a, Textual t, Textual e)+ => String -- ^ name to include in error messages+ -> Bool -- ^ case-insensitive when 'True'+ -> Bool -- ^ accept unique prefixes when 'True'+ -> t -- ^ textual input to parse+ -> Either e a -- ^ error or parsed value parseEnum' name allowCI allowPrefix =- parseEnum allowCI allowPrefix ("invalid " ++ name) ("ambiguous " ++ name)+ parseEnum+ allowCI allowPrefix+ (fromS $ "invalid " ++ name)+ (fromS $ "ambiguous " ++ name) {-# INLINEABLE parseEnum' #-} -- | Parse a value using the 'Read' instance+--+-- @since 0.1.0.0 parseWithRead :: (Read a, Textual t) => e -- ^ invalid input error@@ -574,11 +720,13 @@ parseWithRead invalidError = maybe (Left invalidError) Right . readMaybe . toS {-# INLINEABLE parseWithRead #-} --- | Parse a value using the 'Read' instance, with 'String' error messages+-- | Parse a value using the 'Read' instance, with 'Textual' error messages -- -- The following English error message is returned: -- -- * \"invalid {name}\" when the parse fails+--+-- @since 0.3.0.0 parseWithRead' :: (Read a, Textual t, Textual e) => String -- ^ name to include in error messages@@ -588,6 +736,8 @@ {-# INLINEABLE parseWithRead' #-} -- | Parse a value to a 'Maybe' type using the 'Read' instance+--+-- @since 0.3.0.0 maybeParseWithRead :: (Read a, Textual t) => t -- ^ textual input to parse@@ -597,6 +747,8 @@ -- | Implement 'ReadS' using 'parseEnum' -- -- This implementation expects all of the input to be consumed.+--+-- @since 0.1.0.0 readsEnum :: (Bounded a, Enum a, Render a) => Bool -- ^ case-insensitive when 'True'@@ -611,6 +763,8 @@ -- | Implement 'ReadS' using a 'Parse' instance -- -- This implementation expects all of the input to be consumed.+--+-- @since 0.3.0.0 readsWithParse :: Parse a => ReadS a@@ -619,7 +773,26 @@ Nothing -> [] {-# INLINEABLE readsWithParse #-} +------------------------------------------------------------------------------ -- $ParseValid+--+-- The follow functions provide a number of ways to use a 'Parse' instance to+-- validate constants at compile-time.+--+-- If you can use Template Haskell typed expressions in your project, use+-- 'valid', 'mkValid', or 'validOf'. Use 'valid' to define constants for+-- types that have a 'THS.Lift' instance. For types that do not have a+-- 'THS.Lift' instance, use 'mkValid' to define a validation function for that+-- type using a 'Proxy', or use 'validOf' to pass the 'Proxy' when defining+-- constants.+--+-- Typed expressions were not supported in @haskell-src-exts <1.22.0@, which+-- causes problems with old versions of @hlint@. If the issue affects you,+-- you may use 'mkUntypedValid', 'mkUntypedValidQQ', or 'untypedValidOf'+-- instead of the above functions. Use 'mkUntypedValid' to define a+-- validation function for a type using a 'Proxy', or use 'untypedValidOf' to+-- pass the 'Proxy' when defining constants. Alternatively, use+-- 'mkUntypedValidQQ' to define a validation quasi-quoter. -- | Validate a constant at compile-time using a 'Parse' instance --@@ -628,20 +801,61 @@ -- a 'THS.Lift' instance. When this is inconvenient, use one of the -- alternative functions in this library. ----- This function uses a typed expression. Typed expressions were not--- supported in @haskell-src-exts <1.22.0@, which caused problems with--- @hlint@. If the issue effects you, use @hlint -i "Parse error"@ to ignore--- parse errors or use one of the alternative functions in this library.+-- This function uses a Template Haskell typed expression. Typed expressions+-- were not supported in @haskell-src-exts <1.22.0@, which causes problems+-- with old versions of @hlint@. If the issue affects you, use+-- @hlint -i "Parse error"@ to ignore parse errors or use one of the+-- alternative functions in this library. ----- See the @valid@, @invalid@, and @lift@ example programs in the @examples@--- directory.+-- Note that the typed Template Haskell API changed in GHC 9. The type+-- displayed in this documentation is determined by the version of GHC used to+-- build the documentation.+--+-- The type of this function in GHC 9 or later is as follows:+--+-- @+-- valid+-- :: (MonadFail m, THS.Quote m, Parse a, THS.Lift a)+-- => String+-- -> THS.Code m a+-- @+--+-- The type of this function in previous versions of GHC is as follows:+--+-- @+-- valid+-- :: (Parse a, THS.Lift a)+-- => String+-- -> TH.Q (TH.TExp a)+-- @+--+-- This function is used the same way in all GHC versions. See the @valid@,+-- @invalid@, and @lift@ example programs in the @examples@ directory. The+-- following is example usage from the @valid@ example:+--+-- @+-- sample :: Username+-- sample = $$(TTC.valid "tcard")+-- @+--+-- @since 0.1.0.0+#if __GLASGOW_HASKELL__ >= 900 valid+ :: (MonadFail m, THS.Quote m, Parse a, THS.Lift a)+ => String+ -> THS.Code m a+valid s = case parse s of+ Right x -> [|| x ||]+ Left err -> THS.Code . fail $ "Invalid constant: " ++ err+#else+valid :: (Parse a, THS.Lift a) => String -> TH.Q (TH.TExp a) valid s = case parse s of Right x -> [|| x ||] Left err -> fail $ "Invalid constant: " ++ err+#endif -- | Validate a constant at compile-time using a 'Parse' instance --@@ -653,13 +867,57 @@ -- run-time. Since the result is not compiled in, no 'THS.Lift' instance is -- required. ----- This function uses a typed expression. Typed expressions were not--- supported in @haskell-src-exts <1.22.0@, which caused problems with--- @hlint@. If the issue effects you, use @hlint -i "Parse error"@ to ignore--- parse errors or use 'untypedValidOf' instead.+-- This function uses a Template Haskell typed expression. Typed expressions+-- were not supported in @haskell-src-exts <1.22.0@, which causes problems+-- with old versions of @hlint@. If the issue affects you, use+-- @hlint -i "Parse error"@ to ignore parse errors or use 'untypedValidOf'+-- instead. ----- See the @validof@ example program in the @examples@ directory.+-- Note that the typed Template Haskell API changed in GHC 9. The type+-- displayed in this documentation is determined by the version of GHC used to+-- build the documentation.+--+-- The type of this function in GHC 9 or later is as follows:+--+-- @+-- validOf+-- :: (MonadFail m, THS.Quote m, Parse a)+-- => Proxy a+-- -> String+-- -> THS.Code m a+-- @+--+-- The type of this function in previous versions of GHC is as follows:+--+-- @+-- validOf+-- :: Parse a+-- => Proxy a+-- -> String+-- -> TH.Q (TH.TExp a)+-- @+--+-- This function is used the same way in all GHC versions. See the @validof@+-- example program in the @examples@ directory. The following is example+-- usage from the @validof@ example:+--+-- @+-- sample :: Username+-- sample = $$(TTC.validOf (Proxy :: Proxy Username) "tcard")+-- @+--+-- @since 0.1.0.0+#if __GLASGOW_HASKELL__ >= 900 validOf+ :: (MonadFail m, THS.Quote m, Parse a)+ => Proxy a+ -> String+ -> THS.Code m a+validOf proxy s = case (`asProxyTypeOf` proxy) <$> parse s of+ Right{} -> [|| parseUnsafeS s ||]+ Left err -> THS.Code . fail $ "Invalid constant: " ++ err+#else+validOf :: Parse a => Proxy a -> String@@ -667,18 +925,57 @@ validOf proxy s = case (`asProxyTypeOf` proxy) <$> parse s of Right{} -> [|| parseUnsafeS s ||] Left err -> fail $ "Invalid constant: " ++ err+#endif -- | Make a @valid@ function using 'validOf' for the given type ----- Create a @valid@ function in the module for a type in order to avoid having--- to write a 'Proxy' when defining constants.+-- Create a @valid@ function for a type in order to avoid having to write a+-- 'Proxy' when defining constants. ----- This function uses a typed expression. Typed expressions were not--- supported in @haskell-src-exts <1.22.0@, which caused problems with--- @hlint@. If the issue effects you, use @hlint -i "Parse error"@ to ignore--- parse errors or use 'mkUntypedValidOf' instead.+-- This function uses a Template Haskell typed expression. Typed expressions+-- were not supported in @haskell-src-exts <1.22.0@, which causes problems+-- with old versions of @hlint@. If the issue affects you, use+-- @hlint -i "Parse error"@ to ignore parse errors or use 'mkUntypedValid'+-- instead. ----- See the @mkvalid@ example program in the @examples@ directory.+-- Note that the typed Template Haskell API changed in GHC 9. The type+-- displayed in this documentation is determined by the version of GHC used to+-- build the documentation.+--+-- The type of the created @valid@ function in GHC 9 or later is as follows:+--+-- @+-- \$funName+-- :: forall m. (MonadFail m, THS.Quote m)+-- => String+-- -> THS.Code m $resultType+-- @+--+-- The type of the created @valid@ function in previous versions of GHC is as+-- follows:+--+-- @+-- \$funName+-- :: String+-- -> TH.Q (TH.TExp $resultType)+-- @+--+-- This function is used the same way in all GHC versions. See the @mkvalid@+-- example program in the @examples@ directory. The following is example+-- usage from the @mkvalid@ example:+--+-- @+-- \$(TTC.mkValid "valid" ''Username)+-- @+--+-- The created @valid@ function can then be used as follows:+--+-- @+-- sample :: Username+-- sample = $$(Username.valid "tcard")+-- @+--+-- @since 0.1.0.0 mkValid :: String -> TH.Name@@ -686,7 +983,15 @@ mkValid funName typeName = do let funName' = TH.mkName funName resultType = pure $ TH.ConT typeName+#if __GLASGOW_HASKELL__ >= 900+ funType <-+ [t|+ forall m . (MonadFail m, THS.Quote m) =>+ String -> THS.Code m $resultType+ |]+#else funType <- [t| String -> TH.Q (TH.TExp $resultType) |]+#endif body <- [| validOf (Proxy :: Proxy $resultType) |] return [ TH.SigD funName' funType@@ -703,7 +1008,15 @@ -- run-time. Since the result is not compiled in, no 'THS.Lift' instance is -- required. ----- See the @uvalidof@ example program in the @examples@ directory.+-- See the @uvalidof@ example program in the @examples@ directory. The+-- following is example usage from the @uvalidof@ example:+--+-- @+-- sample :: Username+-- sample = $(TTC.untypedValidOf (Proxy :: Proxy Username) "tcard")+-- @+--+-- @since 0.2.0.0 untypedValidOf :: Parse a => Proxy a@@ -715,10 +1028,24 @@ -- | Make a @valid@ function using 'untypedValidOf' for the given type ----- Create a @valid@ function in the module for a type in order to avoid having--- to write a 'Proxy' when defining constants.+-- Create a @valid@ function for a type in order to avoid having to write a+-- 'Proxy' when defining constants. ----- See the @mkuvalid@ example program in the @examples@ directory.+-- See the @mkuvalid@ example program in the @examples@ directory. The+-- following is example usage from the @mkuvalid@ example:+--+-- @+-- \$(TTC.mkUntypedValid "valid" ''Username)+-- @+--+-- The created @valid@ function can then be used as follows:+--+-- @+-- sample :: Username+-- sample = $(Username.valid "tcard")+-- @+--+-- @since 0.2.0.0 mkUntypedValid :: String -> TH.Name@@ -735,7 +1062,21 @@ -- | Make a @valid@ quasi-quoter using 'untypedValidOf' for the given type ----- See the @uvalidqq@ example program in the @examples@ directory.+-- See the @uvalidqq@ example program in the @examples@ directory. The+-- following is example usage from the @uvalidqq@ example:+--+-- @+-- \$(TTC.mkUntypedValidQQ "valid" ''Username)+-- @+--+-- The created @valid@ function can then be used as follows:+--+-- @+-- sample :: Username+-- sample = [Username.valid|tcard|]+-- @+--+-- @since 0.2.0.0 mkUntypedValidQQ :: String -> TH.Name
src/Data/TTC/Instances.hs view
@@ -2,7 +2,7 @@ -- | -- Module : Data.TTC.Instances -- Description : instances for basic data types--- Copyright : Copyright (c) 2019-2020 Travis Cardwell+-- Copyright : Copyright (c) 2019-2021 Travis Cardwell -- License : MIT -- -- This module defines TTC 'TTC.Render' and 'TTC.Parse' instances for some@@ -44,7 +44,7 @@ instance TTC.Parse Char where parse = TTC.asS $ \case [c] -> Right c- _ -> Left $ TTC.fromS "invalid Char"+ _cs -> Left $ TTC.fromS "invalid Char" instance TTC.Render Char where render c = TTC.fromS [c]
test/Data/TTC/Test.hs view
@@ -123,6 +123,12 @@ answerBSL :: BSL.ByteString answerBSL = "42" +answerTLB :: TLB.Builder+answerTLB = "42"++answerSBS :: SBS.ShortByteString+answerSBS = "42"+ answerZ :: Int answerZ = 42 @@ -394,6 +400,16 @@ testRenderBSL :: TestTree testRenderBSL = testCase "renderBSL" $ answerBSL @=? TTC.renderBSL answer +testRenderTLB :: TestTree+testRenderTLB = testCase "renderTLB" $ answerTLB @=? TTC.renderTLB answer++testRenderBSB :: TestTree+testRenderBSB = testCase "renderBSB" $+ answerBSL @=? BSB.toLazyByteString (TTC.renderBSB answer)++testRenderSBS :: TestTree+testRenderSBS = testCase "renderSBS" $ answerSBS @=? TTC.renderSBS answer+ testRenderWithShow :: TestTree testRenderWithShow = testGroup "renderWithShow" [ testCase "S" $ answerS @=? TTC.renderWithShow answerZ@@ -657,6 +673,9 @@ , testRenderTL , testRenderBS , testRenderBSL+ , testRenderTLB+ , testRenderBSB+ , testRenderSBS , testRenderWithShow ] , testGroup "Parse"
ttc.cabal view
@@ -1,5 +1,5 @@ name: ttc-version: 0.3.0.0+version: 0.4.0.0 category: Data, Text synopsis: Textual Type Classes description:@@ -11,18 +11,19 @@ bug-reports: https://github.com/ExtremaIS/ttc-haskell/issues author: Travis Cardwell <travis.cardwell@extrema.is> maintainer: Travis Cardwell <travis.cardwell@extrema.is>-copyright: Copyright (c) 2019-2020 Travis Cardwell+copyright: Copyright (c) 2019-2021 Travis Cardwell license: MIT license-file: LICENSE -cabal-version: >=1.10+cabal-version: 1.24 build-type: Simple tested-with: GHC ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4- || ==8.10.2+ || ==8.10.4+ || ==9.0.1 extra-source-files: CHANGELOG.md@@ -32,6 +33,10 @@ type: git location: https://github.com/ExtremaIS/ttc-haskell.git +flag write-hie+ description: write .hie files+ default: False+ library hs-source-dirs: src exposed-modules:@@ -40,10 +45,15 @@ build-depends: base >=4.7 && <5 , bytestring >=0.10.8 && <0.12- , template-haskell >=2.12 && <2.17+ , template-haskell >=2.12 && <2.18 , text >=1.2.3 && <1.3 default-language: Haskell2010- ghc-options: -Wall+ default-extensions:+ OverloadedStrings+ if flag(write-hie)+ ghc-options: -Wall -fwrite-ide-info -hiedir=.hie+ else+ ghc-options: -Wall test-suite ttc-test type: exitcode-stdio-1.0