Add instances for unsigned integral types

This commit is contained in:
Taylor Fausak 2021-04-11 10:04:53 -04:00
parent 7212448445
commit 8f6b77e67f
7 changed files with 474 additions and 111 deletions

View File

@ -18,6 +18,7 @@ module Witch
) where
import qualified Witch.Cast
import Witch.Instances ()
import qualified Witch.Lift
import qualified Witch.Utility
import qualified Witch.TryCast

View File

@ -4,56 +4,9 @@
module Witch.Cast where
import qualified Data.Coerce as Coerce
import qualified Data.Int as Int
import qualified Data.List.NonEmpty as NonEmpty
class Cast source target where
cast :: source -> target
default cast :: Coerce.Coercible source target => source -> target
cast = Coerce.coerce
instance Cast (NonEmpty.NonEmpty a) [a] where
cast = NonEmpty.toList
instance Cast Int.Int8 Int.Int16 where
cast = fromIntegral
instance Cast Int.Int8 Int.Int32 where
cast = fromIntegral
instance Cast Int.Int8 Int.Int64 where
cast = fromIntegral
instance Cast Int.Int8 Int where
cast = fromIntegral
instance Cast Int.Int8 Integer where
cast = fromIntegral
instance Cast Int.Int16 Int.Int32 where
cast = fromIntegral
instance Cast Int.Int16 Int.Int64 where
cast = fromIntegral
instance Cast Int.Int16 Int where
cast = fromIntegral
instance Cast Int.Int16 Integer where
cast = fromIntegral
instance Cast Int.Int32 Int.Int64 where
cast = fromIntegral
instance Cast Int.Int32 Integer where
cast = fromIntegral
instance Cast Int.Int64 Integer where
cast = fromIntegral
instance Cast Int Int.Int64 where
cast = fromIntegral
instance Cast Int Integer where
cast = fromIntegral

232
src/lib/Witch/Instances.hs Normal file
View File

@ -0,0 +1,232 @@
{-# OPTIONS_GHC -Wno-orphans #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Witch.Instances where
import qualified Data.Bits as Bits
import qualified Data.Int as Int
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Word as Word
import qualified Numeric.Natural as Natural
import qualified Witch.Cast as Cast
import qualified Witch.TryCast as TryCast
import qualified Witch.TryCastException as TryCastException
-- []
instance TryCast.TryCast [a] (NonEmpty.NonEmpty a) where
tryCast = maybeTryCast NonEmpty.nonEmpty
-- NonEmpty
instance Cast.Cast (NonEmpty.NonEmpty a) [a] where
cast = NonEmpty.toList
-- Int8
instance Cast.Cast Int.Int8 Int.Int16 where
cast = fromIntegral
instance Cast.Cast Int.Int8 Int.Int32 where
cast = fromIntegral
instance Cast.Cast Int.Int8 Int.Int64 where
cast = fromIntegral
instance Cast.Cast Int.Int8 Int where
cast = fromIntegral
instance Cast.Cast Int.Int8 Integer where
cast = fromIntegral
-- Int16
instance TryCast.TryCast Int.Int16 Int.Int8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance Cast.Cast Int.Int16 Int.Int32 where
cast = fromIntegral
instance Cast.Cast Int.Int16 Int.Int64 where
cast = fromIntegral
instance Cast.Cast Int.Int16 Int where
cast = fromIntegral
instance Cast.Cast Int.Int16 Integer where
cast = fromIntegral
-- Int32
instance TryCast.TryCast Int.Int32 Int.Int8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Int.Int32 Int.Int16 where
tryCast = maybeTryCast Bits.toIntegralSized
instance Cast.Cast Int.Int32 Int.Int64 where
cast = fromIntegral
instance TryCast.TryCast Int.Int32 Int where
tryCast = maybeTryCast Bits.toIntegralSized
instance Cast.Cast Int.Int32 Integer where
cast = fromIntegral
-- Int64
instance TryCast.TryCast Int.Int64 Int.Int8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Int.Int64 Int.Int16 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Int.Int64 Int.Int32 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Int.Int64 Int where
tryCast = maybeTryCast Bits.toIntegralSized
instance Cast.Cast Int.Int64 Integer where
cast = fromIntegral
-- Int
instance TryCast.TryCast Int Int.Int8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Int Int.Int16 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Int Int.Int32 where
tryCast = maybeTryCast Bits.toIntegralSized
instance Cast.Cast Int Int.Int64 where
cast = fromIntegral
instance Cast.Cast Int Integer where
cast = fromIntegral
-- Integer
instance TryCast.TryCast Integer Int.Int8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Integer Int.Int16 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Integer Int.Int32 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Integer Int.Int64 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Integer Int where
tryCast = maybeTryCast Bits.toIntegralSized
-- Word8
instance Cast.Cast Word.Word8 Word.Word16 where
cast = fromIntegral
instance Cast.Cast Word.Word8 Word.Word32 where
cast = fromIntegral
instance Cast.Cast Word.Word8 Word.Word64 where
cast = fromIntegral
instance Cast.Cast Word.Word8 Word where
cast = fromIntegral
instance Cast.Cast Word.Word8 Natural.Natural where
cast = fromIntegral
-- Word16
instance TryCast.TryCast Word.Word16 Word.Word8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance Cast.Cast Word.Word16 Word.Word32 where
cast = fromIntegral
instance Cast.Cast Word.Word16 Word.Word64 where
cast = fromIntegral
instance Cast.Cast Word.Word16 Word where
cast = fromIntegral
instance Cast.Cast Word.Word16 Natural.Natural where
cast = fromIntegral
-- Word32
instance TryCast.TryCast Word.Word32 Word.Word8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Word.Word32 Word.Word16 where
tryCast = maybeTryCast Bits.toIntegralSized
instance Cast.Cast Word.Word32 Word.Word64 where
cast = fromIntegral
instance TryCast.TryCast Word.Word32 Word where
tryCast = maybeTryCast Bits.toIntegralSized
instance Cast.Cast Word.Word32 Natural.Natural where
cast = fromIntegral
-- Word64
instance TryCast.TryCast Word.Word64 Word.Word8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Word.Word64 Word.Word16 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Word.Word64 Word.Word32 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Word.Word64 Word where
tryCast = maybeTryCast Bits.toIntegralSized
instance Cast.Cast Word.Word64 Natural.Natural where
cast = fromIntegral
-- Word
instance TryCast.TryCast Word Word.Word8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Word Word.Word16 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Word Word.Word32 where
tryCast = maybeTryCast Bits.toIntegralSized
instance Cast.Cast Word Word.Word64 where
cast = fromIntegral
instance Cast.Cast Word Natural.Natural where
cast = fromIntegral
-- Natural
instance TryCast.TryCast Natural.Natural Word.Word8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Natural.Natural Word.Word16 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Natural.Natural Word.Word32 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Natural.Natural Word.Word64 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast.TryCast Natural.Natural Word where
tryCast = maybeTryCast Bits.toIntegralSized
maybeTryCast :: (s -> Maybe t) -> s -> Either (TryCastException.TryCastException s t) t
maybeTryCast f s = case f s of
Nothing -> Left $ TryCastException.TryCastException s
Just t -> Right t

View File

@ -2,63 +2,7 @@
module Witch.TryCast where
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Bits as Bits
import qualified Data.Int as Int
import qualified Witch.TryCastException as TryCastException
class TryCast source target where
tryCast :: source -> Either (TryCastException.TryCastException source target) target
instance TryCast [a] (NonEmpty.NonEmpty a) where
tryCast = maybeTryCast NonEmpty.nonEmpty
instance TryCast Int.Int16 Int.Int8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Int.Int32 Int.Int8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Int.Int32 Int.Int16 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Int.Int32 Int where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Int.Int64 Int.Int8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Int.Int64 Int.Int16 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Int.Int64 Int.Int32 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Int.Int64 Int where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Int Int.Int8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Int Int.Int16 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Int Int.Int32 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Integer Int.Int8 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Integer Int.Int16 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Integer Int.Int32 where
tryCast = maybeTryCast Bits.toIntegralSized
instance TryCast Integer Int.Int64 where
tryCast = maybeTryCast Bits.toIntegralSized
maybeTryCast :: (s -> Maybe t) -> s -> Either (TryCastException.TryCastException s t) t
maybeTryCast f s = case f s of
Nothing -> Left $ TryCastException.TryCastException s
Just t -> Right t

View File

@ -1,21 +1,58 @@
{-# OPTIONS_GHC -Wno-missing-import-lists #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
import Data.Int
import Data.List.NonEmpty
import Data.Word
import Numeric.Natural
import Test.HUnit
import Witch
main :: IO ()
main = runTestTTAndExit $ "Witch" ~:
[ "Cast (NonEmpty a) [a]" ~:
[ cast ('a' :| []) ~?= "a"
, cast ('a' :| "b") ~?= "ab"
]
[ as @Int8 1 ~?= 1
, cast (1 :: Int8) ~?= (1 :: Int16)
, from @Int8 1 ~?= (1 :: Int)
, into @Int16 (1 :: Int8) ~?= 1
, over @String (<> "!") (Name "Simon") ~?= Name "Simon!"
, via @Int16 (1 :: Int8) ~?= (1 :: Int32)
, tryFrom @Int16 1 ~?= Right (1 :: Int8)
, tryInto @Int8 (1 :: Int16) ~?= Right 1
, unsafeCast (1 :: Int16) ~?= (1 :: Int8)
, unsafeFrom @Int16 1 ~?= (1 :: Int8)
, unsafeInto @Int8 (1 :: Int16) ~?= 1
# if MIN_VERSION_GLASGOW_HASKELL(8, 10, 1, 0)
, ($$(liftedCast (1 :: Int16)) :: Int8) ~?= 1
, ($$(liftedFrom @Int16 1) :: Int8) ~?= 1
, ($$(liftedInto @Int8 (1 :: Int16)) :: Int8) ~?= 1
# else
, ($(liftedCast (1 :: Int16)) :: Int8) ~?= 1
, ($(liftedFrom @Int16 1) :: Int8) ~?= 1
, ($(liftedInto @Int8 (1 :: Int16)) :: Int8) ~?= 1
# endif
-- []
, "TryCast [a] (NonEmpty a)" ~:
[ tryInto @(NonEmpty Char) "" ~?= Left (TryCastException "")
, tryCast "a" ~?= Right ('a' :| [])
, tryCast "ab" ~?= Right ('a' :| "b")
[ tryCast @[Int] @(NonEmpty Int) [] ~?= Left (TryCastException [])
, tryCast @[Int] @(NonEmpty Int) [1] ~?= Right (1 :| [])
, tryCast @[Int] @(NonEmpty Int) [1, 2] ~?= Right (1 :| [2])
]
-- NonEmpty
, "Cast (NonEmpty a) [a]" ~:
[ cast @(NonEmpty Int) @[Int] (1 :| []) ~?= [1]
, cast @(NonEmpty Int) @[Int] (1 :| [2]) ~?= [1, 2]
]
-- Int8
, "Cast Int8 Int16" ~:
[ cast @Int8 @Int16 0 ~?= 0
, cast @Int8 @Int16 127 ~?= 127
@ -41,6 +78,9 @@ main = runTestTTAndExit $ "Witch" ~:
, cast @Int8 @Integer 127 ~?= 127
, cast @Int8 @Integer (-128) ~?= -128
]
-- Int16
, "TryCast Int16 Int8" ~:
[ tryCast @Int16 @Int8 0 ~?= Right 0
, tryCast @Int16 @Int8 127 ~?= Right 127
@ -68,6 +108,9 @@ main = runTestTTAndExit $ "Witch" ~:
, cast @Int16 @Integer 32767 ~?= 32767
, cast @Int16 @Integer (-32768) ~?= -32768
]
-- Int32
, "TryCast Int32 Int8" ~:
[ tryCast @Int32 @Int8 0 ~?= Right 0
, tryCast @Int32 @Int8 127 ~?= Right 127
@ -99,6 +142,9 @@ main = runTestTTAndExit $ "Witch" ~:
, cast @Int32 @Integer 2147483647 ~?= 2147483647
, cast @Int32 @Integer (-2147483648) ~?= -2147483648
]
-- Int64
, "TryCast Int64 Int8" ~:
[ tryCast @Int64 @Int8 0 ~?= Right 0
, tryCast @Int64 @Int8 127 ~?= Right 127
@ -132,6 +178,9 @@ main = runTestTTAndExit $ "Witch" ~:
, cast @Int64 @Integer 9223372036854775807 ~?= 9223372036854775807
, cast @Int64 @Integer (-9223372036854775808) ~?= -9223372036854775808
]
-- Int
, "TryCast Int Int8" ~:
[ tryCast @Int @Int8 0 ~?= Right 0
, tryCast @Int @Int8 127 ~?= Right 127
@ -165,6 +214,9 @@ main = runTestTTAndExit $ "Witch" ~:
, cast @Int @Integer maxBound ~?= fromIntegral (maxBound :: Int)
, cast @Int @Integer minBound ~?= fromIntegral (minBound :: Int)
]
-- Integer
, "TryCast Integer Int8" ~:
[ tryCast @Integer @Int8 0 ~?= Right 0
, tryCast @Integer @Int8 127 ~?= Right 127
@ -193,4 +245,184 @@ main = runTestTTAndExit $ "Witch" ~:
, tryCast @Integer @Int64 (-9223372036854775808) ~?= Right (-9223372036854775808)
, tryCast @Integer @Int64 (-9223372036854775809) ~?= Left (TryCastException $ -9223372036854775809)
]
, "TryCast Integer Int" ~:
[ tryCast @Integer @Int 0 ~?= Right 0
, let x = maxBound :: Int
in tryCast @Integer @Int (fromIntegral x) ~?= Right x
, let x = toInteger (maxBound :: Int) + 1
in tryCast @Integer @Int x ~?= Left (TryCastException x)
, let x = minBound :: Int
in tryCast @Integer @Int (fromIntegral x) ~?= Right x
, let x = toInteger (minBound :: Int) - 1
in tryCast @Integer @Int x ~?= Left (TryCastException x)
]
-- Word8
, "Cast Word8 Word16" ~:
[ cast @Word8 @Word16 0 ~?= 0
, cast @Word8 @Word16 255 ~?= 255
]
, "Cast Word8 Word32" ~:
[ cast @Word8 @Word32 0 ~?= 0
, cast @Word8 @Word32 255 ~?= 255
]
, "Cast Word8 Word64" ~:
[ cast @Word8 @Word64 0 ~?= 0
, cast @Word8 @Word64 255 ~?= 255
]
, "Cast Word8 Word" ~:
[ cast @Word8 @Word 0 ~?= 0
, cast @Word8 @Word 255 ~?= 255
]
, "Cast Word8 Natural" ~:
[ cast @Word8 @Natural 0 ~?= 0
, cast @Word8 @Natural 255 ~?= 255
]
-- Word16
, "TryCast Word16 Word8" ~:
[ tryCast @Word16 @Word8 0 ~?= Right 0
, tryCast @Word16 @Word8 255 ~?= Right 255
, tryCast @Word16 @Word8 256 ~?= Left (TryCastException 256)
]
, "Cast Word16 Word32" ~:
[ cast @Word16 @Word32 0 ~?= 0
, cast @Word16 @Word32 65535 ~?= 65535
]
, "Cast Word16 Word64" ~:
[ cast @Word16 @Word64 0 ~?= 0
, cast @Word16 @Word64 65535 ~?= 65535
]
, "Cast Word16 Word" ~:
[ cast @Word16 @Word 0 ~?= 0
, cast @Word16 @Word 65535 ~?= 65535
]
, "Cast Word16 Natural" ~:
[ cast @Word16 @Natural 0 ~?= 0
, cast @Word16 @Natural 65535 ~?= 65535
]
-- Word32
, "TryCast Word32 Word8" ~:
[ tryCast @Word32 @Word8 0 ~?= Right 0
, tryCast @Word32 @Word8 255 ~?= Right 255
, tryCast @Word32 @Word8 256 ~?= Left (TryCastException 256)
]
, "TryCast Word32 Word16" ~:
[ tryCast @Word32 @Word16 0 ~?= Right 0
, tryCast @Word32 @Word16 65535 ~?= Right 65535
, tryCast @Word32 @Word16 65536 ~?= Left (TryCastException 65536)
]
, "Cast Word32 Word64" ~:
[ cast @Word32 @Word64 0 ~?= 0
, cast @Word32 @Word64 4294967295 ~?= 4294967295
]
, "TryCast Word32 Word" ~:
if toInteger (maxBound :: Word) >= 4294967295 then
[ tryCast @Word32 @Word 0 ~?= Right 0
, tryCast @Word32 @Word 4294967295 ~?= Right 4294967295
]
else [False ~? "untested"]
, "Cast Word32 Natural" ~:
[ cast @Word32 @Natural 0 ~?= 0
, cast @Word32 @Natural 4294967295 ~?= 4294967295
]
-- Word64
, "TryCast Word64 Word8" ~:
[ tryCast @Word64 @Word8 0 ~?= Right 0
, tryCast @Word64 @Word8 255 ~?= Right 255
, tryCast @Word64 @Word8 256 ~?= Left (TryCastException 256)
]
, "TryCast Word64 Word16" ~:
[ tryCast @Word64 @Word16 0 ~?= Right 0
, tryCast @Word64 @Word16 65535 ~?= Right 65535
, tryCast @Word64 @Word16 65536 ~?= Left (TryCastException 65536)
]
, "TryCast Word64 Word32" ~:
[ tryCast @Word64 @Word32 0 ~?= Right 0
, tryCast @Word64 @Word32 4294967295 ~?= Right 4294967295
, tryCast @Word64 @Word32 4294967296 ~?= Left (TryCastException 4294967296)
]
, "TryCast Word64 Word" ~:
if toInteger (maxBound :: Word) >= 18446744073709551615 then
[ tryCast @Word64 @Word 0 ~?= Right 0
, tryCast @Word64 @Word 18446744073709551615 ~?= Right 18446744073709551615
]
else [False ~? "untested"]
, "Cast Word64 Natural" ~:
[ cast @Word64 @Natural 0 ~?= 0
, cast @Word64 @Natural 18446744073709551615 ~?= 18446744073709551615
]
-- Word
, "TryCast Word Word8" ~:
[ tryCast @Word @Word8 0 ~?= Right 0
, tryCast @Word @Word8 255 ~?= Right 255
, tryCast @Word @Word8 256 ~?= Left (TryCastException 256)
]
, "TryCast Word Word16" ~:
[ tryCast @Word @Word16 0 ~?= Right 0
, tryCast @Word @Word16 65535 ~?= Right 65535
, tryCast @Word @Word16 65536 ~?= Left (TryCastException 65536)
]
, "TryCast Word Word32" ~:
if toInteger (maxBound :: Word) >= 4294967295 then
[ tryCast @Word @Word32 0 ~?= Right 0
, tryCast @Word @Word32 4294967295 ~?= Right 4294967295
, tryCast @Word @Word32 4294967296 ~?= Left (TryCastException 4294967296)
]
else [False ~? "untested"]
, "Cast Word Word64" ~:
[ cast @Word @Word64 0 ~?= 0
, cast @Word @Word64 maxBound ~?= fromIntegral (maxBound :: Word)
]
, "Cast Word Natural" ~:
[ cast @Word @Natural 0 ~?= 0
, cast @Word @Natural maxBound ~?= fromIntegral (maxBound :: Word)
]
-- Natural
, "TryCast Natural Word8" ~:
[ tryCast @Natural @Word8 0 ~?= Right 0
, tryCast @Natural @Word8 255 ~?= Right 255
, tryCast @Natural @Word8 256 ~?= Left (TryCastException 256)
]
, "TryCast Natural Word16" ~:
[ tryCast @Natural @Word16 0 ~?= Right 0
, tryCast @Natural @Word16 65535 ~?= Right 65535
, tryCast @Natural @Word16 65536 ~?= Left (TryCastException 65536)
]
, "TryCast Natural Word32" ~:
[ tryCast @Natural @Word32 0 ~?= Right 0
, tryCast @Natural @Word32 4294967295 ~?= Right 4294967295
, tryCast @Natural @Word32 4294967296 ~?= Left (TryCastException 4294967296)
]
, "TryCast Natural Word64" ~:
[ tryCast @Natural @Word64 0 ~?= Right 0
, tryCast @Natural @Word64 18446744073709551615 ~?= Right 18446744073709551615
, tryCast @Natural @Word64 18446744073709551616 ~?= Left (TryCastException 18446744073709551616)
]
, "TryCast Natural Word" ~:
[ tryCast @Natural @Word 0 ~?= Right 0
, let x = maxBound :: Word
in tryCast @Natural @Word (fromIntegral x) ~?= Right x
, let x = fromIntegral (maxBound :: Word) + 1 :: Natural
in tryCast @Natural @Word x ~?= Left (TryCastException x)
]
]
newtype Name
= Name String
deriving (Eq, Show)
instance Cast Name String
instance Cast String Name

View File

@ -1 +1 @@
resolver: lts-17.0
resolver: nightly-2021-04-06

View File

@ -25,6 +25,7 @@ library
Witch
Witch.Cast
Witch.Identity
Witch.Instances
Witch.Lift
Witch.TryCast
Witch.TryCastException