packages feed

unwitch 2.2.0 → 3.0.0

raw patch · 24 files changed

+294/−50 lines, 24 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Unwitch.Convert.Int32: toInt :: Int32 -> Maybe Int
+ Unwitch.Convert.Int32: toInt :: Int32 -> Int

Files

Changelog.md view
@@ -1,5 +1,26 @@ # Change log for unwitch project +## Version 3.0.0++### Breaking changes (GHC only)++ Int32.toInt is now total on GHC (Int32 -> Int instead of Int32 -> Maybe Int).+  GHC's Int# is always at least 32 bits wide (WORD_SIZE_IN_BITS), so every+  Int32 value fits without loss. Code pattern matching on Just/Nothing will+  need updating.++ On non-GHC compilers, Int32.toInt retains the Maybe Int return type since+  the Haskell Report only guarantees Int has 30 bits.++ CInt.toInt now delegates to Int32.toInt instead of using fromIntegral.++### Added++ Add MicroHs CI support — all exposed modules are compiled with mhs++ Guard GHC-specific code behind CPP: unboxed types, GHC.Exts imports,+  MagicHash/UnboxedSums/UnboxedTuples extensions, and ghc-bignum dependency++ Guard CInt module and all toCInt functions behind __GLASGOW_HASKELL__+  (MicroHs defines CInt as a newtype over Int, not Int32)++ Replace GHC.Float.double2Float/float2Double with portable realToFrac++ Replace Data.Text.all with Prelude.all for MicroHs compatibility++ CI module list is now extracted from the cabal file automatically+ ## Version 2.2.0 + New module Unwitch.Convert.CInt — conversions from CInt (Foreign.C.Types)   to all supported numeric types (Int, Int8–64, Word, Word8–64, Integer,
src/Unwitch/Convert/ByteString.hs view
@@ -5,11 +5,13 @@   , toWord8s   , fromWord8s   , toTextLatin1+#ifdef __GLASGOW_HASKELL__   , toTextUtf8   , toTextUtf16LE   , toTextUtf16BE   , toTextUtf32LE   , toTextUtf32BE+#endif   ) where @@ -20,9 +22,11 @@ import Data.ByteString.Short (ShortByteString) import Data.Text (Text) import Data.Text.Encoding qualified as TE-import Data.Text.Encoding.Error (UnicodeException) import Data.Word (Word8)+#ifdef __GLASGOW_HASKELL__+import Data.Text.Encoding.Error (UnicodeException) import Unwitch.TryEvaluate (tryEvaluate)+#endif  toLazyByteString :: ByteString -> LBS.ByteString toLazyByteString = LBS.fromStrict@@ -39,6 +43,7 @@ toTextLatin1 :: ByteString -> Text toTextLatin1 = TE.decodeLatin1 +#ifdef __GLASGOW_HASKELL__ toTextUtf8 :: ByteString -> Either UnicodeException Text toTextUtf8 = TE.decodeUtf8' @@ -53,4 +58,4 @@  toTextUtf32BE :: ByteString -> Either UnicodeException Text toTextUtf32BE = tryEvaluate . TE.decodeUtf32BE-+#endif
src/Unwitch/Convert/CInt.hs view
@@ -1,5 +1,8 @@ -- | Conversions from 'CInt'.+-- CInt is GHC-specific (wraps Int32 in GHC, Int in MicroHs),+-- so this module is only available under GHC. module Unwitch.Convert.CInt+#ifdef __GLASGOW_HASKELL__   ( -- * Conversions     toInt8   , toInt16@@ -16,8 +19,10 @@   , toFloat   , toDouble   )+#endif where +#ifdef __GLASGOW_HASKELL__ import           Unwitch.Errors import qualified Unwitch.Convert.Int32 as Int32 import           Data.Word@@ -44,7 +49,7 @@  -- | Total conversion, Int is at least 32 bits wide. toInt :: CInt -> Int-toInt (CInt x) = fromIntegral x+toInt (CInt x) = Int32.toInt x  -- | Total conversion to Integer. toInteger :: CInt -> Integer@@ -81,3 +86,4 @@ -- | Total conversion, all Int32 values are exactly representable as Double. toDouble :: CInt -> Double toDouble (CInt x) = Int32.toDouble x+#endif
src/Unwitch/Convert/Char.hs view
@@ -5,22 +5,28 @@   , toWord   , fromInt   , fromWord+#ifdef __GLASGOW_HASKELL__   -- * Unboxed conversions   -- $unboxed   , fromInt#   , fromWord#+#endif   ) where  import Data.Char (ord, chr)+#ifdef __GLASGOW_HASKELL__ import GHC.Exts (Int(..), Word(..), Char(..), chr#,                  word2Int#, (>=#), (<=#), leWord#, gtWord#)+#endif +#ifdef __GLASGOW_HASKELL__ -- $unboxed -- These use GHC unboxed types and unboxed sums for zero-allocation -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and -- @UnboxedTuples@ language extensions. -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.+#endif  -- | Converts a Char to its Unicode codepoint as Int. Infallible. toInt :: Char -> Int@@ -37,6 +43,14 @@   then Just $ chr i   else Nothing +-- | Converts a Word to a Char if it is a valid Unicode codepoint.+-- Valid range: 0..0xD7FF and 0xE000..0x10FFFF (excludes surrogates).+fromWord :: Word -> Maybe Char+fromWord w = if isValidCodepoint (fromIntegral w)+  then Just $ chr (fromIntegral w)+  else Nothing++#ifdef __GLASGOW_HASKELL__ -- | Unboxed variant of 'fromInt'. Checks valid Unicode codepoint range. fromInt# :: Int -> (# Char | (# #) #) fromInt# (I# i#) = case i# >=# 0# of@@ -49,13 +63,6 @@       _  -> (# | (# #) #)   _  -> (# | (# #) #) --- | Converts a Word to a Char if it is a valid Unicode codepoint.--- Valid range: 0..0xD7FF and 0xE000..0x10FFFF (excludes surrogates).-fromWord :: Word -> Maybe Char-fromWord w = if isValidCodepoint (fromIntegral w)-  then Just $ chr (fromIntegral w)-  else Nothing- -- | Unboxed variant of 'fromWord'. Checks valid Unicode codepoint range. fromWord# :: Word -> (# Char | (# #) #) fromWord# (W# w#) = case leWord# w# 0xD7FF## of@@ -65,6 +72,7 @@       1# -> (# C# (chr# (word2Int# w#)) | #)       _  -> (# | (# #) #)     _  -> (# | (# #) #)+#endif  isValidCodepoint :: Integer -> Bool isValidCodepoint cp =
src/Unwitch/Convert/Double.hs view
@@ -15,7 +15,9 @@   , toWord64   , toWord   , toNatural+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif   , ViaIntegerErrors(..)   , IntegerErrors(..)   , RationalErrors(..)@@ -25,7 +27,6 @@ import           Data.Bifunctor(first) import           Data.Fixed (Fixed, HasResolution) import           Unwitch.Constant-import qualified GHC.Float as F import           Unwitch.Convert.Ratio(unwrapIfDenominatorOne) import qualified Prelude import           Unwitch.Errors@@ -34,11 +35,13 @@ import Data.Word import Data.Int import Numeric.Natural (Natural)+#ifdef __GLASGOW_HASKELL__ import Foreign.C.Types (CInt(CInt))+#endif  -- | Lossy narrowing conversion, may lose precision. toFloat :: Double -> Float-toFloat = F.double2Float+toFloat = realToFrac  -- | Converts a Double to a Fixed value. Rejects NaN and infinities. toFixed :: (HasResolution a) => Double -> Either RationalErrors (Fixed a)@@ -103,9 +106,11 @@     Left err -> Left $ MkInteger $ IntegerFlow integer err     Right n -> Right n +#ifdef __GLASGOW_HASKELL__ -- | Converts via 'Integer', fails if not a whole number or out of range. toCInt :: Double -> Either ViaIntegerErrors CInt toCInt x = CInt <$> toInt32 x+#endif  -- | Convert via 'Integer' then narrow, combining errors. toViaInteger :: (Integer -> Maybe a) -> Double -> Either ViaIntegerErrors a
src/Unwitch/Convert/Float.hs view
@@ -14,7 +14,9 @@   , toWord64   , toWord   , toNatural+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif   , ViaIntegerErrors(..)   , IntegerErrors(..)   , RationalErrors(..)@@ -23,7 +25,6 @@  import           Data.Bifunctor(first) import           Unwitch.Constant-import qualified GHC.Float as F import           Unwitch.Convert.Ratio(unwrapIfDenominatorOne) import qualified Prelude import           Unwitch.Errors@@ -32,10 +33,12 @@ import Data.Word import Data.Int import Numeric.Natural (Natural)+#ifdef __GLASGOW_HASKELL__ import Foreign.C.Types (CInt(CInt))+#endif  toDouble :: Float -> Double-toDouble = F.float2Double+toDouble = realToFrac  data IntegerErrors = IntegerFlow Integer Overflows                    | RationalConversion RationalErrors@@ -100,9 +103,11 @@     Left err -> Left $ MkInteger $ IntegerFlow integer err     Right n -> Right n +#ifdef __GLASGOW_HASKELL__ -- | Converts via 'Integer', fails if not a whole number or out of range. toCInt :: Float -> Either ViaIntegerErrors CInt toCInt x = CInt <$> toInt32 x+#endif  -- | Convert via 'Integer' then narrow, combining errors. toViaInteger :: (Integer -> Maybe a) -> Float -> Either ViaIntegerErrors a
src/Unwitch/Convert/Int.hs view
@@ -14,7 +14,10 @@   , toNatural   , toFloat   , toDouble+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif+#ifdef __GLASGOW_HASKELL__   -- * Unboxed conversions   -- $unboxed   , toInt8#@@ -28,6 +31,7 @@   , toNatural#   , toFloat#   , toDouble#+#endif   ) where @@ -37,8 +41,9 @@ import           Data.Word import           Data.Int import           Numeric.Natural (Natural)-import           Foreign.C.Types (CInt(CInt)) import           Prelude hiding (toInteger)+#ifdef __GLASGOW_HASKELL__+import           Foreign.C.Types (CInt(CInt)) import           GHC.Exts (Int(..), Word(..), Float(..), Double(..),                            intToInt8#, int8ToInt#, intToInt16#, int16ToInt#,                            intToInt32#, int32ToInt#,@@ -52,12 +57,15 @@ import           GHC.Int (Int8(..), Int16(..), Int32(..)) import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..)) import           GHC.Num.Natural (Natural(NS))+#endif +#ifdef __GLASGOW_HASKELL__ -- $unboxed -- These use GHC unboxed types and unboxed sums for zero-allocation -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and -- @UnboxedTuples@ language extensions. -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.+#endif  toInt8 :: Int -> Maybe Int8 toInt8 = Bits.toIntegralSized@@ -102,9 +110,11 @@   | x > maxIntegralRepFloat  -> Left Overflow   | otherwise                -> Right $ fromIntegral x +#ifdef __GLASGOW_HASKELL__ -- | Narrowing conversion via Int32, fails if outside Int32 range. toCInt :: Int -> Maybe CInt toCInt x = CInt <$> toInt32 x+#endif  -- | Checked conversion, fails if outside exact double integer range (+/-9007199254740991). toDouble :: Int -> Either Overflows Double@@ -113,6 +123,7 @@   | fromIntegral x > (maxIntegralRepDouble :: Integer)  -> Left Overflow   | otherwise                                           -> Right $ fromIntegral x +#ifdef __GLASGOW_HASKELL__ -- | Signed narrowing, roundtrip at Int# toInt8# :: Int -> (# Int8 | (# #) #) toInt8# (I# x#) =@@ -194,3 +205,4 @@   _  -> case i# ># 9007199254740991# of     1# -> (# Overflow | #)     _  -> (# | D# (int2Double# i#) #)+#endif
src/Unwitch/Convert/Int16.hs view
@@ -14,7 +14,10 @@   , toNatural   , toFloat   , toDouble+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif+#ifdef __GLASGOW_HASKELL__   -- * Unboxed conversions   -- $unboxed   , toInt8#@@ -24,6 +27,7 @@   , toWord64#   , toWord#   , toNatural#+#endif   ) where @@ -32,8 +36,9 @@ import           Data.Word import           Data.Int import           Numeric.Natural (Natural)-import           Foreign.C.Types (CInt(CInt)) import           Prelude hiding (toInteger)+#ifdef __GLASGOW_HASKELL__+import           Foreign.C.Types (CInt(CInt)) import           GHC.Exts (Word(..), int16ToInt#, intToInt8#, int8ToInt#,                            int2Word#, word2Int#,                            wordToWord8#, word8ToWord#,@@ -42,12 +47,15 @@ import           GHC.Int (Int8(..), Int16(..)) import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..)) import           GHC.Num.Natural (Natural(NS))+#endif +#ifdef __GLASGOW_HASKELL__ -- $unboxed -- These use GHC unboxed types and unboxed sums for zero-allocation -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and -- @UnboxedTuples@ language extensions. -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.+#endif  toInt8 :: Int16 -> Maybe Int8 toInt8 = Bits.toIntegralSized@@ -85,9 +93,11 @@   | x < 0     -> Left Underflow   | otherwise  -> Right $ fromIntegral x +#ifdef __GLASGOW_HASKELL__ -- | Widening conversion via Int32, always succeeds. toCInt :: Int16 -> CInt toCInt x = CInt $ toInt32 x+#endif  toFloat :: Int16 -> Float toFloat = fromIntegral@@ -95,6 +105,7 @@ toDouble :: Int16 -> Double toDouble = fromIntegral +#ifdef __GLASGOW_HASKELL__ -- | Signed narrowing, roundtrip at Int# toInt8# :: Int16 -> (# Int8 | (# #) #) toInt8# (I16# x16#) =@@ -142,3 +153,4 @@ toNatural# (I16# x16#) = case int16ToInt# x16# >=# 0# of   1# -> (# | NS (int2Word# (int16ToInt# x16#)) #)   _  -> (# Underflow | #)+#endif
src/Unwitch/Convert/Int32.hs view
@@ -14,7 +14,10 @@   , toNatural   , toFloat   , toDouble+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif+#ifdef __GLASGOW_HASKELL__   -- * Unboxed conversions   -- $unboxed   , toInt8#@@ -27,6 +30,7 @@   , toWord#   , toNatural#   , toFloat#+#endif   ) where @@ -36,8 +40,9 @@ import           Data.Word import           Data.Int import           Numeric.Natural (Natural)-import           Foreign.C.Types (CInt(CInt)) import           Prelude hiding (toInteger)+#ifdef __GLASGOW_HASKELL__+import           Foreign.C.Types (CInt(CInt)) import           GHC.Exts (Int(..), Word(..), Float(..),                            int32ToInt#, intToInt8#, int8ToInt#,                            intToInt16#, int16ToInt#,@@ -50,12 +55,15 @@ import           GHC.Int (Int8(..), Int16(..), Int32(..)) import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..)) import           GHC.Num.Natural (Natural(NS))+#endif +#ifdef __GLASGOW_HASKELL__ -- $unboxed -- These use GHC unboxed types and unboxed sums for zero-allocation -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and -- @UnboxedTuples@ language extensions. -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.+#endif  toInt8 :: Int32 -> Maybe Int8 toInt8 = Bits.toIntegralSized@@ -66,8 +74,26 @@ toInt64 :: Int32 -> Int64 toInt64 = fromIntegral +#ifdef __GLASGOW_HASKELL__+-- | Total conversion — GHC implements 'Int' using the primitive type+-- @Int#@, whose size equals the @MachDeps.h@ constant+-- @WORD_SIZE_IN_BITS@ (32 on 32-bit platforms, 64 on 64-bit).+-- Since @Int#@ is always at least 32 bits wide, every 'Int32' value+-- fits without loss.+--+-- See: <https://hackage.haskell.org/package/ghc-prim-0.9.0/docs/src/GHC.Prim.html GHC.Prim>+-- ("Int32 always fits in Int (Int is at least 32 bits)") and+-- <https://hackage.haskell.org/package/base/docs/Data-Int.html Data.Int>+-- ("A fixed-precision integer type with at least the range [-2^29 .. 2^29-1]").+toInt :: Int32 -> Int+toInt (I32# x#) = I# (int32ToInt# x#)+#else+-- | Fallible conversion — the Haskell Report only guarantees 'Int' has+-- at least the range @[-2^29 .. 2^29-1]@ (30 bits), so 'Int32' values+-- outside that range may not fit on non-GHC compilers. toInt :: Int32 -> Maybe Int toInt = Bits.toIntegralSized+#endif  toInteger :: Int32 -> Integer toInteger = fromIntegral@@ -100,13 +126,16 @@   | x > maxIntegralRepFloat  -> Left Overflow   | otherwise                -> Right $ fromIntegral x +#ifdef __GLASGOW_HASKELL__ -- | Direct wrapping, CInt is a newtype over Int32. toCInt :: Int32 -> CInt toCInt = CInt+#endif  toDouble :: Int32 -> Double toDouble = fromIntegral +#ifdef __GLASGOW_HASKELL__ -- | Signed narrowing, roundtrip at Int# toInt8# :: Int32 -> (# Int8 | (# #) #) toInt8# (I32# x32#) =@@ -180,3 +209,4 @@     _  -> case i# ># 16777215# of       1# -> (# Overflow | #)       _  -> (# | F# (int2Float# i#) #)+#endif
src/Unwitch/Convert/Int64.hs view
@@ -14,7 +14,10 @@   , toNatural   , toFloat   , toDouble+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif+#ifdef __GLASGOW_HASKELL__   -- * Unboxed conversions   -- $unboxed   , toInt8#@@ -29,6 +32,7 @@   , toNatural#   , toFloat#   , toDouble#+#endif   ) where @@ -38,8 +42,9 @@ import           Data.Word import           Data.Int import           Numeric.Natural (Natural)-import           Foreign.C.Types (CInt(CInt)) import           Prelude hiding (toInteger)+#ifdef __GLASGOW_HASKELL__+import           Foreign.C.Types (CInt(CInt)) import           GHC.Exts (Int(..), Word(..), Float(..), Double(..),                            int64ToInt#, intToInt64#,                            intToInt8#, int8ToInt#,@@ -56,12 +61,15 @@ import           GHC.Int (Int8(..), Int16(..), Int32(..), Int64(..)) import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..)) import           GHC.Num.Natural (Natural(NS))+#endif +#ifdef __GLASGOW_HASKELL__ -- $unboxed -- These use GHC unboxed types and unboxed sums for zero-allocation -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and -- @UnboxedTuples@ language extensions. -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.+#endif  toInt8 :: Int64 -> Maybe Int8 toInt8 = Bits.toIntegralSized@@ -75,9 +83,11 @@ toInt :: Int64 -> Maybe Int toInt = Bits.toIntegralSized +#ifdef __GLASGOW_HASKELL__ -- | Narrowing conversion via Int32, fails if outside Int32 range. toCInt :: Int64 -> Maybe CInt toCInt x = CInt <$> toInt32 x+#endif  toInteger :: Int64 -> Integer toInteger = fromIntegral@@ -117,6 +127,7 @@   | x > maxIntegralRepDouble  -> Left Overflow   | otherwise                 -> Right $ fromIntegral x +#ifdef __GLASGOW_HASKELL__ -- | Narrow through Int#, compare at Int64# toInt8# :: Int64 -> (# Int8 | (# #) #) toInt8# (I64# x64#) =@@ -230,3 +241,4 @@     _  -> case geInt64# x64# (intToInt64# 0#) of       1# -> (# Overflow | #)       _  -> (# Underflow | #)+#endif
src/Unwitch/Convert/Int8.hs view
@@ -14,7 +14,10 @@   , toNatural   , toFloat   , toDouble+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif+#ifdef __GLASGOW_HASKELL__   -- * Unboxed conversions   -- $unboxed   , toWord8#@@ -23,6 +26,7 @@   , toWord64#   , toWord#   , toNatural#+#endif   ) where @@ -31,20 +35,24 @@ import           Data.Word import           Data.Int import           Numeric.Natural (Natural)-import           Foreign.C.Types (CInt(CInt)) import           Prelude hiding (toInteger)+#ifdef __GLASGOW_HASKELL__+import           Foreign.C.Types (CInt(CInt)) import           GHC.Exts (Word(..), int8ToInt#, int2Word#,                            wordToWord8#, wordToWord16#, wordToWord32#,                            wordToWord64#, (>=#)) import           GHC.Int (Int8(..)) import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..)) import           GHC.Num.Natural (Natural(NS))+#endif +#ifdef __GLASGOW_HASKELL__ -- $unboxed -- These use GHC unboxed types and unboxed sums for zero-allocation -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and -- @UnboxedTuples@ language extensions. -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.+#endif  toInt16 :: Int8 -> Int16 toInt16 = fromIntegral@@ -82,9 +90,11 @@   | x < 0     -> Left Underflow   | otherwise  -> Right $ fromIntegral x +#ifdef __GLASGOW_HASKELL__ -- | Widening conversion via Int32, always succeeds. toCInt :: Int8 -> CInt toCInt x = CInt $ toInt32 x+#endif  toFloat :: Int8 -> Float toFloat = fromIntegral@@ -92,6 +102,7 @@ toDouble :: Int8 -> Double toDouble = fromIntegral +#ifdef __GLASGOW_HASKELL__ -- | Signed->unsigned, check non-negative toWord8# :: Int8 -> (# Word8 | (# #) #) toWord8# (I8# x8#) = case int8ToInt# x8# >=# 0# of@@ -127,3 +138,4 @@ toNatural# (I8# x8#) = case int8ToInt# x8# >=# 0# of   1# -> (# | NS (int2Word# (int8ToInt# x8#)) #)   _  -> (# Underflow | #)+#endif
src/Unwitch/Convert/Integer.hs view
@@ -14,7 +14,10 @@   , toWord32   , toWord64   , toWord+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif+#ifdef __GLASGOW_HASKELL__   -- * Unboxed conversions   -- $unboxed   , toDouble#@@ -30,6 +33,7 @@   , toWord32#   , toWord64#   , toWord#+#endif   ) where @@ -39,6 +43,7 @@ import Data.Word import Data.Int import Numeric.Natural (Natural)+#ifdef __GLASGOW_HASKELL__ import Foreign.C.Types (CInt(CInt)) import           GHC.Exts (Int(..), Word(..), Float(..), Double(..),                            intToInt8#, int8ToInt#,@@ -57,21 +62,24 @@ import           GHC.Num.Integer (Integer(..), integerToWord#,                                   integerFromWord#, integerEq#) import           GHC.Num.Natural (Natural(NS, NB))+#endif +#ifdef __GLASGOW_HASKELL__ -- $unboxed -- These use GHC unboxed types and unboxed sums for zero-allocation -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and -- @UnboxedTuples@ language extensions. -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.+#endif --- | Checked conversion, fails if outside exact double integer range (\u00b19007199254740991).+-- | Checked conversion, fails if outside exact double integer range (±9007199254740991). toDouble :: Integer -> Either Overflows Double toDouble integer = if     | integer < -maxIntegralRepDouble -> Left Underflow     | integer > maxIntegralRepDouble -> Left Overflow     | otherwise -> Right $ Prelude.fromIntegral integer --- | Checked conversion, fails if outside exact float integer range (\u00b116777215).+-- | Checked conversion, fails if outside exact float integer range (±16777215). toFloat :: Integer -> Either Overflows Float toFloat integer = if     | integer < -maxIntegralRepFloat -> Left Underflow@@ -114,10 +122,13 @@ toWord :: Integer -> Maybe Word toWord = Bits.toIntegralSized +#ifdef __GLASGOW_HASKELL__ -- | Narrowing conversion via Int32, fails if outside Int32 range. toCInt :: Integer -> Maybe CInt toCInt x = CInt <$> toInt32 x+#endif +#ifdef __GLASGOW_HASKELL__ -- | Bounds-checked double conversion via IS/IP/IN toDouble# :: Integer -> (# Overflows | Double #) toDouble# x = case x of@@ -254,3 +265,4 @@       1# -> (# W# w# | #)       _  -> (# | (# #) #)   IN _ -> (# | (# #) #)+#endif
src/Unwitch/Convert/LazyByteString.hs view
@@ -3,22 +3,26 @@   ( toByteString   , toWord8s   , fromWord8s+#ifdef __GLASGOW_HASKELL__   , toLazyTextLatin1   , toLazyTextUtf8   , toLazyTextUtf16LE   , toLazyTextUtf16BE   , toLazyTextUtf32LE   , toLazyTextUtf32BE+#endif   ) where  import Data.ByteString (ByteString) import Data.ByteString.Lazy qualified as LBS+import Data.Word (Word8)+#ifdef __GLASGOW_HASKELL__ import Data.Text.Lazy qualified as LT import Data.Text.Lazy.Encoding qualified as LTE import Data.Text.Encoding.Error (UnicodeException)-import Data.Word (Word8) import Unwitch.TryEvaluate (tryEvaluate)+#endif  toByteString :: LBS.ByteString -> ByteString toByteString = LBS.toStrict@@ -29,6 +33,7 @@ fromWord8s :: [Word8] -> LBS.ByteString fromWord8s = LBS.pack +#ifdef __GLASGOW_HASKELL__ toLazyTextLatin1 :: LBS.ByteString -> LT.Text toLazyTextLatin1 = LTE.decodeLatin1 @@ -46,4 +51,4 @@  toLazyTextUtf32BE :: LBS.ByteString -> Either UnicodeException LT.Text toLazyTextUtf32BE = tryEvaluate . LTE.decodeUtf32BE-+#endif
src/Unwitch/Convert/LazyText.hs view
@@ -3,20 +3,24 @@   ( toText   , toString   , fromString+#ifdef __GLASGOW_HASKELL__   , toLazyByteStringUtf8   , toLazyByteStringUtf16LE   , toLazyByteStringUtf16BE   , toLazyByteStringUtf32LE   , toLazyByteStringUtf32BE   , toLazyByteStringLatin1+#endif   ) where -import Data.ByteString.Lazy qualified as LBS-import Data.ByteString.Lazy.Char8 qualified as LBSC8 import Data.Text (Text) import Data.Text.Lazy qualified as LT+#ifdef __GLASGOW_HASKELL__+import Data.ByteString.Lazy qualified as LBS+import Data.ByteString.Lazy.Char8 qualified as LBSC8 import Data.Text.Lazy.Encoding qualified as LTE+#endif  toText :: LT.Text -> Text toText = LT.toStrict@@ -27,6 +31,7 @@ fromString :: String -> LT.Text fromString = LT.pack +#ifdef __GLASGOW_HASKELL__ toLazyByteStringUtf8 :: LT.Text -> LBS.ByteString toLazyByteStringUtf8 = LTE.encodeUtf8 @@ -50,3 +55,4 @@  isLatin1 :: Char -> Bool isLatin1 c = c <= '\xFF'+#endif
src/Unwitch/Convert/Natural.hs view
@@ -14,7 +14,10 @@   , toInteger   , toFloat   , toDouble+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif+#ifdef __GLASGOW_HASKELL__   -- * Unboxed conversions   -- $unboxed   , toWord8#@@ -29,6 +32,7 @@   , toInt#   , toFloat#   , toDouble#+#endif   ) where @@ -38,8 +42,9 @@ import           Data.Word import           Data.Int import           Numeric.Natural (Natural)-import           Foreign.C.Types (CInt(CInt)) import           Prelude hiding (toInteger)+#ifdef __GLASGOW_HASKELL__+import           Foreign.C.Types (CInt(CInt)) import           GHC.Exts (Int(..), Word(..), Float(..), Double(..),                            word2Int#,                            wordToWord8#, word8ToWord#,@@ -55,12 +60,15 @@ import           GHC.Int (Int8(..), Int16(..), Int32(..), Int64(..)) import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..)) import           GHC.Num.Natural (naturalToWordMaybe#)+#endif +#ifdef __GLASGOW_HASKELL__ -- $unboxed -- These use GHC unboxed types and unboxed sums for zero-allocation -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and -- @UnboxedTuples@ language extensions. -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.+#endif  toWord8 :: Natural -> Maybe Word8 toWord8 = Bits.toIntegralSized@@ -95,22 +103,25 @@ toInteger :: Natural -> Integer toInteger = fromIntegral +#ifdef __GLASGOW_HASKELL__ -- | Narrowing conversion via Int32, fails if outside Int32 range. toCInt :: Natural -> Maybe CInt toCInt x = CInt <$> toInt32 x+#endif --- | Checked conversion, fails if outside exact float integer range (\u00b116777215).+-- | Checked conversion, fails if outside exact float integer range (±16777215). toFloat :: Natural -> Either Overflows Float toFloat x = if   | x > maxIntegralRepFloat -> Left Overflow   | otherwise               -> Right $ fromIntegral x --- | Checked conversion, fails if outside exact double integer range (\u00b19007199254740991).+-- | Checked conversion, fails if outside exact double integer range (±9007199254740991). toDouble :: Natural -> Either Overflows Double toDouble x = if   | x > maxIntegralRepDouble -> Left Overflow   | otherwise                -> Right $ fromIntegral x +#ifdef __GLASGOW_HASKELL__ -- | Via naturalToWordMaybe#, then narrow and roundtrip at Word# toWord8# :: Natural -> (# Word8 | (# #) #) toWord8# nat = case naturalToWordMaybe# nat of@@ -212,3 +223,4 @@   (# | w# #) -> case leWord# w# 9007199254740991## of     1# -> (# | D# (int2Double# (word2Int# w#)) #)     _  -> (# Overflow | #)+#endif
src/Unwitch/Convert/Text.hs view
@@ -20,7 +20,11 @@ import Data.Text.Lazy qualified as LT  toLazyText :: Text -> LT.Text+#ifdef __GLASGOW_HASKELL__ toLazyText = LT.fromStrict+#else+toLazyText = LT.toLazy+#endif  toString :: Text -> String toString = T.unpack@@ -45,10 +49,10 @@  -- | Returns 'Nothing' if any character exceeds @\xFF@. toByteStringLatin1 :: Text -> Maybe ByteString-toByteStringLatin1 t = if T.all isLatin1 t-  then Just $ BSC8.pack (T.unpack t)+toByteStringLatin1 t = if all isLatin1 str+  then Just $ BSC8.pack str   else Nothing+  where str = T.unpack t  isLatin1 :: Char -> Bool isLatin1 c = c <= '\xFF'-
src/Unwitch/Convert/Word.hs view
@@ -14,7 +14,10 @@   , toInteger   , toFloat   , toDouble+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif+#ifdef __GLASGOW_HASKELL__   -- * Unboxed conversions   -- $unboxed   , toWord8#@@ -27,6 +30,7 @@   , toInt#   , toFloat#   , toDouble#+#endif   ) where @@ -36,8 +40,9 @@ import           Data.Word import           Data.Int import           Numeric.Natural (Natural)-import           Foreign.C.Types (CInt(CInt)) import           Prelude hiding (toInteger)+#ifdef __GLASGOW_HASKELL__+import           Foreign.C.Types (CInt(CInt)) import           GHC.Exts (Int(..), Word(..), Float(..), Double(..),                            wordToWord8#, word8ToWord#,                            wordToWord16#, word16ToWord#,@@ -49,12 +54,15 @@                            eqWord#, leWord#, (>=#)) import           GHC.Int (Int8(..), Int16(..), Int32(..), Int64(..)) import           GHC.Word (Word8(..), Word16(..), Word32(..))+#endif +#ifdef __GLASGOW_HASKELL__ -- $unboxed -- These use GHC unboxed types and unboxed sums for zero-allocation -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and -- @UnboxedTuples@ language extensions. -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.+#endif  toWord8 :: Word -> Maybe Word8 toWord8 = Bits.toIntegralSized@@ -89,9 +97,11 @@ toInteger :: Word -> Integer toInteger = fromIntegral +#ifdef __GLASGOW_HASKELL__ -- | Narrowing conversion via Int32, fails if outside Int32 range. toCInt :: Word -> Maybe CInt toCInt x = CInt <$> toInt32 x+#endif  -- | Checked conversion, fails if outside exact float integer range (+-16777215). toFloat :: Word -> Either Overflows Float@@ -105,6 +115,7 @@   | fromIntegral x > (maxIntegralRepDouble :: Integer) -> Left Overflow   | otherwise                                          -> Right $ fromIntegral x +#ifdef __GLASGOW_HASKELL__ -- | Unsigned narrowing, roundtrip at Word# toWord8# :: Word -> (# Word8 | (# #) #) toWord8# (W# w#) =@@ -174,3 +185,4 @@ toDouble# (W# w#) = case leWord# w# 9007199254740991## of   1# -> (# | D# (int2Double# (word2Int# w#)) #)   _  -> (# Overflow | #)+#endif
src/Unwitch/Convert/Word16.hs view
@@ -14,12 +14,16 @@   , toInteger   , toFloat   , toDouble+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif+#ifdef __GLASGOW_HASKELL__   -- * Unboxed conversions   -- $unboxed   , toWord8#   , toInt8#   , toInt16#+#endif   ) where @@ -27,8 +31,9 @@ import           Data.Word import           Data.Int import           Numeric.Natural (Natural)-import           Foreign.C.Types (CInt(CInt)) import           Prelude hiding (toInteger)+#ifdef __GLASGOW_HASKELL__+import           Foreign.C.Types (CInt(CInt)) import           GHC.Exts (word16ToWord#, word2Int#,                            wordToWord8#, word8ToWord#,                            intToInt8#, int8ToInt#,@@ -36,12 +41,15 @@                            eqWord#, (==#)) import           GHC.Int (Int8(..), Int16(..)) import           GHC.Word (Word8(..), Word16(..))+#endif +#ifdef __GLASGOW_HASKELL__ -- $unboxed -- These use GHC unboxed types and unboxed sums for zero-allocation -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and -- @UnboxedTuples@ language extensions. -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.+#endif  toWord8 :: Word16 -> Maybe Word8 toWord8 = Bits.toIntegralSized@@ -82,10 +90,13 @@ toDouble :: Word16 -> Double toDouble = fromIntegral +#ifdef __GLASGOW_HASKELL__ -- | Widening conversion via Int32, always succeeds. toCInt :: Word16 -> CInt toCInt x = CInt $ toInt32 x+#endif +#ifdef __GLASGOW_HASKELL__ -- | Unsigned narrowing, roundtrip at Word# toWord8# :: Word16 -> (# Word8 | (# #) #) toWord8# (W16# w16#) =@@ -112,3 +123,4 @@   in case int16ToInt# n# ==# i# of     1# -> (# I16# n# | #)     _  -> (# | (# #) #)+#endif
src/Unwitch/Convert/Word32.hs view
@@ -14,7 +14,10 @@   , toInteger   , toFloat   , toDouble+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif+#ifdef __GLASGOW_HASKELL__   -- * Unboxed conversions   -- $unboxed   , toWord8#@@ -25,6 +28,7 @@   , toInt32#   , toInt#   , toFloat#+#endif   ) where @@ -34,8 +38,9 @@ import           Data.Word import           Data.Int import           Numeric.Natural (Natural)-import           Foreign.C.Types (CInt(CInt)) import           Prelude hiding (toInteger)+#ifdef __GLASGOW_HASKELL__+import           Foreign.C.Types (CInt(CInt)) import           GHC.Exts (Int(..), Word(..), Float(..),                            word32ToWord#, word2Int#,                            wordToWord8#, word8ToWord#,@@ -47,12 +52,15 @@                            eqWord#, leWord#, (==#), (>=#)) import           GHC.Int (Int8(..), Int16(..), Int32(..)) import           GHC.Word (Word8(..), Word16(..), Word32(..))+#endif +#ifdef __GLASGOW_HASKELL__ -- $unboxed -- These use GHC unboxed types and unboxed sums for zero-allocation -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and -- @UnboxedTuples@ language extensions. -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.+#endif  toWord8 :: Word32 -> Maybe Word8 toWord8 = Bits.toIntegralSized@@ -96,10 +104,13 @@ toDouble :: Word32 -> Double toDouble = fromIntegral +#ifdef __GLASGOW_HASKELL__ -- | Narrowing conversion via Int32, fails if outside Int32 range. toCInt :: Word32 -> Maybe CInt toCInt x = CInt <$> toInt32 x+#endif +#ifdef __GLASGOW_HASKELL__ -- | Unsigned narrowing, roundtrip at Word# toWord8# :: Word32 -> (# Word8 | (# #) #) toWord8# (W32# w32#) =@@ -162,3 +173,4 @@ toFloat# (W32# w32#) = case leWord# (word32ToWord# w32#) 16777215## of   1# -> (# | F# (int2Float# (word2Int# (word32ToWord# w32#))) #)   _  -> (# Overflow | #)+#endif
src/Unwitch/Convert/Word64.hs view
@@ -14,7 +14,10 @@   , toInteger   , toFloat   , toDouble+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif+#ifdef __GLASGOW_HASKELL__   -- * Unboxed conversions   -- $unboxed   , toWord8#@@ -28,6 +31,7 @@   , toInt#   , toFloat#   , toDouble#+#endif   ) where @@ -37,8 +41,9 @@ import           Data.Word import           Data.Int import           Numeric.Natural (Natural)-import           Foreign.C.Types (CInt(CInt)) import           Prelude hiding (toInteger)+#ifdef __GLASGOW_HASKELL__+import           Foreign.C.Types (CInt(CInt)) import           GHC.Exts (Int(..), Word(..), Float(..), Double(..),                            word64ToWord#, wordToWord64#,                            word2Int#,@@ -52,12 +57,15 @@                            (>=#)) import           GHC.Int (Int8(..), Int16(..), Int32(..), Int64(..)) import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..))+#endif +#ifdef __GLASGOW_HASKELL__ -- $unboxed -- These use GHC unboxed types and unboxed sums for zero-allocation -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and -- @UnboxedTuples@ language extensions. -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.+#endif  toWord8 :: Word64 -> Maybe Word8 toWord8 = Bits.toIntegralSized@@ -92,9 +100,11 @@ toInteger :: Word64 -> Integer toInteger = fromIntegral +#ifdef __GLASGOW_HASKELL__ -- | Narrowing conversion via Int32, fails if outside Int32 range. toCInt :: Word64 -> Maybe CInt toCInt x = CInt <$> toInt32 x+#endif  -- | Checked conversion, fails with 'Overflow' if outside exact float integer range. toFloat :: Word64 -> Either Overflows Float@@ -108,6 +118,7 @@   | x > maxIntegralRepDouble -> Left Overflow   | otherwise                -> Right $ fromIntegral x +#ifdef __GLASGOW_HASKELL__ -- | Unsigned narrowing via Word64# comparison toWord8# :: Word64 -> (# Word8 | (# #) #) toWord8# (W64# w64#) =@@ -194,3 +205,4 @@ toDouble# (W64# w64#) = case leWord64# w64# (wordToWord64# 9007199254740991##) of   1# -> (# | D# (int2Double# (word2Int# (word64ToWord# w64#))) #)   _  -> (# Overflow | #)+#endif
src/Unwitch/Convert/Word8.hs view
@@ -14,10 +14,14 @@   , toInteger   , toFloat   , toDouble+#ifdef __GLASGOW_HASKELL__   , toCInt+#endif+#ifdef __GLASGOW_HASKELL__   -- * Unboxed conversions   -- $unboxed   , toInt8#+#endif   ) where @@ -25,18 +29,22 @@ import           Data.Word import           Data.Int import           Numeric.Natural (Natural)-import           Foreign.C.Types (CInt(CInt)) import           Prelude hiding (toInteger)+#ifdef __GLASGOW_HASKELL__+import           Foreign.C.Types (CInt(CInt)) import           GHC.Exts (word8ToWord#, word2Int#, intToInt8#, int8ToInt#,                            (==#)) import           GHC.Int (Int8(..)) import           GHC.Word (Word8(..))+#endif +#ifdef __GLASGOW_HASKELL__ -- $unboxed -- These use GHC unboxed types and unboxed sums for zero-allocation -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and -- @UnboxedTuples@ language extensions. -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.+#endif  toWord16 :: Word8 -> Word16 toWord16 = fromIntegral@@ -77,10 +85,13 @@ toDouble :: Word8 -> Double toDouble = fromIntegral +#ifdef __GLASGOW_HASKELL__ -- | Widening conversion via Int32, always succeeds. toCInt :: Word8 -> CInt toCInt x = CInt $ toInt32 x+#endif +#ifdef __GLASGOW_HASKELL__ -- | Unsigned->signed, source fits in Int#, roundtrip at Int# toInt8# :: Word8 -> (# Int8 | (# #) #) toInt8# (W8# w8#) =@@ -89,3 +100,4 @@   in case int8ToInt# n# ==# i# of     1# -> (# I8# n# | #)     _  -> (# | (# #) #)+#endif
test/Test/Convert/Int32Spec.hs view
@@ -20,9 +20,13 @@     it "rejects out-of-range" $       Int32.toInt8 (200 :: Int32) `shouldBe` Nothing -  describe "toInt (fallible via toIntegralSized)" $+  describe "toInt (total)" $ do     it "converts 0" $-      Int32.toInt 0 `shouldBe` Just 0+      Int32.toInt 0 `shouldBe` (0 :: Int)+    it "converts maxBound" $+      Int32.toInt maxBound `shouldBe` (2147483647 :: Int)+    it "converts minBound" $+      Int32.toInt minBound `shouldBe` (-2147483648 :: Int)    describe "toWord8 (fallible)" $ do     it "rejects negative" $
test/Test/Convert/UnboxedSpec.hs view
@@ -5,6 +5,8 @@ where  import Test.Hspec++#ifdef __GLASGOW_HASKELL__ import Test.Hspec.QuickCheck (prop)  import qualified Unwitch.Convert.Integer as Integer@@ -23,8 +25,10 @@ import Data.Int import Data.Word import Numeric.Natural (Natural)+#endif  spec :: Spec+#ifdef __GLASGOW_HASKELL__ spec = describe "Unboxed sum variants" $ do    describe "Integer" $ do@@ -152,3 +156,6 @@         (# y | #)      -> Char.fromInt x `shouldBe` Just y         (# | (# #) #)  -> Char.fromInt x `shouldBe` Nothing +#else+spec = pure ()+#endif
unwitch.cabal view
@@ -1,7 +1,7 @@ cabal-version:      3.0  name:           unwitch-version:        2.2.0+version:        3.0.0 homepage:       https://github.com/jappeace/unwitch#readme synopsis:  converts between primitives description: @@ -37,6 +37,7 @@  common common-options   default-extensions:+      CPP       EmptyCase       FlexibleContexts       FlexibleInstances@@ -58,21 +59,26 @@       TypeApplications       NumericUnderscores       ImportQualifiedPost-      UnboxedSums-      UnboxedTuples-      MagicHash -  ghc-options:-    -Wall -Wincomplete-uni-patterns-    -Wincomplete-record-updates -Widentities -Wredundant-constraints-    -Wcpp-undef -fwarn-tabs -Wpartial-fields-    -fdefer-diagnostics -Wunused-packages-    -fenable-th-splice-warnings-    -fno-omit-yields+  if impl(ghc)+    default-extensions:+        UnboxedSums+        UnboxedTuples+        MagicHash +    ghc-options:+      -Wall -Wincomplete-uni-patterns+      -Wincomplete-record-updates -Widentities -Wredundant-constraints+      -Wcpp-undef -fwarn-tabs -Wpartial-fields+      -fdefer-diagnostics -Wunused-packages+      -fenable-th-splice-warnings+      -fno-omit-yields++    build-depends:+        ghc-bignum < 2+   build-depends:       base >=4.18.0.0 && <4.23,-      ghc-bignum < 2,       bytestring >= 0.10 && < 0.13,       text >= 1.2 && < 2.2