d10 0.3.0.1 → 1
raw patch · 31 files changed
+3108/−2948 lines, 31 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Data.D10.Char: (*) :: D10 -> D10 -> D10
- Data.D10.Char: (+) :: D10 -> D10 -> D10
- Data.D10.Char: (-) :: D10 -> D10 -> D10
- Data.D10.Char: D10_Unsafe :: Char -> D10
- Data.D10.Char: charD10Either :: Char -> Either String D10
- Data.D10.Char: charD10Fail :: MonadFail m => Char -> m D10
- Data.D10.Char: charD10Maybe :: Char -> Maybe D10
- Data.D10.Char: d10 :: QuasiQuoter
- Data.D10.Char: d10Char :: D10 -> Char
- Data.D10.Char: d10Exp :: Integer -> Q Exp
- Data.D10.Char: d10Int :: D10 -> Int
- Data.D10.Char: d10Integer :: D10 -> Integer
- Data.D10.Char: d10ListExp :: String -> Q Exp
- Data.D10.Char: d10ListPat :: String -> Q Pat
- Data.D10.Char: d10Nat :: D10 -> Natural
- Data.D10.Char: d10Num :: Num a => D10 -> a
- Data.D10.Char: d10Pat :: Integer -> Q Pat
- Data.D10.Char: d10Str :: D10 -> String
- Data.D10.Char: d10list :: QuasiQuoter
- Data.D10.Char: instance GHC.Classes.Eq Data.D10.Char.D10
- Data.D10.Char: instance GHC.Classes.Ord Data.D10.Char.D10
- Data.D10.Char: instance GHC.Enum.Bounded Data.D10.Char.D10
- Data.D10.Char: instance GHC.Enum.Enum Data.D10.Char.D10
- Data.D10.Char: instance GHC.Show.Show Data.D10.Char.D10
- Data.D10.Char: instance Language.Haskell.TH.Syntax.Lift Data.D10.Char.D10
- Data.D10.Char: intD10Either :: Int -> Either String D10
- Data.D10.Char: intD10Fail :: MonadFail m => Int -> m D10
- Data.D10.Char: intD10Maybe :: Int -> Maybe D10
- Data.D10.Char: intMod10 :: Int -> D10
- Data.D10.Char: integerD10Either :: Integer -> Either String D10
- Data.D10.Char: integerD10Fail :: MonadFail m => Integer -> m D10
- Data.D10.Char: integerD10Maybe :: Integer -> Maybe D10
- Data.D10.Char: integerMod10 :: Integer -> D10
- Data.D10.Char: integralD10Either :: Integral a => a -> Either String D10
- Data.D10.Char: integralD10Fail :: (Integral a, MonadFail m) => a -> m D10
- Data.D10.Char: integralD10Maybe :: Integral a => a -> Maybe D10
- Data.D10.Char: integralMod10 :: Integral a => a -> D10
- Data.D10.Char: natD10Either :: Natural -> Either String D10
- Data.D10.Char: natD10Fail :: MonadFail m => Natural -> m D10
- Data.D10.Char: natD10Maybe :: Natural -> Maybe D10
- Data.D10.Char: natMod10 :: Natural -> D10
- Data.D10.Char: newtype D10
- Data.D10.Char: strD10Either :: String -> Either String D10
- Data.D10.Char: strD10Fail :: MonadFail m => String -> m D10
- Data.D10.Char: strD10ListEither :: String -> Either String [D10]
- Data.D10.Char: strD10ListFail :: MonadFail m => String -> m [D10]
- Data.D10.Char: strD10ListMaybe :: String -> Maybe [D10]
- Data.D10.Char: strD10Maybe :: String -> Maybe D10
- Data.D10.Num: (*) :: Integral a => D10 a -> D10 a -> D10 a
- Data.D10.Num: (+) :: Integral a => D10 a -> D10 a -> D10 a
- Data.D10.Num: (-) :: Integral a => D10 a -> D10 a -> D10 a
- Data.D10.Num: D10_Unsafe :: a -> D10 a
- Data.D10.Num: charD10Either :: Num a => Char -> Either String (D10 a)
- Data.D10.Num: charD10Fail :: (Num a, MonadFail m) => Char -> m (D10 a)
- Data.D10.Num: charD10Maybe :: Num a => Char -> Maybe (D10 a)
- Data.D10.Num: d10 :: QuasiQuoter
- Data.D10.Num: d10Char :: Integral a => D10 a -> Char
- Data.D10.Num: d10Exp :: Integer -> Q Exp
- Data.D10.Num: d10Int :: Integral a => D10 a -> Int
- Data.D10.Num: d10Integer :: Integral a => D10 a -> Integer
- Data.D10.Num: d10ListExp :: String -> Q Exp
- Data.D10.Num: d10ListPat :: String -> Q Pat
- Data.D10.Num: d10Nat :: Integral a => D10 a -> Natural
- Data.D10.Num: d10Num :: (Integral b, Num a) => D10 b -> a
- Data.D10.Num: d10Pat :: Integer -> Q Pat
- Data.D10.Num: d10Str :: Integral a => D10 a -> String
- Data.D10.Num: d10list :: QuasiQuoter
- Data.D10.Num: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.D10.Num.D10 a)
- Data.D10.Num: instance GHC.Classes.Ord a => GHC.Classes.Ord (Data.D10.Num.D10 a)
- Data.D10.Num: instance GHC.Num.Num a => GHC.Enum.Bounded (Data.D10.Num.D10 a)
- Data.D10.Num: instance GHC.Real.Integral a => GHC.Enum.Enum (Data.D10.Num.D10 a)
- Data.D10.Num: instance GHC.Real.Integral a => GHC.Show.Show (Data.D10.Num.D10 a)
- Data.D10.Num: instance Language.Haskell.TH.Syntax.Lift a => Language.Haskell.TH.Syntax.Lift (Data.D10.Num.D10 a)
- Data.D10.Num: intD10Either :: Num a => Int -> Either String (D10 a)
- Data.D10.Num: intD10Fail :: (Num a, MonadFail m) => Int -> m (D10 a)
- Data.D10.Num: intD10Maybe :: Num a => Int -> Maybe (D10 a)
- Data.D10.Num: intMod10 :: Num a => Int -> D10 a
- Data.D10.Num: integerD10Either :: Num a => Integer -> Either String (D10 a)
- Data.D10.Num: integerD10Fail :: (Num a, MonadFail m) => Integer -> m (D10 a)
- Data.D10.Num: integerD10Maybe :: Num a => Integer -> Maybe (D10 a)
- Data.D10.Num: integerMod10 :: Num a => Integer -> D10 a
- Data.D10.Num: integralD10Either :: (Num b, Integral a) => a -> Either String (D10 b)
- Data.D10.Num: integralD10Fail :: (Num b, Integral a, MonadFail m) => a -> m (D10 b)
- Data.D10.Num: integralD10Maybe :: (Num b, Integral a) => a -> Maybe (D10 b)
- Data.D10.Num: integralMod10 :: (Num b, Integral a) => a -> D10 b
- Data.D10.Num: natD10Either :: Num a => Natural -> Either String (D10 a)
- Data.D10.Num: natD10Fail :: (Num a, MonadFail m) => Natural -> m (D10 a)
- Data.D10.Num: natD10Maybe :: Num a => Natural -> Maybe (D10 a)
- Data.D10.Num: natMod10 :: Num a => Natural -> D10 a
- Data.D10.Num: newtype D10 a
- Data.D10.Num: strD10Either :: Num a => String -> Either String (D10 a)
- Data.D10.Num: strD10Fail :: (Num a, MonadFail m) => String -> m (D10 a)
- Data.D10.Num: strD10ListEither :: Num a => String -> Either String [D10 a]
- Data.D10.Num: strD10ListFail :: (Num a, MonadFail m) => String -> m [D10 a]
- Data.D10.Num: strD10ListMaybe :: Num a => String -> Maybe [D10 a]
- Data.D10.Num: strD10Maybe :: Num a => String -> Maybe (D10 a)
- Data.D10.Predicate: isD10Char :: Char -> Bool
- Data.D10.Predicate: isD10Int :: Int -> Bool
- Data.D10.Predicate: isD10Integer :: Integer -> Bool
- Data.D10.Predicate: isD10Integral :: Integral a => a -> Bool
- Data.D10.Predicate: isD10ListStr :: String -> Bool
- Data.D10.Predicate: isD10Nat :: Natural -> Bool
- Data.D10.Predicate: isD10Str :: String -> Bool
- Data.D10.Safe: (*) :: D10 -> D10 -> D10
- Data.D10.Safe: (+) :: D10 -> D10 -> D10
- Data.D10.Safe: (-) :: D10 -> D10 -> D10
- Data.D10.Safe: D0 :: D10
- Data.D10.Safe: D1 :: D10
- Data.D10.Safe: D2 :: D10
- Data.D10.Safe: D3 :: D10
- Data.D10.Safe: D4 :: D10
- Data.D10.Safe: D5 :: D10
- Data.D10.Safe: D6 :: D10
- Data.D10.Safe: D7 :: D10
- Data.D10.Safe: D8 :: D10
- Data.D10.Safe: D9 :: D10
- Data.D10.Safe: charD10Either :: Char -> Either String D10
- Data.D10.Safe: charD10Fail :: MonadFail m => Char -> m D10
- Data.D10.Safe: charD10Maybe :: Char -> Maybe D10
- Data.D10.Safe: d10Char :: D10 -> Char
- Data.D10.Safe: d10Int :: D10 -> Int
- Data.D10.Safe: d10Integer :: D10 -> Integer
- Data.D10.Safe: d10ListExp :: String -> Q Exp
- Data.D10.Safe: d10ListPat :: String -> Q Pat
- Data.D10.Safe: d10Nat :: D10 -> Natural
- Data.D10.Safe: d10Num :: Num a => D10 -> a
- Data.D10.Safe: d10Str :: D10 -> String
- Data.D10.Safe: d10list :: QuasiQuoter
- Data.D10.Safe: data D10
- Data.D10.Safe: instance Data.Data.Data Data.D10.Safe.D10
- Data.D10.Safe: instance GHC.Classes.Eq Data.D10.Safe.D10
- Data.D10.Safe: instance GHC.Classes.Ord Data.D10.Safe.D10
- Data.D10.Safe: instance GHC.Enum.Bounded Data.D10.Safe.D10
- Data.D10.Safe: instance GHC.Enum.Enum Data.D10.Safe.D10
- Data.D10.Safe: instance GHC.Generics.Generic Data.D10.Safe.D10
- Data.D10.Safe: instance GHC.Show.Show Data.D10.Safe.D10
- Data.D10.Safe: instance Language.Haskell.TH.Syntax.Lift Data.D10.Safe.D10
- Data.D10.Safe: intD10Either :: Int -> Either String D10
- Data.D10.Safe: intD10Fail :: MonadFail m => Int -> m D10
- Data.D10.Safe: intD10Maybe :: Int -> Maybe D10
- Data.D10.Safe: intMod10 :: Int -> D10
- Data.D10.Safe: integerD10Either :: Integer -> Either String D10
- Data.D10.Safe: integerD10Fail :: MonadFail m => Integer -> m D10
- Data.D10.Safe: integerD10Maybe :: Integer -> Maybe D10
- Data.D10.Safe: integerMod10 :: Integer -> D10
- Data.D10.Safe: integralD10Either :: Integral a => a -> Either String D10
- Data.D10.Safe: integralD10Fail :: (Integral a, MonadFail m) => a -> m D10
- Data.D10.Safe: integralD10Maybe :: Integral a => a -> Maybe D10
- Data.D10.Safe: integralMod10 :: Integral a => a -> D10
- Data.D10.Safe: natD10Either :: Natural -> Either String D10
- Data.D10.Safe: natD10Fail :: MonadFail m => Natural -> m D10
- Data.D10.Safe: natD10Maybe :: Natural -> Maybe D10
- Data.D10.Safe: natMod10 :: Natural -> D10
- Data.D10.Safe: strD10Either :: String -> Either String D10
- Data.D10.Safe: strD10Fail :: MonadFail m => String -> m D10
- Data.D10.Safe: strD10ListEither :: String -> Either String [D10]
- Data.D10.Safe: strD10ListFail :: MonadFail m => String -> m [D10]
- Data.D10.Safe: strD10ListMaybe :: String -> Maybe [D10]
- Data.D10.Safe: strD10Maybe :: String -> Maybe D10
+ D10.Char: charD10Either :: Char -> Either String D10
+ D10.Char: charD10Fail :: MonadFail m => Char -> m D10
+ D10.Char: charD10Maybe :: Char -> Maybe D10
+ D10.Char: d10 :: QuasiQuoter
+ D10.Char: d10Char :: D10 -> Char
+ D10.Char: d10Exp :: Integer -> Q Exp
+ D10.Char: d10Int :: D10 -> Int
+ D10.Char: d10Integer :: D10 -> Integer
+ D10.Char: d10ListExp :: String -> Q Exp
+ D10.Char: d10ListPat :: String -> Q Pat
+ D10.Char: d10Nat :: D10 -> Natural
+ D10.Char: d10Num :: Num a => D10 -> a
+ D10.Char: d10Pat :: Integer -> Q Pat
+ D10.Char: d10Str :: D10 -> String
+ D10.Char: d10list :: QuasiQuoter
+ D10.Char: data D10
+ D10.Char: intD10Either :: Int -> Either String D10
+ D10.Char: intD10Fail :: MonadFail m => Int -> m D10
+ D10.Char: intD10Maybe :: Int -> Maybe D10
+ D10.Char: intMod10 :: Int -> D10
+ D10.Char: integerD10Either :: Integer -> Either String D10
+ D10.Char: integerD10Fail :: MonadFail m => Integer -> m D10
+ D10.Char: integerD10Maybe :: Integer -> Maybe D10
+ D10.Char: integerMod10 :: Integer -> D10
+ D10.Char: integralD10Either :: Integral a => a -> Either String D10
+ D10.Char: integralD10Fail :: (Integral a, MonadFail m) => a -> m D10
+ D10.Char: integralD10Maybe :: Integral a => a -> Maybe D10
+ D10.Char: integralMod10 :: Integral a => a -> D10
+ D10.Char: natD10Either :: Natural -> Either String D10
+ D10.Char: natD10Fail :: MonadFail m => Natural -> m D10
+ D10.Char: natD10Maybe :: Natural -> Maybe D10
+ D10.Char: natMod10 :: Natural -> D10
+ D10.Char: strD10Either :: String -> Either String D10
+ D10.Char: strD10Fail :: MonadFail m => String -> m D10
+ D10.Char: strD10ListEither :: String -> Either String [D10]
+ D10.Char: strD10ListFail :: MonadFail m => String -> m [D10]
+ D10.Char: strD10ListMaybe :: String -> Maybe [D10]
+ D10.Char: strD10Maybe :: String -> Maybe D10
+ D10.Char.Arithmetic: (*) :: D10 -> D10 -> D10
+ D10.Char.Arithmetic: (+) :: D10 -> D10 -> D10
+ D10.Char.Arithmetic: (-) :: D10 -> D10 -> D10
+ D10.Char.Conversions: charD10Either :: Char -> Either String D10
+ D10.Char.Conversions: charD10Fail :: MonadFail m => Char -> m D10
+ D10.Char.Conversions: charD10Maybe :: Char -> Maybe D10
+ D10.Char.Conversions: d10Char :: D10 -> Char
+ D10.Char.Conversions: d10Int :: D10 -> Int
+ D10.Char.Conversions: d10Integer :: D10 -> Integer
+ D10.Char.Conversions: d10Nat :: D10 -> Natural
+ D10.Char.Conversions: d10Num :: Num a => D10 -> a
+ D10.Char.Conversions: d10Str :: D10 -> String
+ D10.Char.Conversions: intD10Either :: Int -> Either String D10
+ D10.Char.Conversions: intD10Fail :: MonadFail m => Int -> m D10
+ D10.Char.Conversions: intD10Maybe :: Int -> Maybe D10
+ D10.Char.Conversions: intMod10 :: Int -> D10
+ D10.Char.Conversions: integerD10Either :: Integer -> Either String D10
+ D10.Char.Conversions: integerD10Fail :: MonadFail m => Integer -> m D10
+ D10.Char.Conversions: integerD10Maybe :: Integer -> Maybe D10
+ D10.Char.Conversions: integerMod10 :: Integer -> D10
+ D10.Char.Conversions: integralD10Either :: Integral a => a -> Either String D10
+ D10.Char.Conversions: integralD10Fail :: (Integral a, MonadFail m) => a -> m D10
+ D10.Char.Conversions: integralD10Maybe :: Integral a => a -> Maybe D10
+ D10.Char.Conversions: integralMod10 :: Integral a => a -> D10
+ D10.Char.Conversions: natD10Either :: Natural -> Either String D10
+ D10.Char.Conversions: natD10Fail :: MonadFail m => Natural -> m D10
+ D10.Char.Conversions: natD10Maybe :: Natural -> Maybe D10
+ D10.Char.Conversions: natMod10 :: Natural -> D10
+ D10.Char.Conversions: strD10Either :: String -> Either String D10
+ D10.Char.Conversions: strD10Fail :: MonadFail m => String -> m D10
+ D10.Char.Conversions: strD10ListEither :: String -> Either String [D10]
+ D10.Char.Conversions: strD10ListFail :: MonadFail m => String -> m [D10]
+ D10.Char.Conversions: strD10ListMaybe :: String -> Maybe [D10]
+ D10.Char.Conversions: strD10Maybe :: String -> Maybe D10
+ D10.Char.Quotes: d10 :: QuasiQuoter
+ D10.Char.Quotes: d10list :: QuasiQuoter
+ D10.Char.Splices: d10Exp :: Integer -> Q Exp
+ D10.Char.Splices: d10ListExp :: String -> Q Exp
+ D10.Char.Splices: d10ListPat :: String -> Q Pat
+ D10.Char.Splices: d10Pat :: Integer -> Q Pat
+ D10.Char.Type: data D10
+ D10.Char.Unsafe: D10_Unsafe :: Char -> D10
+ D10.Char.Unsafe: instance GHC.Classes.Eq D10.Char.Unsafe.D10
+ D10.Char.Unsafe: instance GHC.Classes.Ord D10.Char.Unsafe.D10
+ D10.Char.Unsafe: instance GHC.Enum.Bounded D10.Char.Unsafe.D10
+ D10.Char.Unsafe: instance GHC.Enum.Enum D10.Char.Unsafe.D10
+ D10.Char.Unsafe: instance GHC.Show.Show D10.Char.Unsafe.D10
+ D10.Char.Unsafe: newtype D10
+ D10.Num: charD10Either :: Num a => Char -> Either String (D10 a)
+ D10.Num: charD10Fail :: (Num a, MonadFail m) => Char -> m (D10 a)
+ D10.Num: charD10Maybe :: Num a => Char -> Maybe (D10 a)
+ D10.Num: d10 :: QuasiQuoter
+ D10.Num: d10Char :: Integral a => D10 a -> Char
+ D10.Num: d10Exp :: Integer -> Q Exp
+ D10.Num: d10Int :: Integral a => D10 a -> Int
+ D10.Num: d10Integer :: Integral a => D10 a -> Integer
+ D10.Num: d10ListExp :: String -> Q Exp
+ D10.Num: d10ListPat :: String -> Q Pat
+ D10.Num: d10Nat :: Integral a => D10 a -> Natural
+ D10.Num: d10Num :: (Integral b, Num a) => D10 b -> a
+ D10.Num: d10Pat :: Integer -> Q Pat
+ D10.Num: d10Str :: Integral a => D10 a -> String
+ D10.Num: d10list :: QuasiQuoter
+ D10.Num: data D10 a
+ D10.Num: intD10Either :: Num a => Int -> Either String (D10 a)
+ D10.Num: intD10Fail :: (Num a, MonadFail m) => Int -> m (D10 a)
+ D10.Num: intD10Maybe :: Num a => Int -> Maybe (D10 a)
+ D10.Num: intMod10 :: Num a => Int -> D10 a
+ D10.Num: integerD10Either :: Num a => Integer -> Either String (D10 a)
+ D10.Num: integerD10Fail :: (Num a, MonadFail m) => Integer -> m (D10 a)
+ D10.Num: integerD10Maybe :: Num a => Integer -> Maybe (D10 a)
+ D10.Num: integerMod10 :: Num a => Integer -> D10 a
+ D10.Num: integralD10Either :: (Num b, Integral a) => a -> Either String (D10 b)
+ D10.Num: integralD10Fail :: (Num b, Integral a, MonadFail m) => a -> m (D10 b)
+ D10.Num: integralD10Maybe :: (Num b, Integral a) => a -> Maybe (D10 b)
+ D10.Num: integralMod10 :: (Num b, Integral a) => a -> D10 b
+ D10.Num: natD10Either :: Num a => Natural -> Either String (D10 a)
+ D10.Num: natD10Fail :: (Num a, MonadFail m) => Natural -> m (D10 a)
+ D10.Num: natD10Maybe :: Num a => Natural -> Maybe (D10 a)
+ D10.Num: natMod10 :: Num a => Natural -> D10 a
+ D10.Num: strD10Either :: Num a => String -> Either String (D10 a)
+ D10.Num: strD10Fail :: (Num a, MonadFail m) => String -> m (D10 a)
+ D10.Num: strD10ListEither :: Num a => String -> Either String [D10 a]
+ D10.Num: strD10ListFail :: (Num a, MonadFail m) => String -> m [D10 a]
+ D10.Num: strD10ListMaybe :: Num a => String -> Maybe [D10 a]
+ D10.Num: strD10Maybe :: Num a => String -> Maybe (D10 a)
+ D10.Num.Arithmetic: (*) :: Integral a => D10 a -> D10 a -> D10 a
+ D10.Num.Arithmetic: (+) :: Integral a => D10 a -> D10 a -> D10 a
+ D10.Num.Arithmetic: (-) :: Integral a => D10 a -> D10 a -> D10 a
+ D10.Num.Conversions: charD10Either :: Num a => Char -> Either String (D10 a)
+ D10.Num.Conversions: charD10Fail :: (Num a, MonadFail m) => Char -> m (D10 a)
+ D10.Num.Conversions: charD10Maybe :: Num a => Char -> Maybe (D10 a)
+ D10.Num.Conversions: d10Char :: Integral a => D10 a -> Char
+ D10.Num.Conversions: d10Int :: Integral a => D10 a -> Int
+ D10.Num.Conversions: d10Integer :: Integral a => D10 a -> Integer
+ D10.Num.Conversions: d10Nat :: Integral a => D10 a -> Natural
+ D10.Num.Conversions: d10Num :: (Integral b, Num a) => D10 b -> a
+ D10.Num.Conversions: d10Str :: Integral a => D10 a -> String
+ D10.Num.Conversions: intD10Either :: Num a => Int -> Either String (D10 a)
+ D10.Num.Conversions: intD10Fail :: (Num a, MonadFail m) => Int -> m (D10 a)
+ D10.Num.Conversions: intD10Maybe :: Num a => Int -> Maybe (D10 a)
+ D10.Num.Conversions: intMod10 :: Num a => Int -> D10 a
+ D10.Num.Conversions: integerD10Either :: Num a => Integer -> Either String (D10 a)
+ D10.Num.Conversions: integerD10Fail :: (Num a, MonadFail m) => Integer -> m (D10 a)
+ D10.Num.Conversions: integerD10Maybe :: Num a => Integer -> Maybe (D10 a)
+ D10.Num.Conversions: integerMod10 :: Num a => Integer -> D10 a
+ D10.Num.Conversions: integralD10Either :: (Num b, Integral a) => a -> Either String (D10 b)
+ D10.Num.Conversions: integralD10Fail :: (Num b, Integral a, MonadFail m) => a -> m (D10 b)
+ D10.Num.Conversions: integralD10Maybe :: (Num b, Integral a) => a -> Maybe (D10 b)
+ D10.Num.Conversions: integralMod10 :: (Num b, Integral a) => a -> D10 b
+ D10.Num.Conversions: natD10Either :: Num a => Natural -> Either String (D10 a)
+ D10.Num.Conversions: natD10Fail :: (Num a, MonadFail m) => Natural -> m (D10 a)
+ D10.Num.Conversions: natD10Maybe :: Num a => Natural -> Maybe (D10 a)
+ D10.Num.Conversions: natMod10 :: Num a => Natural -> D10 a
+ D10.Num.Conversions: strD10Either :: Num a => String -> Either String (D10 a)
+ D10.Num.Conversions: strD10Fail :: (Num a, MonadFail m) => String -> m (D10 a)
+ D10.Num.Conversions: strD10ListEither :: Num a => String -> Either String [D10 a]
+ D10.Num.Conversions: strD10ListFail :: (Num a, MonadFail m) => String -> m [D10 a]
+ D10.Num.Conversions: strD10ListMaybe :: Num a => String -> Maybe [D10 a]
+ D10.Num.Conversions: strD10Maybe :: Num a => String -> Maybe (D10 a)
+ D10.Num.Quotes: d10 :: QuasiQuoter
+ D10.Num.Quotes: d10list :: QuasiQuoter
+ D10.Num.Splices: d10Exp :: Integer -> Q Exp
+ D10.Num.Splices: d10ListExp :: String -> Q Exp
+ D10.Num.Splices: d10ListPat :: String -> Q Pat
+ D10.Num.Splices: d10Pat :: Integer -> Q Pat
+ D10.Num.Type: data D10 a
+ D10.Num.Unsafe: D10_Unsafe :: a -> D10 a
+ D10.Num.Unsafe: instance GHC.Classes.Eq a => GHC.Classes.Eq (D10.Num.Unsafe.D10 a)
+ D10.Num.Unsafe: instance GHC.Classes.Ord a => GHC.Classes.Ord (D10.Num.Unsafe.D10 a)
+ D10.Num.Unsafe: instance GHC.Num.Num a => GHC.Enum.Bounded (D10.Num.Unsafe.D10 a)
+ D10.Num.Unsafe: instance GHC.Real.Integral a => GHC.Enum.Enum (D10.Num.Unsafe.D10 a)
+ D10.Num.Unsafe: instance GHC.Real.Integral a => GHC.Show.Show (D10.Num.Unsafe.D10 a)
+ D10.Num.Unsafe: newtype D10 a
+ D10.Predicate: isD10Char :: Char -> Bool
+ D10.Predicate: isD10Int :: Int -> Bool
+ D10.Predicate: isD10Integer :: Integer -> Bool
+ D10.Predicate: isD10Integral :: Integral a => a -> Bool
+ D10.Predicate: isD10ListStr :: String -> Bool
+ D10.Predicate: isD10Nat :: Natural -> Bool
+ D10.Predicate: isD10Str :: String -> Bool
+ D10.Safe: D0 :: D10
+ D10.Safe: D1 :: D10
+ D10.Safe: D2 :: D10
+ D10.Safe: D3 :: D10
+ D10.Safe: D4 :: D10
+ D10.Safe: D5 :: D10
+ D10.Safe: D6 :: D10
+ D10.Safe: D7 :: D10
+ D10.Safe: D8 :: D10
+ D10.Safe: D9 :: D10
+ D10.Safe: charD10Either :: Char -> Either String D10
+ D10.Safe: charD10Fail :: MonadFail m => Char -> m D10
+ D10.Safe: charD10Maybe :: Char -> Maybe D10
+ D10.Safe: d10Char :: D10 -> Char
+ D10.Safe: d10Int :: D10 -> Int
+ D10.Safe: d10Integer :: D10 -> Integer
+ D10.Safe: d10ListExp :: String -> Q Exp
+ D10.Safe: d10ListPat :: String -> Q Pat
+ D10.Safe: d10Nat :: D10 -> Natural
+ D10.Safe: d10Num :: Num a => D10 -> a
+ D10.Safe: d10Str :: D10 -> String
+ D10.Safe: d10list :: QuasiQuoter
+ D10.Safe: data D10
+ D10.Safe: intD10Either :: Int -> Either String D10
+ D10.Safe: intD10Fail :: MonadFail m => Int -> m D10
+ D10.Safe: intD10Maybe :: Int -> Maybe D10
+ D10.Safe: intMod10 :: Int -> D10
+ D10.Safe: integerD10Either :: Integer -> Either String D10
+ D10.Safe: integerD10Fail :: MonadFail m => Integer -> m D10
+ D10.Safe: integerD10Maybe :: Integer -> Maybe D10
+ D10.Safe: integerMod10 :: Integer -> D10
+ D10.Safe: integralD10Either :: Integral a => a -> Either String D10
+ D10.Safe: integralD10Fail :: (Integral a, MonadFail m) => a -> m D10
+ D10.Safe: integralD10Maybe :: Integral a => a -> Maybe D10
+ D10.Safe: integralMod10 :: Integral a => a -> D10
+ D10.Safe: natD10Either :: Natural -> Either String D10
+ D10.Safe: natD10Fail :: MonadFail m => Natural -> m D10
+ D10.Safe: natD10Maybe :: Natural -> Maybe D10
+ D10.Safe: natMod10 :: Natural -> D10
+ D10.Safe: strD10Either :: String -> Either String D10
+ D10.Safe: strD10Fail :: MonadFail m => String -> m D10
+ D10.Safe: strD10ListEither :: String -> Either String [D10]
+ D10.Safe: strD10ListFail :: MonadFail m => String -> m [D10]
+ D10.Safe: strD10ListMaybe :: String -> Maybe [D10]
+ D10.Safe: strD10Maybe :: String -> Maybe D10
+ D10.Safe.Arithmetic: (*) :: D10 -> D10 -> D10
+ D10.Safe.Arithmetic: (+) :: D10 -> D10 -> D10
+ D10.Safe.Arithmetic: (-) :: D10 -> D10 -> D10
+ D10.Safe.Conversions: charD10Either :: Char -> Either String D10
+ D10.Safe.Conversions: charD10Fail :: MonadFail m => Char -> m D10
+ D10.Safe.Conversions: charD10Maybe :: Char -> Maybe D10
+ D10.Safe.Conversions: d10Char :: D10 -> Char
+ D10.Safe.Conversions: d10Int :: D10 -> Int
+ D10.Safe.Conversions: d10Integer :: D10 -> Integer
+ D10.Safe.Conversions: d10Nat :: D10 -> Natural
+ D10.Safe.Conversions: d10Num :: Num a => D10 -> a
+ D10.Safe.Conversions: d10Str :: D10 -> String
+ D10.Safe.Conversions: intD10Either :: Int -> Either String D10
+ D10.Safe.Conversions: intD10Fail :: MonadFail m => Int -> m D10
+ D10.Safe.Conversions: intD10Maybe :: Int -> Maybe D10
+ D10.Safe.Conversions: intMod10 :: Int -> D10
+ D10.Safe.Conversions: integerD10Either :: Integer -> Either String D10
+ D10.Safe.Conversions: integerD10Fail :: MonadFail m => Integer -> m D10
+ D10.Safe.Conversions: integerD10Maybe :: Integer -> Maybe D10
+ D10.Safe.Conversions: integerMod10 :: Integer -> D10
+ D10.Safe.Conversions: integralD10Either :: Integral a => a -> Either String D10
+ D10.Safe.Conversions: integralD10Fail :: (Integral a, MonadFail m) => a -> m D10
+ D10.Safe.Conversions: integralD10Maybe :: Integral a => a -> Maybe D10
+ D10.Safe.Conversions: integralMod10 :: Integral a => a -> D10
+ D10.Safe.Conversions: natD10Either :: Natural -> Either String D10
+ D10.Safe.Conversions: natD10Fail :: MonadFail m => Natural -> m D10
+ D10.Safe.Conversions: natD10Maybe :: Natural -> Maybe D10
+ D10.Safe.Conversions: natMod10 :: Natural -> D10
+ D10.Safe.Conversions: strD10Either :: String -> Either String D10
+ D10.Safe.Conversions: strD10Fail :: MonadFail m => String -> m D10
+ D10.Safe.Conversions: strD10ListEither :: String -> Either String [D10]
+ D10.Safe.Conversions: strD10ListFail :: MonadFail m => String -> m [D10]
+ D10.Safe.Conversions: strD10ListMaybe :: String -> Maybe [D10]
+ D10.Safe.Conversions: strD10Maybe :: String -> Maybe D10
+ D10.Safe.Quotes: d10list :: QuasiQuoter
+ D10.Safe.Splices: d10ListExp :: String -> Q Exp
+ D10.Safe.Splices: d10ListPat :: String -> Q Pat
+ D10.Safe.Type: D0 :: D10
+ D10.Safe.Type: D1 :: D10
+ D10.Safe.Type: D2 :: D10
+ D10.Safe.Type: D3 :: D10
+ D10.Safe.Type: D4 :: D10
+ D10.Safe.Type: D5 :: D10
+ D10.Safe.Type: D6 :: D10
+ D10.Safe.Type: D7 :: D10
+ D10.Safe.Type: D8 :: D10
+ D10.Safe.Type: D9 :: D10
+ D10.Safe.Type: data D10
+ D10.Safe.Type: instance Data.Data.Data D10.Safe.Type.D10
+ D10.Safe.Type: instance GHC.Classes.Eq D10.Safe.Type.D10
+ D10.Safe.Type: instance GHC.Classes.Ord D10.Safe.Type.D10
+ D10.Safe.Type: instance GHC.Enum.Bounded D10.Safe.Type.D10
+ D10.Safe.Type: instance GHC.Enum.Enum D10.Safe.Type.D10
+ D10.Safe.Type: instance GHC.Generics.Generic D10.Safe.Type.D10
+ D10.Safe.Type: instance GHC.Show.Show D10.Safe.Type.D10
Files
- CHANGELOG.md +44/−0
- README.md +5/−29
- d10.cabal +102/−155
- src/D10/Char.hs +132/−0
- src/D10/Char/Arithmetic.hs +40/−0
- src/D10/Char/Conversions.hs +547/−0
- src/D10/Char/Quotes.hs +104/−0
- src/D10/Char/Splices.hs +102/−0
- src/D10/Char/Type.hs +5/−0
- src/D10/Char/Unsafe.hs +65/−0
- src/D10/Num.hs +138/−0
- src/D10/Num/Arithmetic.hs +41/−0
- src/D10/Num/Conversions.hs +547/−0
- src/D10/Num/Quotes.hs +104/−0
- src/D10/Num/Splices.hs +108/−0
- src/D10/Num/Type.hs +5/−0
- src/D10/Num/Unsafe.hs +64/−0
- src/D10/Predicate.hs +39/−0
- src/D10/Safe.hs +110/−0
- src/D10/Safe/Arithmetic.hs +39/−0
- src/D10/Safe/Conversions.hs +623/−0
- src/D10/Safe/Quotes.hs +56/−0
- src/D10/Safe/Splices.hs +59/−0
- src/D10/Safe/Type.hs +20/−0
- src/Data/D10/Char.hs +0/−917
- src/Data/D10/Num.hs +0/−928
- src/Data/D10/Predicate.hs +0/−37
- src/Data/D10/Safe.hs +0/−876
- test/char.hs +3/−2
- test/num.hs +3/−2
- test/safe.hs +3/−2
CHANGELOG.md view
@@ -97,3 +97,47 @@ **v0.3.0.1** * Support GHC 9.0, base 4.15, template-haskell 2.17++**v1**++Removed the `Data` prefix from module names.++ * `Data.D10.Char` is now `D10.Char`+ * `Data.D10.Num` is now `D10.Num`+ * `Data.D10.Predicate` is now `D10.Predicate`+ * `Data.D10.Safe` is now `D10.Safe`++Constructors for `D10` are no longer exported by the modules formerly+known as `Data.D10.Char` and `Data.D10.Num`. They have moved to the+new modules `D10.Char.Unsafe` and `D10.Num.Unsafe` respectively.++Operators `(+)`, `(-)`, and `(*)` have been moved into their own+separate modules, as the names conflict with `Prelude` functions.+These new modules are:++ * `D10.Char.Arithmetic`+ * `D10.Num.Arithmetic`+ * `D10.Safe.Arithmetic`++Other new modules:++ * `D10.Char.Conversions`+ * `D10.Char.Quotes`+ * `D10.Char.Splices`+ * `D10.Char.Type`+ * `D10.Num.Conversions`+ * `D10.Num.Quotes`+ * `D10.Num.Splices`+ * `D10.Num.Type`+ * `D10.Safe.Conversions`+ * `D10.Safe.Quotes`+ * `D10.Safe.Splices`+ * `D10.Safe.Type`++All instances of the `Language.Haskell.TH.Syntax.Lift` class+have been removed++Added *Safe Haskell* language flags (`Safe`, `Trustworthy`, `Unsafe`)+to indicate which modules permit constructing invalid values++Required Cabal version required is raised from 2.2 to 3.0
README.md view
@@ -4,36 +4,12 @@ ## Modules -Each of the following modules defines a different type named-`D10`, all of which are different representations of the same-concept:--* `Data.D10.Char` - Defines a `D10` type as a newtype for `Char`,- where the values are restricted to characters between `'0'` and- `'9'`.-* `Data.D10.Num` - Defines a `D10` type as a newtype for any- type with an instance of the `Num` class, where the values- are restricted to numbers between `fromInteger 0` and- `fromInteger 9`.-* `Data.D10.Safe` - Defines a `D10` type as- `D0 | D1 | D2 | ... | D9`.--Other modules:+Each of the following modules defines a different type named `D10`, all of which are different representations of the same concept: -* `Data.D10.Predicate` - Functions to test whether values of- various types represent digits in the range *0* to *9*.+* `D10.Char` - Defines a `D10` type as a newtype for `Char`, where the values are restricted to characters between `'0'` and `'9'`.+* `D10.Num` - Defines a `D10` type as a newtype for any type with an instance of the `Num` class, where the values are restricted to numbers between `fromInteger 0` and `fromInteger 9`.+* `D10.Safe` - Defines a `D10` type as `D0 | D1 | D2 | ... | D9`. ## Quasi-quoters -Each module that defines a `D10` type also defines quasi-quoters-for it. With the `QuasiQuotes` GHC extension enabled, a single-digit like *7* can be written as `[d10|7|]`, and a list of digits-like *[4,5,6]* can be written as `[d10|456|]`. For `Data.D10.Char`-and `Data.D10.Num`, the quasi-quoters are an important feature,-because the `D10` types defined in these modules have unsafe-constructors, and the quasi-quoters provide compile-time assurance-that we never construct a `D10` that represents a value outside-the range *0* to *9*. For `Data.D10.Safe`, the quasi-quoter is-offered merely as a possible convenience, allowing you to write-`[d10|456789|]` in place of the somewhat longer expression-`[D4,D5,D6,D7,D8,D9]`.+Each module that defines a `D10` type also defines quasi-quoters for it. With the `QuasiQuotes` GHC extension enabled, a single digit like *7* can be written as `[d10|7|]`, and a list of digits like *[4,5,6]* can be written as `[d10|456|]`. For `D10.Char` and `D10.Num`, the quasi-quoters are an important feature, because the `D10` types defined in these modules have unsafe constructors, and the quasi-quoters provide compile-time assurance that we never construct a `D10` that represents a value outside the range *0* to *9*.
d10.cabal view
@@ -1,49 +1,59 @@-cabal-version: 2.2+cabal-version: 3.0 name: d10-version: 0.3.0.1+version: 1 category: Data synopsis: Digits 0-9 description: Data types representing the digits zero through nine.- .+ == Modules- .+ Each of the following modules defines a different type named @D10@, all of which are different representations of the same concept:- .- * "Data.D10.Char" - Defines a @D10@ type as a newtype for @Char@,- where the values are restricted to characters between @'0'@ and- @'9'@.- * "Data.D10.Num" - Defines a @D10@ type as a newtype for any++ * "D10.Char" - Defines a @D10@ type as a newtype for @Char@,+ where the values are restricted to characters between+ @'0'@ and @'9'@+ * "D10.Num" - Defines a @D10@ type as a newtype for any type with an instance of the @Num@ class, where the values are restricted to numbers between @fromInteger 0@ and- @fromInteger 9@.- * "Data.D10.Safe" - Defines a @D10@ type as- @D0 | D1 | D2 | ... | D9@.- .- Other modules:- .- * "Data.D10.Predicate" - Functions to test whether values of- various types represent digits in the range /0/ to /9/.- .+ @fromInteger 9@+ * "D10.Safe" - Defines a @D10@ type as @D0 | D1 | D2 | ... | D9@++ The @Arithmetic@ modules provide infix operators (@+@), (@-@), (@*@)+ for modular arithmetic:++ * "D10.Char.Arithmetic"+ * "D10.Num.Arithmetic"+ * "D10.Safe.Arithmetic"++ The following modules export constructors that can be used+ incorrectly:++ * "D10.Char.Unsafe"+ * "D10.Num.Unsafe"++ Functions to test whether values of various types represent+ digits in the range 0 to 9:++ * "D10.Predicate"+ == Quasi-quoters- .- Each module that defines a @D10@ type also defines quasi-quoters- for it. With the @QuasiQuotes@ GHC extension enabled, a single- digit like /7/ can be written as @[d10|7|]@, and a list of digits- like /[4,5,6]/ can be written as @[d10|456|]@. For "Data.D10.Char"- and "Data.D10.Num", the quasi-quoters are an important feature,- because the @D10@ types defined in these modules have unsafe- constructors, and the quasi-quoters provide compile-time assurance- that we never construct a @D10@ that represents a value outside- the range /0/ to /9/. For "Data.D10.Safe", the quasi-quoter is- offered merely as a possible convenience, allowing you to write- @[d10|456789|]@ in place of the somewhat longer expression- @[D4,D5,D6,D7,D8,D9]@. + * The "D10.Char" and "D10.Num" modules include quasi-quoters+ named @d10@ used to express single digits. For example, 7+ can be written as @[d10|7|]@. This is an important feature+ because the @D10@ types defined in these modules have+ unsafe constructors, and the quasi-quoters provide+ compile-time assurance that we never construct a @D10@+ that represents a value outside the range 0 to 9.+ * "D10.Char", "D10.Num", and "D10.Safe" include quasi-quoters+ named @d10list@ to express lists of digits. For example,+ [4, 5, 6] can be written as @[d10list|456|]@.+ homepage: https://github.com/typeclasses/d10 bug-reports: https://github.com/typeclasses/d10/issues @@ -57,143 +67,80 @@ build-type: Simple tested-with:- GHC == 8.2.2- , GHC == 8.4.3- , GHC == 8.6.1- , GHC == 8.8.1- , GHC == 8.10.1- , GHC == 9.0.1+ GHC == 8.2.2, GHC == 8.4.3, GHC == 8.6.1+ , GHC == 8.8.1, GHC == 8.10.1, GHC == 9.0.1 extra-source-files: README.md CHANGELOG.md source-repository head- type: git- location: https://github.com/typeclasses/d10+ type: git+ location: https://github.com/typeclasses/d10 -library- default-language: Haskell2010- ghc-options: -Wall- hs-source-dirs: src+common language+ default-language: Haskell2010+ ghc-options: -Wall+ default-extensions:+ DeriveDataTypeable DeriveGeneric DeriveLift+ DerivingStrategies InstanceSigs QuasiQuotes+ ScopedTypeVariables TypeApplications - build-depends:- base ^>= 4.10 || ^>= 4.11 || ^>= 4.12- || ^>= 4.13 || ^>= 4.14 || ^>= 4.15- , template-haskell- ^>= 2.12 || ^>= 2.13 || ^>= 2.14- || ^>= 2.15 || ^>= 2.16 || ^>= 2.17+common library-dependencies+ build-depends:+ base ^>= 4.10 || ^>= 4.11 || ^>= 4.12+ || ^>= 4.13 || ^>= 4.14 || ^>= 4.15+ , template-haskell+ ^>= 2.12 || ^>= 2.13 || ^>= 2.14+ || ^>= 2.15 || ^>= 2.16 || ^>= 2.17 - exposed-modules:- Data.D10.Char- , Data.D10.Num- , Data.D10.Predicate- , Data.D10.Safe+common test+ import: language+ import: library-dependencies+ hs-source-dirs: test+ other-modules: AssertQFails Fallible+ build-depends: d10, hedgehog ^>= 1.0+ default-extensions: GeneralizedNewtypeDeriving+ TemplateHaskell - default-extensions:- DeriveDataTypeable- DeriveGeneric- DeriveLift- DerivingStrategies- GeneralizedNewtypeDeriving- InstanceSigs- QuasiQuotes- ScopedTypeVariables- TemplateHaskell- TypeApplications+library+ import: language+ import: library-dependencies+ hs-source-dirs: src+ exposed-modules:+ D10.Char+ , D10.Char.Arithmetic+ , D10.Char.Conversions+ , D10.Char.Quotes+ , D10.Char.Splices+ , D10.Char.Type+ , D10.Char.Unsafe+ , D10.Num+ , D10.Num.Arithmetic+ , D10.Num.Conversions+ , D10.Num.Quotes+ , D10.Num.Splices+ , D10.Num.Type+ , D10.Num.Unsafe+ , D10.Safe+ , D10.Safe.Arithmetic+ , D10.Safe.Conversions+ , D10.Safe.Quotes+ , D10.Safe.Splices+ , D10.Safe.Type+ , D10.Predicate test-suite char- default-language: Haskell2010- ghc-options: -Wall- hs-source-dirs: test- type: exitcode-stdio-1.0- main-is: char.hs-- other-modules:- AssertQFails- Fallible-- build-depends:- d10- , base ^>= 4.10 || ^>= 4.11 || ^>= 4.12- || ^>= 4.13 || ^>= 4.14 || ^>= 4.15- , hedgehog ^>= 1.0- , template-haskell- ^>= 2.12 || ^>= 2.13 || ^>= 2.14- || ^>= 2.15 || ^>= 2.16 || ^>= 2.17-- default-extensions:- DeriveDataTypeable- DeriveGeneric- DeriveLift- DerivingStrategies- GeneralizedNewtypeDeriving- InstanceSigs- QuasiQuotes- ScopedTypeVariables- TemplateHaskell- TypeApplications+ import: test+ type: exitcode-stdio-1.0+ main-is: char.hs test-suite num- default-language: Haskell2010- ghc-options: -Wall- hs-source-dirs: test- type: exitcode-stdio-1.0- main-is: num.hs-- other-modules:- AssertQFails- Fallible-- build-depends:- d10- , base ^>= 4.10 || ^>= 4.11 || ^>= 4.12- || ^>= 4.13 || ^>= 4.14 || ^>= 4.15- , hedgehog ^>= 1.0- , template-haskell- ^>= 2.12 || ^>= 2.13 || ^>= 2.14- || ^>= 2.15 || ^>= 2.16 || ^>= 2.17-- default-extensions:- DeriveDataTypeable- DeriveGeneric- DeriveLift- DerivingStrategies- GeneralizedNewtypeDeriving- InstanceSigs- QuasiQuotes- ScopedTypeVariables- TemplateHaskell- TypeApplications+ import: test+ type: exitcode-stdio-1.0+ main-is: num.hs test-suite safe- default-language: Haskell2010- ghc-options: -Wall- hs-source-dirs: test- type: exitcode-stdio-1.0- main-is: safe.hs-- other-modules:- AssertQFails- Fallible-- build-depends:- d10- , base ^>= 4.10 || ^>= 4.11 || ^>= 4.12- || ^>= 4.13 || ^>= 4.14 || ^>= 4.15- , hedgehog ^>= 1.0- , template-haskell- ^>= 2.12 || ^>= 2.13 || ^>= 2.14- || ^>= 2.15 || ^>= 2.16 || ^>= 2.17-- default-extensions:- DeriveDataTypeable- DeriveGeneric- DeriveLift- DerivingStrategies- GeneralizedNewtypeDeriving- InstanceSigs- QuasiQuotes- ScopedTypeVariables- TemplateHaskell- TypeApplications+ import: test+ type: exitcode-stdio-1.0+ main-is: safe.hs
+ src/D10/Char.hs view
@@ -0,0 +1,132 @@+{-# language Safe #-}++{- |++Defines a 'D10' type as a newtype for 'Char', where the+values are restricted to characters between @'0'@ and @'9'@.++This module provides many functions for constructing 'D10'+values, including:++ * @'integerD10Maybe' :: 'Integer' -> 'Maybe' 'D10'@+ * @'integerMod10' :: 'Integer' -> 'D10'@++With the @QuasiQuotes@ GHC extension enabled, you can write+'D10' literals using the quasi-quoters 'd10' and 'd10list'.++-}++module D10.Char+ (+ -- * Related modules+ -- $modules++ -- * Type+ D10++ -- ** Bounded+ -- $bounded++ -- ** Enum+ -- $enum++ -- ** Show+ -- $show++ -- * Quasi-quoters+ , d10, d10list++ -- * Splices+ -- ** Expressions+ , d10Exp, d10ListExp+ -- ** Patterns+ , d10Pat, d10ListPat++ -- * Conversions+ -- ** D10 / Char+ , d10Char, charD10Maybe, charD10Either, charD10Fail+ -- ** D10 / String+ , d10Str, strD10Maybe, strD10Either, strD10Fail+ -- ** [D10] / String+ , strD10ListMaybe, strD10ListEither, strD10ListFail+ -- ** D10 / Natural+ , d10Nat, natD10Maybe, natD10Either, natD10Fail+ , natMod10+ -- ** D10 / Integer+ , d10Integer, integerD10Maybe, integerD10Either+ , integerD10Fail, integerMod10+ -- ** D10 / Int+ , d10Int, intD10Maybe, intD10Either+ , intD10Fail, intMod10+ -- ** D10 / general numeric types+ , d10Num, integralD10Maybe, integralD10Either+ , integralD10Fail, integralMod10++ ) where++import D10.Char.Conversions+import D10.Char.Type+import D10.Char.Splices+import D10.Char.Quotes++{- $modules++Additional functions related to this 'D10' type may be found in:++ * "D10.Char.Arithmetic"++The contents of the following modules are re-exported here:++ * "D10.Char.Conversions"+ * "D10.Char.Splices"+ * "D10.Char.Quotes"+ * "D10.Char.Type"++The unsafe constructor for 'D10' can be found in:++ * "D10.Char.Unsafe"++The following modules define @D10@ types in different ways+but are otherwise very similar to this one:++ * "D10.Num"+ * "D10.Safe"++-}++{- $bounded++>>> minBound :: D10+[d10|0|]++>>> maxBound :: D10+[d10|9|]++-}++{- $enum++>>> [ [d10|5|] .. ]+[d10list|56789|]++>>> [ [d10|4|] .. [d10|7|] ]+[d10list|4567|]++>>> [ [d10|5|], [d10|4|] .. ]+[d10list|543210|]++>>> [ [d10|1|], [d10|3|] .. ]+[d10list|13579|]++>>> [ minBound .. maxBound ] :: [D10]+[d10list|0123456789|]++-}++{- $show++'show' shows base-10 digits using the quasiquoters defined+in this module. A single digit is displayed using 'd10'.+A list of digits is displayed using 'd10list'.++-}
+ src/D10/Char/Arithmetic.hs view
@@ -0,0 +1,40 @@+{-# language Safe #-}++module D10.Char.Arithmetic where++import qualified Prelude as P++import D10.Char (D10, d10Int, intMod10)++-- | Addition modulo 10.+--+-- >>> [d10|2|] + [d10|3|]+-- [d10|5|]+--+-- >>> [d10|6|] + [d10|7|]+-- [d10|3|]++(+) :: D10 -> D10 -> D10+x + y = intMod10 (d10Int x P.+ d10Int y)++-- | Subtraction modulo 10.+--+-- >>> [d10|7|] - [d10|5|]+-- [d10|2|]+--+-- >>> [d10|3|] - [d10|7|]+-- [d10|6|]++(-) :: D10 -> D10 -> D10+x - y = intMod10 (d10Int x P.- d10Int y)++-- | Multiplication modulo 10.+--+-- >>> [d10|2|] * [d10|4|]+-- [d10|8|]+--+-- >>> [d10|7|] * [d10|8|]+-- [d10|6|]++(*) :: D10 -> D10 -> D10+x * y = intMod10 (d10Int x P.* d10Int y)
+ src/D10/Char/Conversions.hs view
@@ -0,0 +1,547 @@+{-# language Trustworthy #-}++module D10.Char.Conversions+ (+ -- * D10 / Char+ d10Char, charD10Maybe, charD10Either, charD10Fail+ -- * D10 / String+ , d10Str, strD10Maybe, strD10Either, strD10Fail+ -- * [D10] / String+ , strD10ListMaybe, strD10ListEither, strD10ListFail+ -- * D10 / Natural+ , d10Nat, natD10Maybe, natD10Either, natD10Fail+ , natMod10+ -- * D10 / Integer+ , d10Integer, integerD10Maybe, integerD10Either+ , integerD10Fail, integerMod10+ -- * D10 / Int+ , d10Int, intD10Maybe, intD10Either+ , intD10Fail, intMod10+ -- * D10 / general numeric types+ , d10Num, integralD10Maybe, integralD10Either+ , integralD10Fail, integralMod10+ ) where++import D10.Char.Unsafe (D10(..))+import D10.Predicate (isD10Char, isD10Int, isD10Integer, isD10Nat)++-- base+import Control.Monad.Fail (MonadFail (fail))+import Data.Char (chr, ord)+import Numeric.Natural (Natural)+import Prelude hiding (fail)++import qualified Prelude as P++-- | Convert a 'D10' to its underlying 'Char' representation.+--+-- >>> d10Char [d10|7|]+-- '7'++d10Char :: D10 -> Char+d10Char (D10_Unsafe x) = x++-- | Convert a 'D10' to a 'String'.+--+-- @'d10Str' x = ['d10Char' x]@+--+-- >>> d10Str [d10|7|]+-- "7"++d10Str :: D10 -> String+d10Str (D10_Unsafe x) = [x]++-- | Convert a 'D10' to a 'Natural'.+--+-- 'd10Num' is a more general version of this function.+--+-- >>> d10Nat [d10|7|]+-- 7++d10Nat :: D10 -> Natural+d10Nat (D10_Unsafe x) = fromIntegral (ord x P.- ord '0')++-- | Convert a 'D10' to an 'Integer'.+--+-- 'd10Num' is a more general version of this function.+--+-- >>> d10Integer [d10|7|]+-- 7++d10Integer :: D10 -> Integer+d10Integer (D10_Unsafe x) = toInteger (ord x P.- ord '0')++-- | Convert a 'D10' to an 'Int'.+--+-- 'd10Num' is a more general version of this function.+--+-- >>> d10Int [d10|7|]+-- 7++d10Int :: D10 -> Int+d10Int (D10_Unsafe x) = ord x P.- ord '0'++-- | Convert a 'D10' to any kind of number with a 'Num' instance.+--+-- Specialized versions of this function include 'd10Nat',+-- 'd10Integer', and 'd10Int'.+--+-- >>> d10Num [d10|7|] :: Integer+-- 7++d10Num :: Num a => D10 -> a+d10Num (D10_Unsafe x) = fromIntegral (ord x P.- ord '0')++---------------------------------------------------++-- | The 'D10' which is uniquely congruent modulo 10 to the given 'Natural'.+--+-- 'integralMod10' is a more general version of this function.+--+-- >>> natMod10 56+-- [d10|6|]++natMod10 :: Natural -> D10+natMod10 x = D10_Unsafe (chr (ord '0' P.+ fromIntegral (x `mod` 10)))++-- | The 'D10' which is uniquely congruent modulo 10 to the given 'Integer'.+--+-- 'integralMod10' is a more general version of this function.+--+-- >>> integerMod10 56+-- [d10|6|]+--+-- >>> integerMod10 (-56)+-- [d10|4|]++integerMod10 :: Integer -> D10+integerMod10 x = D10_Unsafe (chr (ord '0' P.+ fromInteger (x `mod` 10)))++-- | The 'D10' which is uniquely congruent modulo 10 to the given 'Int'.+--+-- 'integralMod10' is a more general version of this function.+--+-- >>> intMod10 56+-- [d10|6|]+--+-- >>> intMod10 (-56)+-- [d10|4|]++intMod10 :: Int -> D10+intMod10 x = D10_Unsafe (chr (ord '0' P.+ (x `mod` 10)))++-- | The 'D10' which is uniquely congruent modulo 10 to the given number+-- (whose type must have an instance of the 'Integral' class).+--+-- Specialized versions of this function include 'natMod10',+-- 'integerMod10', and 'intMod10'.+--+-- >>> integralMod10 (56 :: Integer)+-- [d10|6|]+--+-- >>> integralMod10 ((-56) :: Integer)+-- [d10|4|]++integralMod10 :: Integral a => a -> D10+integralMod10 x = D10_Unsafe (chr (ord '0' P.+ fromIntegral (x `mod` 10)))++---------------------------------------------------++-- | Convert a 'Char' to a 'D10' if it is within the range+-- @'0'@ to @'9'@, or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Char' x = 'Data.Maybe.isJust' ('charD10Maybe' x)@+--+-- 'charD10Fail' is a more general version of this function.+--+-- >>> charD10Maybe '5'+-- Just [d10|5|]+--+-- >>> charD10Maybe 'a'+-- Nothing++charD10Maybe :: Char -> Maybe D10+charD10Maybe x+ | isD10Char x = Just (D10_Unsafe x)+ | otherwise = Nothing++-- | Convert a 'String' to a 'D10' if it consists of exactly one+-- character and that character is within the range @'0'@ to @'9'@,+-- or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Str' x = 'Data.Maybe.isJust' ('strD10Maybe' x)@+--+-- 'strD10Fail' is a more general version of this function.+--+-- >>> strD10Maybe "5"+-- Just [d10|5|]+--+-- >>> strD10Maybe "a"+-- Nothing+--+-- >>> strD10Maybe "58"+-- Nothing++strD10Maybe :: String -> Maybe D10+strD10Maybe [x] = charD10Maybe x+strD10Maybe _ = Nothing++-- | Convert a 'String' to a list of 'D10' if all of the characters+-- in the string are within the range @'0'@ to @'9'@, or produce+-- 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10ListStr' x = 'Data.Maybe.isJust' ('strD10ListMaybe' x)@+--+-- 'strD10ListFail' is a more general version of this function.+--+-- >>> strD10ListMaybe "5"+-- Just [d10list|5|]+--+-- >>> strD10ListMaybe "a"+-- Nothing+--+-- >>> strD10ListMaybe "58"+-- Just [d10list|58|]++strD10ListMaybe :: String -> Maybe [D10]+strD10ListMaybe = traverse charD10Maybe++-- | Convert a 'Natural' to a 'D10' if it is less than 10,+-- or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Nat' x = 'Data.Maybe.isJust' ('natD10Maybe' x)@+--+-- 'integralD10Maybe', 'natD10Fail', and 'integralD10Fail'+-- are more general versions of this function.+--+-- >>> natD10Maybe 5+-- Just [d10|5|]+--+-- >>> natD10Maybe 12+-- Nothing++natD10Maybe :: Natural -> Maybe D10+natD10Maybe x+ | isD10Nat x = Just (D10_Unsafe (chr (fromIntegral x P.+ ord '0')))+ | otherwise = Nothing++-- | Convert an 'Integer' to a 'D10' if it is within the range 0 to 9,+-- or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Integer' x = 'Data.Maybe.isJust' ('integerD10Maybe' x)@+--+-- 'integralD10Maybe', 'integerD10Fail', and 'integralD10Fail'+-- are more general versions of this function.+--+-- >>> integerD10Maybe 5+-- Just [d10|5|]+--+-- >>> integerD10Maybe 12+-- Nothing+--+-- >>> integerD10Maybe (-5)+-- Nothing++integerD10Maybe :: Integer -> Maybe D10+integerD10Maybe x+ | isD10Integer x = Just (D10_Unsafe (chr (fromInteger x P.+ ord '0')))+ | otherwise = Nothing++-- | Convert an 'Int' to a 'D10' if it is within the range 0 to 9,+-- or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Int' x = 'Data.Maybe.isJust' ('intD10Maybe' x)@+--+-- 'integralD10Maybe', 'intD10Fail', and 'integralD10Fail'+-- are more general versions of this function.+--+-- >>> intD10Maybe 5+-- Just [d10|5|]+--+-- >>> intD10Maybe 12+-- Nothing+--+-- >>> intD10Maybe (-5)+-- Nothing++intD10Maybe :: Int -> Maybe D10+intD10Maybe x+ | isD10Int x = Just (D10_Unsafe (chr (x P.+ ord '0')))+ | otherwise = Nothing++-- | Construct a 'D10' from any kind of number with an 'Integral'+-- instance, or produce 'Nothing' if the number falls outside the+-- range 0 to 9.+--+-- @'D10.Predicate.isD10Integral' x = 'Data.Maybe.isJust' ('integralD10Maybe' x)@+--+-- Specialized versions of this function include 'natD10Maybe',+-- 'integerD10Maybe', and 'intD10Maybe'.+--+-- 'integralD10Fail' is a more general version of this function.+--+-- >>> integralD10Maybe (5 :: Integer)+-- Just [d10|5|]+--+-- >>> integralD10Maybe (12 :: Integer)+-- Nothing+--+-- >>> integralD10Maybe ((-5) :: Integer)+-- Nothing++integralD10Maybe :: Integral a => a -> Maybe D10+integralD10Maybe x = integerD10Maybe (toInteger x)++---------------------------------------------------++-- | Convert a 'Char' to a 'D10' if it is within the range+-- @'0'@ to @'9'@, or 'Left' with an error message otherwise.+--+-- >>> charD10Either '5'+-- Right [d10|5|]+--+-- >>> charD10Either 'a'+-- Left "d10 must be between 0 and 9"++charD10Either :: Char -> Either String D10+charD10Either x+ | isD10Char x = Right (D10_Unsafe x)+ | otherwise = Left "d10 must be between 0 and 9"++-- | Convert a 'String' to a 'D10' if it consists of a single+-- character and that character is within the range @'0'@ to+-- @'9'@, or 'Left' with an error message otherwise.+--+-- >>> strD10Either "5"+-- Right [d10|5|]+--+-- >>> strD10Either "a"+-- Left "d10 must be between 0 and 9"+--+-- >>> strD10Either "58"+-- Left "d10 must be a single character"++strD10Either :: String -> Either String D10+strD10Either [x] = charD10Either x+strD10Either _ = Left "d10 must be a single character"++-- | Convert a 'String' to a 'D10' if all of the characters in+-- the string fall within the range @'0'@ to @'9'@, or 'Left'+-- with an error message otherwise.+--+-- >>> strD10ListEither "5"+-- Right [d10list|5|]+--+-- >>> strD10ListEither "a"+-- Left "d10 must be between 0 and 9"+--+-- >>> strD10ListEither "58"+-- Right [d10list|58|]++strD10ListEither :: String -> Either String [D10]+strD10ListEither = traverse charD10Either++-- | Convert a 'Natural' to a 'D10' if it is less than 10,+-- or 'Left' with an error message otherwise.+--+-- >>> natD10Either 5+-- Right [d10|5|]+--+-- >>> natD10Either 12+-- Left "d10 must be less than 10"++natD10Either :: Natural -> Either String D10+natD10Either x =+ case (natD10Maybe x) of+ Just y -> Right y+ Nothing -> Left "d10 must be less than 10"++-- | Convert an 'Integer' to a 'D10' if it is within the+-- range 0 to 9, or 'Left' with an error message otherwise.+--+-- >>> integerD10Either 5+-- Right [d10|5|]+--+-- >>> integerD10Either 12+-- Left "d10 must be between 0 and 9"+--+-- >>> integerD10Either (-5)+-- Left "d10 must be between 0 and 9"++integerD10Either :: Integer -> Either String D10+integerD10Either x =+ case (integerD10Maybe x) of+ Just y -> Right y+ Nothing -> Left "d10 must be between 0 and 9"++-- | Convert an 'Int' to a 'D10' if it is within the range+-- 0 to 9, or 'Left' with an error message otherwise.+--+-- >>> intD10Either 5+-- Right [d10|5|]+--+-- >>> intD10Either 12+-- Left "d10 must be between 0 and 9"+--+-- >>> intD10Either (-5)+-- Left "d10 must be between 0 and 9"++intD10Either :: Int -> Either String D10+intD10Either x =+ case (intD10Maybe x) of+ Just y -> Right y+ Nothing -> Left "d10 must be between 0 and 9"++-- | Convert a number of a type that has an 'Integral' instance+-- to a 'D10' if it falls within the range 0 to 9, or 'Left'+-- with an error message otherwise.+--+-- >>> integralD10Either (5 :: Integer)+-- Right [d10|5|]+--+-- >>> integralD10Either (12 :: Integer)+-- Left "d10 must be between 0 and 9"+--+-- >>> integralD10Either ((-5) :: Integer)+-- Left "d10 must be between 0 and 9"++integralD10Either :: Integral a => a -> Either String D10+integralD10Either x = integerD10Either (toInteger x)++---------------------------------------------------++-- | Convert a 'Char' to a 'D10' if it is within the range+-- @'0'@ to @'9'@, or 'fail' with an error message otherwise.+--+-- 'charD10Maybe' is a specialized version of this function.+--+-- >>> charD10Fail '5' :: IO D10+-- [d10|5|]+--+-- >>> charD10Fail 'a' :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)++charD10Fail :: MonadFail m => Char -> m D10+charD10Fail x+ | isD10Char x = return (D10_Unsafe x)+ | otherwise = fail "d10 must be between 0 and 9"++-- | Convert a 'String' to a 'D10' if it consists of a single+-- character and that character is within the range @'0'@ to+-- @'9'@, or 'fail' with an error message otherwise.+--+-- 'strD10Maybe' is a specialized version of this function.+--+-- >>> strD10Fail "5" :: IO D10+-- [d10|5|]+--+-- >>> strD10Fail "a" :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> strD10Fail "58" :: IO D10+-- *** Exception: user error (d10 must be a single character)++strD10Fail :: MonadFail m => String -> m D10+strD10Fail [x] = charD10Fail x+strD10Fail _ = fail "d10 must be a single character"++-- | Convert a 'String' to a 'D10' if all of the characters in+-- the string fall within the range @'0'@ to @'9'@, or 'fail'+-- with an error message otherwise.+--+-- 'strD10ListMaybe' is a specialized version of this function.+--+-- >>> strD10ListFail "5" :: IO [D10]+-- [d10list|5|]+--+-- >>> strD10ListFail "a" :: IO [D10]+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> strD10ListFail "58" :: IO [D10]+-- [d10list|58|]++strD10ListFail :: MonadFail m => String -> m [D10]+strD10ListFail = traverse charD10Fail++-- | Convert a 'Natural' to a 'D10' if it is less than 10,+-- or 'fail' with an error message otherwise.+--+-- 'natD10Maybe' is a specialized version of this function.+--+-- 'integralD10Fail' is a more general version of this function.+--+-- >>> natD10Fail 5 :: IO D10+-- [d10|5|]+--+-- >>> natD10Fail 12 :: IO D10+-- *** Exception: user error (d10 must be less than 10)++natD10Fail :: MonadFail m => Natural -> m D10+natD10Fail x =+ case (natD10Maybe x) of+ Just y -> return y+ Nothing -> fail "d10 must be less than 10"++-- | Convert an 'Integer' to a 'D10' if it is within the+-- range 0 to 9, or 'fail' with an error message otherwise.+--+-- 'integerD10Maybe' is a specialized version of this function.+--+-- 'integralD10Fail' is a more general version of this function.+--+-- >>> integerD10Fail 5 :: IO D10+-- [d10|5|]+--+-- >>> integerD10Fail 12 :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> integerD10Fail (-5) :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)++integerD10Fail :: MonadFail m => Integer -> m D10+integerD10Fail x =+ case (integerD10Maybe x) of+ Just y -> return y+ Nothing -> fail "d10 must be between 0 and 9"++-- | Convert an 'Int' to a 'D10' if it is within the range+-- 0 to 9, or 'fail' with an error message otherwise.+--+-- 'intD10Maybe' is a specialized version of this function.+--+-- 'integralD10Fail' is a more general version of this function.+--+-- >>> intD10Fail 5 :: IO D10+-- [d10|5|]+--+-- >>> intD10Fail 12 :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> intD10Fail (-5) :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)++intD10Fail :: MonadFail m => Int -> m D10+intD10Fail x =+ case (intD10Maybe x) of+ Just y -> return y+ Nothing -> fail "d10 must be between 0 and 9"++-- | Convert a number of a type that has an 'Integral' instance+-- to a 'D10' if it falls within the range 0 to 9, or 'fail'+-- with an error message otherwise.+--+-- 'natD10Maybe', 'integerD10Maybe', 'intD10Maybe',+-- 'integralD10Maybe', 'natD10Fail', 'integerD10Fail', and+-- 'intD10Fail' are all specialized versions of this function.+--+-- >>> integralD10Fail (5 :: Integer) :: IO D10+-- [d10|5|]+--+-- >>> integralD10Fail (12 :: Integer) :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> integralD10Fail ((-5) :: Integer) :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)++integralD10Fail :: (Integral a, MonadFail m) => a -> m D10+integralD10Fail x = integerD10Fail (toInteger x)
+ src/D10/Char/Quotes.hs view
@@ -0,0 +1,104 @@+{-# language Safe #-}++module D10.Char.Quotes where++import D10.Char.Conversions+import D10.Char.Splices++import Control.Monad ((>=>))+import Control.Monad.Fail (MonadFail (fail))+import Language.Haskell.TH.Quote (QuasiQuoter (..))+import Prelude hiding (fail)++-- | A single base-10 digit.+--+-- This quasi-quoter, when used as an expression, produces a+-- value of type 'D10'.+--+-- >>> d10Nat [d10|5|]+-- 5+--+-- >>> d10Nat [d10|a|]+-- ...+-- ... d10 must be between 0 and 9+-- ...+--+-- >>> d10Nat [d10|58|]+-- ...+-- ... d10 must be a single character+-- ...+--+-- This quasi-quoter can also be used as a pattern.+--+-- >>> :{+-- case (charD10Maybe '5') of+-- Just [d10|4|] -> "A"+-- Just [d10|5|] -> "B"+-- _ -> "C"+-- >>> :}+-- "B"+--+-- >>> :{+-- case (charD10Maybe '5') of+-- Just [d10|x|] -> "A"+-- Just [d10|5|] -> "B"+-- _ -> "C"+-- >>> :}+-- ...+-- ... d10 must be between 0 and 9+-- ...++d10 :: QuasiQuoter+d10 = QuasiQuoter+ { quoteExp = strD10Fail >=> (d10Exp . d10Integer)+ , quotePat = strD10Fail >=> (d10Pat . d10Integer)+ , quoteType = \_ -> fail "d10 cannot be used in a type context"+ , quoteDec = \_ -> fail "d10 cannot be used in a declaration context"+ }++-- | A list of base-10 digits.+--+-- This quasi-quoter, when used as an expression, produces a+-- value of type @['D10']@.+--+-- >>> d10Nat <$> [d10list||]+-- []+--+-- >>> d10Nat <$> [d10list|5|]+-- [5]+--+-- >>> d10Nat <$> [d10list|58|]+-- [5,8]+--+-- >>> d10Nat <$> [d10list|a|]+-- ...+-- ... d10 must be between 0 and 9+-- ...+--+-- This quasi-quoter can also be used as a pattern.+--+-- >>> :{+-- case [d10list|56|] of+-- [d10list|41|] -> "A"+-- [d10list|56|] -> "B"+-- _ -> "C"+-- >>> :}+-- "B"+--+-- >>> :{+-- case [d10list|56|] of+-- [d10list|4x|] -> "A"+-- [d10list|56|] -> "B"+-- _ -> "C"+-- >>> :}+-- ...+-- ... d10 must be between 0 and 9+-- ...++d10list :: QuasiQuoter+d10list = QuasiQuoter+ { quoteExp = d10ListExp+ , quotePat = d10ListPat+ , quoteType = \_ -> fail "d10list cannot be used in a type context"+ , quoteDec = \_ -> fail "d10list cannot be used in a declaration context"+ }
+ src/D10/Char/Splices.hs view
@@ -0,0 +1,102 @@+{-# language Trustworthy, TemplateHaskell #-}++module D10.Char.Splices+ (+ -- * Expressions+ d10Exp, d10ListExp+ -- * Patterns+ , d10Pat, d10ListPat+ ) where++import D10.Char.Conversions+import D10.Char.Unsafe++import Control.Monad ((>=>))+import Language.Haskell.TH.Lib (appE, charL, conE, litE, litP)+import Language.Haskell.TH.Syntax (Exp (..), Pat (..), Q)++-- | Produces an expression of type 'D10' that can be used+-- in a Template Haskell splice.+--+-- >>> d10Nat $(d10Exp 5)+-- 5+--+-- >>> d10Nat $(d10Exp 12)+-- ...+-- ... d10 must be between 0 and 9+-- ...+--+-- You may also be interested in 'd10', a quasi-quoter which+-- does something similar.++d10Exp :: Integer -> Q Exp+d10Exp = integerD10Fail >=> d10Exp'++d10Exp' :: D10 -> Q Exp+d10Exp' x = conE 'D10_Unsafe `appE` litE (charL (d10Char x))++-- | Produces an expression of type @['D10']@ that can be used+-- in a Template Haskell splice.+--+-- >>> d10Nat <$> $(d10ListExp "")+-- []+--+-- >>> d10Nat <$> $(d10ListExp "5")+-- [5]+--+-- >>> d10Nat <$> $(d10ListExp "58")+-- [5,8]+--+-- >>> d10Nat <$> $(d10ListExp "a")+-- ...+-- ... d10 must be between 0 and 9+-- ...+--+-- You may also be interested in 'd10list', a quasi-quoter which+-- does something similar.++d10ListExp :: String -> Q Exp+d10ListExp = strD10ListFail >=> d10ListExp'++d10ListExp' :: [D10] -> Q Exp+d10ListExp' =+ foldr+ (\x e -> conE '(:) `appE` d10Exp' x `appE` e)+ (conE '[])++---------------------------------------------------++-- | Produces a pattern that can be used in a splice+-- to match a particular 'D10' value.+--+-- >>> :{+-- case (charD10Maybe '5') of+-- Just $(d10Pat 4) -> "A"+-- Just $(d10Pat 5) -> "B"+-- _ -> "C"+-- >>> :}+-- "B"+--+-- You may wish to use the 'd10' quasi-quoter instead.++d10Pat :: Integer -> Q Pat+d10Pat = integerD10Fail >=> d10Pat'++d10Pat' :: D10 -> Q Pat+d10Pat' (D10_Unsafe x) = [p| D10_Unsafe $(litP $ charL x) |]++-- | Produces a pattern that can be used in a splice+-- to match a particular list of 'D10' values.+--+-- >>> :{+-- case (strD10ListMaybe "56") of+-- Just $(d10ListPat "42") -> "A"+-- Just $(d10ListPat "56") -> "B"+-- _ -> "C"+-- >>> :}+-- "B"+--+-- You may wish to use the 'd10list' quasi-quoter instead.++d10ListPat :: String -> Q Pat+d10ListPat = strD10ListFail >=> foldr (\x p -> [p| $(d10Pat' x) : $(p) |]) [p| [] |]
+ src/D10/Char/Type.hs view
@@ -0,0 +1,5 @@+{-# language Trustworthy #-}++module D10.Char.Type (D10) where++import D10.Char.Unsafe
+ src/D10/Char/Unsafe.hs view
@@ -0,0 +1,65 @@+{-# language Unsafe, GeneralizedNewtypeDeriving #-}++module D10.Char.Unsafe (D10 (D10_Unsafe)) where++import qualified D10.Predicate as Predicate++import Data.Char (chr, ord)+import Data.Monoid (Endo (..))++---------------------------------------------------++-- | A 'Char' value between @'0'@ and @'9'@+newtype D10 =+ D10_Unsafe Char+ -- ^ The constructor's name include the word "unsafe" as a reminder+ -- that you should generally avoid using it directly, because it+ -- allows constructing invalid 'D10' values.+ deriving (Eq, Ord)++---------------------------------------------------++instance Bounded D10+ where+ minBound = D10_Unsafe '0'+ maxBound = D10_Unsafe '9'++---------------------------------------------------++instance Enum D10+ where+ fromEnum :: D10 -> Int+ fromEnum (D10_Unsafe x) = ord x - ord '0'++ toEnum :: Int -> D10+ toEnum x | Predicate.isD10Int x = D10_Unsafe (chr (x + ord '0'))+ | otherwise = error "d10 must be between 0 and 9"++ enumFrom :: D10 -> [D10]+ enumFrom x = enumFromTo x maxBound++ enumFromThen :: D10 -> D10 -> [D10]+ enumFromThen x y = enumFromThenTo x y bound+ where+ bound | fromEnum y >= fromEnum x = maxBound+ | otherwise = minBound++ succ :: D10 -> D10+ succ (D10_Unsafe '9') = error "D10 overflow"+ succ (D10_Unsafe x) = D10_Unsafe (succ x)++ pred :: D10 -> D10+ pred (D10_Unsafe '0') = error "D10 underflow"+ pred (D10_Unsafe x) = D10_Unsafe (pred x)++---------------------------------------------------++instance Show D10 where+ showsPrec _ x = showString "[d10|" . showsChar x . showString "|]"+ showList xs = showString "[d10list|" . showsStr xs . showString "|]"++showsChar :: D10 -> ShowS+showsChar (D10_Unsafe x) = showChar x++showsStr :: [D10] -> ShowS+showsStr = appEndo . foldMap (Endo . showsChar)
+ src/D10/Num.hs view
@@ -0,0 +1,138 @@+{-# language Safe #-}++{- |++Defines a 'D10' type as a newtype for any type with an+instance of the 'Num' class, where the values are restricted+to numbers between @'fromInteger' 0@ and @'fromInteger' 9@.++This module provides many functions for constructing 'D10'+values, including:++ * @'integerD10Maybe' :: 'Num' a => 'Integer' -> 'Maybe' ('D10' a)@+ * @'integerMod10' :: 'Num' a => 'Integer' -> 'D10' a@++There are also several ways to safely write 'D10' literals+using Template Haskell:++ * With the @QuasiQuotes@ GHC extension enabled, you can write+ use the quasi-quoters 'd10' and 'd10list'.+ * With the @TemplateHaskell@ GHC extension enabled, you can+ splice expressions produced by 'd10Exp' and 'd10ListExp'.++-}++module D10.Num+ (+ -- * Related modules+ -- $modules++ -- * Type+ D10++ -- ** Bounded+ -- $bounded++ -- ** Enum+ -- $enum++ -- ** Show+ -- $show++ -- * Quasi-quoters+ , d10, d10list++ -- * Splices+ -- ** Expressions+ , d10Exp, d10ListExp+ -- ** Patterns+ , d10Pat, d10ListPat++ -- * Conversions+ -- ** D10 / Char+ , d10Char, charD10Maybe, charD10Either, charD10Fail+ -- ** D10 / String+ , d10Str, strD10Maybe, strD10Either, strD10Fail+ -- ** [D10] / String+ , strD10ListMaybe, strD10ListEither, strD10ListFail+ -- ** D10 / Natural+ , d10Nat, natD10Maybe, natD10Either, natD10Fail+ , natMod10+ -- ** D10 / Integer+ , d10Integer, integerD10Maybe, integerD10Either+ , integerD10Fail, integerMod10+ -- ** D10 / Int+ , d10Int, intD10Maybe, intD10Either, intD10Fail+ , intMod10+ -- ** D10 / general numeric types+ , d10Num, integralD10Maybe, integralD10Either+ , integralD10Fail, integralMod10++ ) where++import D10.Num.Conversions+import D10.Num.Type+import D10.Num.Quotes+import D10.Num.Splices++{- $modules++Additional functions related to this 'D10' type may be found in:++ * "D10.Num.Arithmetic"++The contents of the following modules are re-exported here:++ * "D10.Num.Conversions"+ * "D10.Num.Quotes"+ * "D10.Num.Splices"+ * "D10.Num.Type"++The unsafe constructor for 'D10' can be found in:++ * "D10.Num.Unsafe"++The following modules define @D10@ types in different ways+but are otherwise very similar to this one:++ * "D10.Char"+ * "D10.Safe"++-}++{- $bounded++>>> minBound :: D10 Integer+[d10|0|]++>>> maxBound :: D10 Integer+[d10|9|]++-}++{- $enum++>>> [ [d10|5|] .. ]+[d10list|56789|]++>>> [ [d10|4|] .. [d10|7|] ]+[d10list|4567|]++>>> [ [d10|5|], [d10|4|] .. ]+[d10list|543210|]++>>> [ [d10|1|], [d10|3|] .. ]+[d10list|13579|]++>>> [ minBound .. maxBound ] :: [D10 Integer]+[d10list|0123456789|]++-}++{- $show++'show' shows base-10 digits using the quasiquoters defined+in this module. A single digit is displayed using 'd10'.+A list of digits is displayed using 'd10list'.++-}
+ src/D10/Num/Arithmetic.hs view
@@ -0,0 +1,41 @@+{-# language Safe #-}++module D10.Num.Arithmetic where++import D10.Num (D10, d10Int, intMod10)++import qualified Prelude as P+import Prelude (Integral)++-- | Addition modulo 10.+--+-- >>> [d10|2|] + [d10|3|]+-- [d10|5|]+--+-- >>> [d10|6|] + [d10|7|]+-- [d10|3|]++(+) :: Integral a => D10 a -> D10 a -> D10 a+x + y = intMod10 (d10Int x P.+ d10Int y)++-- | Subtraction modulo 10.+--+-- >>> [d10|7|] - [d10|5|]+-- [d10|2|]+--+-- >>> [d10|3|] - [d10|7|]+-- [d10|6|]++(-) :: Integral a => D10 a -> D10 a -> D10 a+x - y = intMod10 (d10Int x P.- d10Int y)++-- | Multiplication modulo 10.+--+-- >>> [d10|2|] * [d10|4|]+-- [d10|8|]+--+-- >>> [d10|7|] * [d10|8|]+-- [d10|6|]++(*) :: Integral a => D10 a -> D10 a -> D10 a+x * y = intMod10 (d10Int x P.* d10Int y)
+ src/D10/Num/Conversions.hs view
@@ -0,0 +1,547 @@+{-# language Trustworthy #-}++module D10.Num.Conversions+ (+ -- * D10 / Char+ d10Char, charD10Maybe, charD10Either, charD10Fail+ -- * D10 / String+ , d10Str, strD10Maybe, strD10Either, strD10Fail+ -- * [D10] / String+ , strD10ListMaybe, strD10ListEither, strD10ListFail+ -- * D10 / Natural+ , d10Nat, natD10Maybe, natD10Either, natD10Fail+ , natMod10+ -- * D10 / Integer+ , d10Integer, integerD10Maybe, integerD10Either+ , integerD10Fail, integerMod10+ -- * D10 / Int+ , d10Int, intD10Maybe, intD10Either, intD10Fail+ , intMod10+ -- * D10 / general numeric types+ , d10Num, integralD10Maybe, integralD10Either+ , integralD10Fail, integralMod10+ ) where++import D10.Num.Unsafe (D10(..))+import D10.Predicate (isD10Char, isD10Int, isD10Integer, isD10Nat)++-- base+import Control.Monad.Fail (MonadFail (fail))+import Data.Char (chr, ord)+import Numeric.Natural (Natural)+import Prelude hiding (fail)++import qualified Prelude as P++-- | Convert a 'D10' to its underlying 'Char' representation.+--+-- >>> d10Char [d10|7|]+-- '7'++d10Char :: Integral a => D10 a -> Char+d10Char (D10_Unsafe x) = chr (ord '0' P.+ fromIntegral x)++-- | Convert a 'D10' to a 'String'.+--+-- @'d10Str' x = ['d10Char' x]@+--+-- >>> d10Str [d10|7|]+-- "7"++d10Str :: Integral a => D10 a -> String+d10Str x = [d10Char x]++-- | Convert a 'D10' to a 'Natural'.+--+-- 'd10Num' is a more general version of this function.+--+-- >>> d10Nat [d10|7|]+-- 7++d10Nat :: Integral a => D10 a -> Natural+d10Nat = d10Num++-- | Convert a 'D10' to an 'Integer'.+--+-- 'd10Num' is a more general version of this function.+--+-- >>> d10Integer [d10|7|]+-- 7++d10Integer :: Integral a => D10 a -> Integer+d10Integer = d10Num++-- | Convert a 'D10' to an 'Int'.+--+-- 'd10Num' is a more general version of this function.+--+-- >>> d10Int [d10|7|]+-- 7++d10Int :: Integral a => D10 a -> Int+d10Int = d10Num++-- | Convert a 'D10' to any kind of number with a 'Num' instance.+--+-- Specialized versions of this function include 'd10Nat',+-- 'd10Integer', and 'd10Int'.+--+-- >>> d10Num [d10|7|] :: Integer+-- 7++d10Num :: (Integral b, Num a) => D10 b -> a+d10Num (D10_Unsafe x) = fromIntegral x++---------------------------------------------------++-- | The 'D10' which is uniquely congruent modulo 10 to the given 'Natural'.+--+-- 'integralMod10' is a more general version of this function.+--+-- >>> natMod10 56 :: D10 Int+-- [d10|6|]++natMod10 :: Num a => Natural -> D10 a+natMod10 = integralMod10++-- | The 'D10' which is uniquely congruent modulo 10 to the given 'Integer'.+--+-- 'integralMod10' is a more general version of this function.+--+-- >>> integerMod10 56 :: D10 Int+-- [d10|6|]+--+-- >>> integerMod10 (-56) :: D10 Int+-- [d10|4|]++integerMod10 :: Num a => Integer -> D10 a+integerMod10 = integralMod10++-- | The 'D10' which is uniquely congruent modulo 10 to the given 'Int'.+--+-- 'integralMod10' is a more general version of this function.+--+-- >>> intMod10 56 :: D10 Int+-- [d10|6|]+--+-- >>> intMod10 (-56) :: D10 Int+-- [d10|4|]++intMod10 :: Num a => Int -> D10 a+intMod10 = integralMod10++-- | The 'D10' which is uniquely congruent modulo 10 to the given number+-- (whose type must have an instance of the 'Integral' class).+--+-- Specialized versions of this function include 'natMod10',+-- 'integerMod10', and 'intMod10'.+--+-- >>> integralMod10 (56 :: Integer) :: D10 Int+-- [d10|6|]+--+-- >>> integralMod10 ((-56) :: Integer) :: D10 Int+-- [d10|4|]++integralMod10 :: (Num b, Integral a) => a -> D10 b+integralMod10 x = D10_Unsafe (fromIntegral (x `mod` 10))++---------------------------------------------------++-- | Convert a 'Char' to a 'D10' if it is within the range+-- @'0'@ to @'9'@, or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Char' x = 'Data.Maybe.isJust' ('charD10Maybe' x)@+--+-- 'charD10Fail' is a more general version of this function.+--+-- >>> charD10Maybe '5'+-- Just [d10|5|]+--+-- >>> charD10Maybe 'a'+-- Nothing++charD10Maybe :: Num a => Char -> Maybe (D10 a)+charD10Maybe x+ | isD10Char x = Just (D10_Unsafe (fromIntegral (ord x P.- ord '0')))+ | otherwise = Nothing++-- | Convert a 'String' to a 'D10' if it consists of exactly one+-- character and that character is within the range @'0'@ to @'9'@,+-- or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Str' x = 'Data.Maybe.isJust' ('strD10Maybe' x)@+--+-- 'strD10Fail' is a more general version of this function.+--+-- >>> strD10Maybe "5"+-- Just [d10|5|]+--+-- >>> strD10Maybe "a"+-- Nothing+--+-- >>> strD10Maybe "58"+-- Nothing++strD10Maybe :: Num a => String -> Maybe (D10 a)+strD10Maybe [x] = charD10Maybe x+strD10Maybe _ = Nothing++-- | Convert a 'String' to a list of 'D10' if all of the characters+-- in the string are within the range @'0'@ to @'9'@, or produce+-- 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10ListStr' x = 'Data.Maybe.isJust' ('strD10ListMaybe' x)@+--+-- 'strD10ListFail' is a more general version of this function.+--+-- >>> strD10ListMaybe "5"+-- Just [d10list|5|]+--+-- >>> strD10ListMaybe "a"+-- Nothing+--+-- >>> strD10ListMaybe "58"+-- Just [d10list|58|]++strD10ListMaybe :: Num a => String -> Maybe [D10 a]+strD10ListMaybe = traverse charD10Maybe++-- | Convert a 'Natural' to a 'D10' if it is less than 10,+-- or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Nat' x = 'Data.Maybe.isJust' ('natD10Maybe' x)@+--+-- 'integralD10Maybe', 'natD10Fail', and 'integralD10Fail'+-- are more general versions of this function.+--+-- >>> natD10Maybe 5+-- Just [d10|5|]+--+-- >>> natD10Maybe 12+-- Nothing++natD10Maybe :: Num a => Natural -> Maybe (D10 a)+natD10Maybe x+ | isD10Nat x = Just (D10_Unsafe (fromIntegral x))+ | otherwise = Nothing++-- | Convert an 'Integer' to a 'D10' if it is within the range 0 to 9,+-- or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Integer' x = 'Data.Maybe.isJust' ('integerD10Maybe' x)@+--+-- 'integralD10Maybe', 'integerD10Fail', and 'integralD10Fail'+-- are more general versions of this function.+--+-- >>> integerD10Maybe 5+-- Just [d10|5|]+--+-- >>> integerD10Maybe 12+-- Nothing+--+-- >>> integerD10Maybe (-5)+-- Nothing++integerD10Maybe :: Num a => Integer -> Maybe (D10 a)+integerD10Maybe x+ | isD10Integer x = Just (D10_Unsafe (fromIntegral x))+ | otherwise = Nothing++-- | Convert an 'Int' to a 'D10' if it is within the range 0 to 9,+-- or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Int' x = 'Data.Maybe.isJust' ('intD10Maybe' x)@+--+-- 'integralD10Maybe', 'intD10Fail', and 'integralD10Fail'+-- are more general versions of this function.+--+-- >>> intD10Maybe 5+-- Just [d10|5|]+--+-- >>> intD10Maybe 12+-- Nothing+--+-- >>> intD10Maybe (-5)+-- Nothing++intD10Maybe :: Num a => Int -> Maybe (D10 a)+intD10Maybe x+ | isD10Int x = Just (D10_Unsafe (fromIntegral x))+ | otherwise = Nothing++-- | Construct a 'D10' from any kind of number with an 'Integral'+-- instance, or produce 'Nothing' if the number falls outside the+-- range 0 to 9.+--+-- @'D10.Predicate.isD10Integral' x = 'Data.Maybe.isJust' ('integralD10Maybe' x)@+--+-- Specialized versions of this function include 'natD10Maybe',+-- 'integerD10Maybe', and 'intD10Maybe'.+--+-- 'integralD10Fail' is a more general version of this function.+--+-- >>> integralD10Maybe (5 :: Integer)+-- Just [d10|5|]+--+-- >>> integralD10Maybe (12 :: Integer)+-- Nothing+--+-- >>> integralD10Maybe ((-5) :: Integer)+-- Nothing++integralD10Maybe :: (Num b, Integral a) => a -> Maybe (D10 b)+integralD10Maybe x = integerD10Maybe (toInteger x)++---------------------------------------------------++-- | Convert a 'Char' to a 'D10' if it is within the range+-- @'0'@ to @'9'@, or 'Left' with an error message otherwise.+--+-- >>> charD10Either '5'+-- Right [d10|5|]+--+-- >>> charD10Either 'a'+-- Left "d10 must be between 0 and 9"++charD10Either :: Num a => Char -> Either String (D10 a)+charD10Either x+ | isD10Char x = Right (D10_Unsafe (fromIntegral (ord x P.- ord '0')))+ | otherwise = Left "d10 must be between 0 and 9"++-- | Convert a 'String' to a 'D10' if it consists of a single+-- character and that character is within the range @'0'@ to+-- @'9'@, or 'Left' with an error message otherwise.+--+-- >>> strD10Either "5"+-- Right [d10|5|]+--+-- >>> strD10Either "a"+-- Left "d10 must be between 0 and 9"+--+-- >>> strD10Either "58"+-- Left "d10 must be a single character"++strD10Either :: Num a => String -> Either String (D10 a)+strD10Either [x] = charD10Either x+strD10Either _ = Left "d10 must be a single character"++-- | Convert a 'String' to a 'D10' if all of the characters in+-- the string fall within the range @'0'@ to @'9'@, or 'Left'+-- with an error message otherwise.+--+-- >>> strD10ListEither "5"+-- Right [d10list|5|]+--+-- >>> strD10ListEither "a"+-- Left "d10 must be between 0 and 9"+--+-- >>> strD10ListEither "58"+-- Right [d10list|58|]++strD10ListEither :: Num a => String -> Either String [D10 a]+strD10ListEither = traverse charD10Either++-- | Convert a 'Natural' to a 'D10' if it is less than 10,+-- or 'Left' with an error message otherwise.+--+-- >>> natD10Either 5+-- Right [d10|5|]+--+-- >>> natD10Either 12+-- Left "d10 must be less than 10"++natD10Either :: Num a => Natural -> Either String (D10 a)+natD10Either x =+ case (natD10Maybe x) of+ Just y -> Right y+ Nothing -> Left "d10 must be less than 10"++-- | Convert an 'Integer' to a 'D10' if it is within the+-- range 0 to 9, or 'Left' with an error message otherwise.+--+-- >>> integerD10Either 5+-- Right [d10|5|]+--+-- >>> integerD10Either 12+-- Left "d10 must be between 0 and 9"+--+-- >>> integerD10Either (-5)+-- Left "d10 must be between 0 and 9"++integerD10Either :: Num a => Integer -> Either String (D10 a)+integerD10Either x =+ case (integerD10Maybe x) of+ Just y -> Right y+ Nothing -> Left "d10 must be between 0 and 9"++-- | Convert an 'Int' to a 'D10' if it is within the range+-- 0 to 9, or 'Left' with an error message otherwise.+--+-- >>> intD10Either 5+-- Right [d10|5|]+--+-- >>> intD10Either 12+-- Left "d10 must be between 0 and 9"+--+-- >>> intD10Either (-5)+-- Left "d10 must be between 0 and 9"++intD10Either :: Num a => Int -> Either String (D10 a)+intD10Either x =+ case (intD10Maybe x) of+ Just y -> Right y+ Nothing -> Left "d10 must be between 0 and 9"++-- | Convert a number of a type that has an 'Integral' instance+-- to a 'D10' if it falls within the range 0 to 9, or 'Left'+-- with an error message otherwise.+--+-- >>> integralD10Either (5 :: Integer)+-- Right [d10|5|]+--+-- >>> integralD10Either (12 :: Integer)+-- Left "d10 must be between 0 and 9"+--+-- >>> integralD10Either ((-5) :: Integer)+-- Left "d10 must be between 0 and 9"++integralD10Either :: (Num b, Integral a) => a -> Either String (D10 b)+integralD10Either x = integerD10Either (toInteger x)++---------------------------------------------------++-- | Convert a 'Char' to a 'D10' if it is within the range+-- @'0'@ to @'9'@, or 'fail' with an error message otherwise.+--+-- 'charD10Maybe' is a specialized version of this function.+--+-- >>> charD10Fail '5' :: IO (D10 Int)+-- [d10|5|]+--+-- >>> charD10Fail 'a' :: IO (D10 Int)+-- *** Exception: user error (d10 must be between 0 and 9)++charD10Fail :: (Num a, MonadFail m) => Char -> m (D10 a)+charD10Fail x+ | isD10Char x = return (D10_Unsafe (fromIntegral (ord x P.- ord '0')))+ | otherwise = fail "d10 must be between 0 and 9"++-- | Convert a 'String' to a 'D10' if it consists of a single+-- character and that character is within the range @'0'@ to+-- @'9'@, or 'fail' with an error message otherwise.+--+-- 'strD10Maybe' is a specialized version of this function.+--+-- >>> strD10Fail "5" :: IO (D10 Int)+-- [d10|5|]+--+-- >>> strD10Fail "a" :: IO (D10 Int)+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> strD10Fail "58" :: IO (D10 Int)+-- *** Exception: user error (d10 must be a single character)++strD10Fail :: (Num a, MonadFail m) => String -> m (D10 a)+strD10Fail [x] = charD10Fail x+strD10Fail _ = fail "d10 must be a single character"++-- | Convert a 'String' to a 'D10' if all of the characters in+-- the string fall within the range @'0'@ to @'9'@, or 'fail'+-- with an error message otherwise.+--+-- 'strD10ListMaybe' is a specialized version of this function.+--+-- >>> strD10ListFail "5" :: IO [D10 Int]+-- [d10list|5|]+--+-- >>> strD10ListFail "a" :: IO [D10 Int]+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> strD10ListFail "58" :: IO [D10 Int]+-- [d10list|58|]++strD10ListFail :: (Num a, MonadFail m) => String -> m [D10 a]+strD10ListFail = traverse charD10Fail++-- | Convert a 'Natural' to a 'D10' if it is less than 10,+-- or 'fail' with an error message otherwise.+--+-- 'natD10Maybe' is a specialized version of this function.+--+-- 'integralD10Fail' is a more general version of this function.+--+-- >>> natD10Fail 5 :: IO (D10 Int)+-- [d10|5|]+--+-- >>> natD10Fail 12 :: IO (D10 Int)+-- *** Exception: user error (d10 must be less than 10)++natD10Fail :: (Num a, MonadFail m) => Natural -> m (D10 a)+natD10Fail x =+ case (natD10Maybe x) of+ Just y -> return y+ Nothing -> fail "d10 must be less than 10"++-- | Convert an 'Integer' to a 'D10' if it is within the+-- range 0 to 9, or 'fail' with an error message otherwise.+--+-- 'integerD10Maybe' is a specialized version of this function.+--+-- 'integralD10Fail' is a more general version of this function.+--+-- >>> integerD10Fail 5 :: IO (D10 Int)+-- [d10|5|]+--+-- >>> integerD10Fail 12 :: IO (D10 Int)+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> integerD10Fail (-5) :: IO (D10 Int)+-- *** Exception: user error (d10 must be between 0 and 9)++integerD10Fail :: (Num a, MonadFail m) => Integer -> m (D10 a)+integerD10Fail x =+ case (integerD10Maybe x) of+ Just y -> return y+ Nothing -> fail "d10 must be between 0 and 9"++-- | Convert an 'Int' to a 'D10' if it is within the range+-- 0 to 9, or 'fail' with an error message otherwise.+--+-- 'intD10Maybe' is a specialized version of this function.+--+-- 'integralD10Fail' is a more general version of this function.+--+-- >>> intD10Fail 5 :: IO (D10 Int)+-- [d10|5|]+--+-- >>> intD10Fail 12 :: IO (D10 Int)+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> intD10Fail (-5) :: IO (D10 Int)+-- *** Exception: user error (d10 must be between 0 and 9)++intD10Fail :: (Num a, MonadFail m) => Int -> m (D10 a)+intD10Fail x =+ case (intD10Maybe x) of+ Just y -> return y+ Nothing -> fail "d10 must be between 0 and 9"++-- | Convert a number of a type that has an 'Integral' instance+-- to a 'D10' if it falls within the range 0 to 9, or 'fail'+-- with an error message otherwise.+--+-- 'natD10Maybe', 'integerD10Maybe', 'intD10Maybe',+-- 'integralD10Maybe', 'natD10Fail', 'integerD10Fail', and+-- 'intD10Fail' are all specialized versions of this function.+--+-- >>> integralD10Fail (5 :: Integer) :: IO (D10 Int)+-- [d10|5|]+--+-- >>> integralD10Fail (12 :: Integer) :: IO (D10 Int)+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> integralD10Fail ((-5) :: Integer) :: IO (D10 Int)+-- *** Exception: user error (d10 must be between 0 and 9)++integralD10Fail :: (Num b, Integral a, MonadFail m) => a -> m (D10 b)+integralD10Fail x = integerD10Fail (toInteger x)
+ src/D10/Num/Quotes.hs view
@@ -0,0 +1,104 @@+{-# language Safe #-}++module D10.Num.Quotes where++import D10.Num.Conversions+import D10.Num.Splices++import Control.Monad ((>=>))+import Control.Monad.Fail (MonadFail (fail))+import Prelude hiding (fail)+import Language.Haskell.TH.Quote (QuasiQuoter (..))++-- | A single base-10 digit.+--+-- This quasi-quoter, when used as an expression, produces a+-- value of type @'D10' a@.+--+-- >>> d10Nat [d10|5|]+-- 5+--+-- >>> d10Nat [d10|a|]+-- ...+-- ... d10 must be between 0 and 9+-- ...+--+-- >>> d10Nat [d10|58|]+-- ...+-- ... d10 must be a single character+-- ...+--+-- This quasi-quoter can also be used as a pattern.+--+-- >>> :{+-- case (charD10Maybe '5') of+-- Just [d10|4|] -> "A"+-- Just [d10|5|] -> "B"+-- _ -> "C"+-- >>> :}+-- "B"+--+-- >>> :{+-- case (charD10Maybe '5') of+-- Just [d10|x|] -> "A"+-- Just [d10|5|] -> "B"+-- _ -> "C"+-- >>> :}+-- ...+-- ... d10 must be between 0 and 9+-- ...++d10 :: QuasiQuoter+d10 = QuasiQuoter+ { quoteExp = strD10Fail >=> (d10Exp . d10Integer @Integer)+ , quotePat = strD10Fail >=> (d10Pat . d10Integer @Integer)+ , quoteType = \_ -> fail "d10 cannot be used in a type context"+ , quoteDec = \_ -> fail "d10 cannot be used in a declaration context"+ }++-- | A list of base-10 digits.+--+-- This quasi-quoter, when used as an expression, produces a+-- value of type @['D10' a]@.+--+-- >>> d10Nat <$> [d10list||]+-- []+--+-- >>> d10Nat <$> [d10list|5|]+-- [5]+--+-- >>> d10Nat <$> [d10list|58|]+-- [5,8]+--+-- >>> d10Nat <$> [d10list|a|]+-- ...+-- ... d10 must be between 0 and 9+-- ...+--+-- This quasi-quoter can also be used as a pattern.+--+-- >>> :{+-- case [d10list|56|] of+-- [d10list|41|] -> "A"+-- [d10list|56|] -> "B"+-- _ -> "C"+-- >>> :}+-- "B"+--+-- >>> :{+-- case [d10list|56|] of+-- [d10list|4x|] -> "A"+-- [d10list|56|] -> "B"+-- _ -> "C"+-- >>> :}+-- ...+-- ... d10 must be between 0 and 9+-- ...++d10list :: QuasiQuoter+d10list = QuasiQuoter+ { quoteExp = d10ListExp+ , quotePat = d10ListPat+ , quoteType = \_ -> fail "d10list cannot be used in a type context"+ , quoteDec = \_ -> fail "d10list cannot be used in a declaration context"+ }
+ src/D10/Num/Splices.hs view
@@ -0,0 +1,108 @@+{-# language Trustworthy, TemplateHaskell #-}++module D10.Num.Splices+ (+ -- * Expressions+ d10Exp, d10ListExp+ -- * Patterns+ , d10Pat, d10ListPat+ ) where++import D10.Num.Conversions+import D10.Num.Unsafe (D10(..))++import Control.Monad ((>=>))+import Language.Haskell.TH.Lib (appE, conE, integerL, litE, litP, varE)+import Language.Haskell.TH.Syntax (Exp (..), Pat (..), Q)++-- | Produces an expression of type @'D10' a@ that can be used+-- in a Template Haskell splice.+--+-- >>> d10Nat $(d10Exp 5)+-- 5+--+-- >>> d10Nat $(d10Exp 12)+-- ...+-- ... d10 must be between 0 and 9+-- ...+--+-- You may also be interested in 'd10', a quasi-quoter which+-- does something similar.++d10Exp :: Integer -> Q Exp+d10Exp = integerD10Fail >=> d10Exp'++d10Exp' :: D10 Integer -> Q Exp+d10Exp' (D10_Unsafe x) = conE 'D10_Unsafe `appE` (varE 'fromInteger `appE` litE (integerL x))++-- | Produces an expression of type @['D10' a]@ that can be used+-- in a Template Haskell splice.+--+-- >>> d10Nat <$> $(d10ListExp "")+-- []+--+-- >>> d10Nat <$> $(d10ListExp "5")+-- [5]+--+-- >>> d10Nat <$> $(d10ListExp "58")+-- [5,8]+--+-- >>> d10Nat <$> $(d10ListExp "a")+-- ...+-- ... d10 must be between 0 and 9+-- ...+--+-- You may also be interested in 'd10list', a quasi-quoter which+-- does something similar.++d10ListExp :: String -> Q Exp+d10ListExp = strD10ListFail >=> d10ListExp'++d10ListExp' :: [D10 Integer] -> Q Exp+d10ListExp' =+ foldr+ (\x e -> conE '(:) `appE` d10Exp' x `appE` e)+ (conE '[])++---------------------------------------------------++-- | Produces a pattern that can be used in a splice+-- to match a particular @'D10' a@ value.+--+-- >>> :{+-- case (charD10Maybe '5') of+-- Just $(d10Pat 4) -> "A"+-- Just $(d10Pat 5) -> "B"+-- _ -> "C"+-- >>> :}+-- "B"+--+-- You may wish to use the 'd10' quasi-quoter instead.++d10Pat :: Integer -> Q Pat+d10Pat = integerD10Fail >=> d10Pat'++d10Pat' :: D10 Integer -> Q Pat+d10Pat' (D10_Unsafe x) = [p| D10_Unsafe $(litP $ integerL x) |]++d10Pat'' :: Integral a => D10 a -> Q Pat+d10Pat'' (D10_Unsafe x) = [p| D10_Unsafe $(litP $ integerL $ toInteger x) |]++-- | Produces a pattern that can be used in a splice+-- to match a particular list of @'D10' a@ values.+--+-- >>> :{+-- case (strD10ListMaybe "56") of+-- Just $(d10ListPat "42") -> "A"+-- Just $(d10ListPat "56") -> "B"+-- _ -> "C"+-- >>> :}+-- "B"+--+-- You may wish to use the 'd10list' quasi-quoter instead.++d10ListPat :: String -> Q Pat+d10ListPat = strD10ListFail >=> d10ListPat'++d10ListPat' :: [D10 Integer] -> Q Pat+d10ListPat' = foldr (\x p -> [p| $(d10Pat'' x) : $(p) |]) [p| [] |]
+ src/D10/Num/Type.hs view
@@ -0,0 +1,5 @@+{-# language Trustworthy #-}++module D10.Num.Type (D10) where++import D10.Num.Unsafe
+ src/D10/Num/Unsafe.hs view
@@ -0,0 +1,64 @@+{-# language Unsafe, GeneralizedNewtypeDeriving #-}++module D10.Num.Unsafe (D10 (..)) where++import qualified D10.Predicate as Predicate++import Data.Char (chr, ord)+import Data.Monoid (Endo (..))++---------------------------------------------------++-- | A value of some numeric type @a@ between+-- @'fromInteger' 0@ and @'fromInteger' 9@.+newtype D10 a =+ D10_Unsafe a+ -- ^ The constructor's name include the word "unsafe" as a reminder+ -- that you should generally avoid using it directly, because it+ -- allows constructing invalid 'D10' values.+ deriving (Eq, Ord)++---------------------------------------------------++instance Num a => Bounded (D10 a)+ where+ minBound = D10_Unsafe 0+ maxBound = D10_Unsafe 9++---------------------------------------------------++instance Integral a => Enum (D10 a)+ where+ fromEnum :: D10 a -> Int+ fromEnum (D10_Unsafe x) = fromIntegral x++ toEnum :: Int -> D10 a+ toEnum x | Predicate.isD10Int x = D10_Unsafe (fromIntegral x)+ | otherwise = error "d10 must be between 0 and 9"++ enumFrom :: D10 a -> [D10 a]+ enumFrom x = enumFromTo x maxBound++ enumFromThen :: D10 a -> D10 a -> [D10 a]+ enumFromThen x y = enumFromThenTo x y bound+ where+ bound | fromEnum y >= fromEnum x = maxBound+ | otherwise = minBound++ succ (D10_Unsafe 9) = error "D10 overflow"+ succ (D10_Unsafe x) = D10_Unsafe (succ x)++ pred (D10_Unsafe 0) = error "D10 underflow"+ pred (D10_Unsafe x) = D10_Unsafe (pred x)++---------------------------------------------------++instance Integral a => Show (D10 a) where+ showsPrec _ x = showString "[d10|" . showsChar x . showString "|]"+ showList xs = showString "[d10list|" . showsStr xs . showString "|]"++showsChar :: Integral a => D10 a -> ShowS+showsChar (D10_Unsafe x) = showChar $ chr (ord '0' + fromIntegral x)++showsStr :: Integral a => [D10 a] -> ShowS+showsStr = appEndo . foldMap (Endo . showsChar)
+ src/D10/Predicate.hs view
@@ -0,0 +1,39 @@+{-# language Safe #-}++-- | Functions to test whether values of various+-- types represent digits in the range /0/ to /9/.+module D10.Predicate where++import Numeric.Natural (Natural)++-- | Determines whether a 'Char' is in the range @'0'@ to @'9'@.+isD10Char :: Char -> Bool+isD10Char x = x >= '0' && x <= '9'++-- | Determines whether a 'String' consists of a single character+-- and that character is within the range @'0'@ to @'9'@.+isD10Str :: String -> Bool+isD10Str [x] = isD10Char x+isD10Str _ = False++-- | Determines whether a 'String' consists entirely of characters+-- that are within the range @'0'@ to @'9'@.+isD10ListStr :: String -> Bool+isD10ListStr = all isD10Char++-- | Determines whether a 'Natural' is in the range 0 to 9.+isD10Nat :: Natural -> Bool+isD10Nat x = x <= 9++-- | Determines whether an 'Integer' is in the range 0 to 9.+isD10Integer :: Integer -> Bool+isD10Integer x = x >= 0 && x <= 9++-- | Determines whether an 'Int' is in the range 0 to 9.+isD10Int :: Int -> Bool+isD10Int x = x >= 0 && x <= 9++-- | Determines whether a number whose type has an 'Integral'+-- instance is in the range 0 to 9.+isD10Integral :: Integral a => a -> Bool+isD10Integral x = isD10Integer (toInteger x)
+ src/D10/Safe.hs view
@@ -0,0 +1,110 @@+{-# language Safe #-}++{- |++Defines a 'D10' type as+@'D0' | 'D1' | 'D2' | 'D3' | 'D4' | 'D5' | 'D6' | 'D7' | 'D8' | 'D9'@.++-}++module D10.Safe+ (+ -- * Related modules+ -- $modules++ -- * Type+ D10 (..)++ -- ** Bounded+ -- $bounded++ -- ** Enum+ -- $enum++ -- * Quasi-quoters+ , d10list++ -- * Splices+ -- ** Expressions+ , d10ListExp+ -- ** Patterns+ , d10ListPat++ -- * Conversions+ -- ** D10 / Char+ , d10Char, charD10Maybe, charD10Either, charD10Fail+ -- ** D10 / String+ , d10Str, strD10Maybe, strD10Either, strD10Fail+ -- ** [D10] / String+ , strD10ListMaybe, strD10ListEither, strD10ListFail+ -- ** D10 / Natural+ , d10Nat, natD10Maybe, natD10Either, natD10Fail, natMod10+ -- ** D10 / Integer+ , d10Integer, integerD10Maybe, integerD10Either+ , integerD10Fail, integerMod10+ -- ** D10 / Int+ , d10Int, intD10Maybe, intD10Either, intD10Fail, intMod10+ -- ** D10 / general numeric types+ , d10Num, integralD10Maybe, integralD10Either+ , integralD10Fail, integralMod10+ ) where++import D10.Safe.Conversions+import D10.Safe.Quotes+import D10.Safe.Splices+import D10.Safe.Type++{- $modules++Additional functions related to this 'D10' type may be found in:++ * "D10.Safe.Arithmetic"++The contents of the following modules are re-exported here:++ * "D10.Safe.Type"+ * "D10.Safe.Conversions"+ * "D10.Safe.Quotes"+ * "D10.Safe.Splices"++The following modules define @D10@ types in different ways+but are otherwise very similar to this one:++ * "D10.Char"+ * "D10.Num"++This module is called "safe" because, in contrast with the+alternative representations of a digit defined in the other+modules, this 'D10' type does not include any possibility+of representing an invalid non-digit value.++-}++{- $bounded++-- >>> minBound :: D10+-- D0+--+-- >>> maxBound :: D10+-- D9++-}++{- $enum++>>> [ D5 .. ]+[D5,D6,D7,D8,D9]++>>> [ D4 .. D7 ]+[D4,D5,D6,D7]++>>> [ D5, D4 .. ]+[D5,D4,D3,D2,D1,D0]++>>> [ D1, D3 .. ]+[D1,D3,D5,D7,D9]++>>> [ minBound .. maxBound ] :: [D10]+[D0,D1,D2,D3,D4,D5,D6,D7,D8,D9]++-}
+ src/D10/Safe/Arithmetic.hs view
@@ -0,0 +1,39 @@+{-# language Safe #-}++module D10.Safe.Arithmetic where++import D10.Safe (D10, d10Int, intMod10)++import qualified Prelude as P++-- | Addition modulo 10.+--+-- >>> D2 + D3+-- D5+--+-- >>> D6 + D7+-- D3++(+) :: D10 -> D10 -> D10+x + y = intMod10 (d10Int x P.+ d10Int y)++-- | Subtraction modulo 10.+--+-- >>> D7 - D5+-- D2+--+-- >>> D3 - D7+-- D6++(-) :: D10 -> D10 -> D10+x - y = intMod10 (d10Int x P.- d10Int y)++-- | Multiplication modulo 10.+--+-- >>> D2 * D4+-- D8+-- >>> D7 * D8+-- D6++(*) :: D10 -> D10 -> D10+x * y = intMod10 (d10Int x P.* d10Int y)
+ src/D10/Safe/Conversions.hs view
@@ -0,0 +1,623 @@+{-# language Safe #-}++module D10.Safe.Conversions+ (+ -- * D10 / Char+ d10Char, charD10Maybe, charD10Either, charD10Fail+ -- * D10 / String+ , d10Str, strD10Maybe, strD10Either, strD10Fail+ -- * [D10] / String+ , strD10ListMaybe, strD10ListEither, strD10ListFail+ -- * D10 / Natural+ , d10Nat, natD10Maybe, natD10Either, natD10Fail, natMod10+ -- * D10 / Integer+ , d10Integer, integerD10Maybe, integerD10Either+ , integerD10Fail, integerMod10+ -- * D10 / Int+ , d10Int, intD10Maybe, intD10Either, intD10Fail, intMod10+ -- * D10 / general numeric types+ , d10Num, integralD10Maybe, integralD10Either+ , integralD10Fail, integralMod10+ ) where++import D10.Safe.Type (D10 (..))++-- base+import Control.Monad.Fail (MonadFail (fail))+import Numeric.Natural (Natural)+import Prelude hiding (fail)++-- | Convert a 'D10' to its underlying 'Char' representation.+--+-- >>> d10Char D7+-- '7'++d10Char :: D10 -> Char+d10Char x =+ case x of+ D0 -> '0'+ D1 -> '1'+ D2 -> '2'+ D3 -> '3'+ D4 -> '4'+ D5 -> '5'+ D6 -> '6'+ D7 -> '7'+ D8 -> '8'+ D9 -> '9'++-- | Convert a 'D10' to a 'String'.+--+-- @'d10Str' x = ['d10Char' x]@+--+-- >>> d10Str D7+-- "7"++d10Str :: D10 -> String+d10Str x = [d10Char x]++-- | Convert a 'D10' to a 'Natural'.+--+-- 'd10Num' is a more general version of this function.+--+-- >>> d10Nat D7+-- 7++d10Nat :: D10 -> Natural+d10Nat = d10Num++-- | Convert a 'D10' to an 'Integer'.+--+-- 'd10Num' is a more general version of this function.+--+-- >>> d10Integer D7+-- 7++d10Integer :: D10 -> Integer+d10Integer = d10Num++-- | Convert a 'D10' to an 'Int'.+--+-- 'd10Num' is a more general version of this function.+--+-- >>> d10Int D7+-- 7++d10Int :: D10 -> Int+d10Int = d10Num++-- | Convert a 'D10' to any kind of number with a 'Num' instance.+--+-- Specialized versions of this function include 'd10Nat',+-- 'd10Integer', and 'd10Int'.+--+-- >>> d10Num D7 :: Integer+-- 7++d10Num :: Num a => D10 -> a+d10Num x =+ case x of+ D0 -> 0+ D1 -> 1+ D2 -> 2+ D3 -> 3+ D4 -> 4+ D5 -> 5+ D6 -> 6+ D7 -> 7+ D8 -> 8+ D9 -> 9++---------------------------------------------------++-- | The 'D10' which is uniquely congruent modulo 10 to the given 'Natural'.+--+-- 'integralMod10' is a more general version of this function.+--+-- >>> natMod10 56+-- D6++natMod10 :: Natural -> D10+natMod10 = integralMod10++-- | The 'D10' which is uniquely congruent modulo 10 to the given 'Integer'.+--+-- 'integralMod10' is a more general version of this function.+--+-- >>> integerMod10 56+-- D6+--+-- >>> integerMod10 (-56)+-- D4++integerMod10 :: Integer -> D10+integerMod10 = integralMod10++-- | The 'D10' which is uniquely congruent modulo 10 to the given 'Int'.+--+-- 'integralMod10' is a more general version of this function.+--+-- >>> intMod10 56+-- D6+--+-- >>> intMod10 (-56)+-- D4++intMod10 :: Int -> D10+intMod10 = integralMod10++-- | The 'D10' which is uniquely congruent modulo 10 to the given number+-- (whose type must have an instance of the 'Integral' class).+--+-- Specialized versions of this function include 'natMod10',+-- 'integerMod10', and 'intMod10'.+--+-- >>> integralMod10 (56 :: Integer)+-- D6+--+-- >>> integralMod10 ((-56) :: Integer)+-- D4++integralMod10 :: Integral a => a -> D10+integralMod10 x =+ case (x `mod` 10) of+ 0 -> D0+ 1 -> D1+ 2 -> D2+ 3 -> D3+ 4 -> D4+ 5 -> D5+ 6 -> D6+ 7 -> D7+ 8 -> D8+ 9 -> D9+ _ -> error "x `mod` 10 is not between 0 and 9"++---------------------------------------------------++-- | Convert a 'Char' to a 'D10' if it is within the range+-- @'0'@ to @'9'@, or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Char' x = 'Data.Maybe.isJust' ('charD10Maybe' x)@+--+-- 'charD10Fail' is a more general version of this function.+--+-- >>> charD10Maybe '5'+-- Just D5+--+-- >>> charD10Maybe 'a'+-- Nothing++charD10Maybe :: Char -> Maybe D10+charD10Maybe x =+ case x of+ '0' -> Just D0+ '1' -> Just D1+ '2' -> Just D2+ '3' -> Just D3+ '4' -> Just D4+ '5' -> Just D5+ '6' -> Just D6+ '7' -> Just D7+ '8' -> Just D8+ '9' -> Just D9+ _ -> Nothing++-- | Convert a 'String' to a 'D10' if it consists of exactly one+-- character and that character is within the range @'0'@ to @'9'@,+-- or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Str' x = 'Data.Maybe.isJust' ('strD10Maybe' x)@+--+-- 'strD10Fail' is a more general version of this function.+--+-- >>> strD10Maybe "5"+-- Just D5+--+-- >>> strD10Maybe "a"+-- Nothing+--+-- >>> strD10Maybe "58"+-- Nothing++strD10Maybe :: String -> Maybe D10+strD10Maybe [x] = charD10Maybe x+strD10Maybe _ = Nothing++-- | Convert a 'String' to a list of 'D10' if all of the characters+-- in the string are within the range @'0'@ to @'9'@, or produce+-- 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10ListStr' x = 'Data.Maybe.isJust' ('strD10ListMaybe' x)@+--+-- 'strD10ListFail' is a more general version of this function.+--+-- >>> strD10ListMaybe "5"+-- Just [D5]+--+-- >>> strD10ListMaybe "a"+-- Nothing+--+-- >>> strD10ListMaybe "58"+-- Just [D5,D8]++strD10ListMaybe :: String -> Maybe [D10]+strD10ListMaybe = traverse charD10Maybe++-- | Convert a 'Natural' to a 'D10' if it is less than 10,+-- or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Nat' x = 'Data.Maybe.isJust' ('natD10Maybe' x)@+--+-- 'integralD10Maybe', 'natD10Fail', and 'integralD10Fail'+-- are more general versions of this function.+--+-- >>> natD10Maybe 5+-- Just D5+--+-- >>> natD10Maybe 12+-- Nothing++natD10Maybe :: Natural -> Maybe D10+natD10Maybe = integralD10Maybe++-- | Convert an 'Integer' to a 'D10' if it is within the range 0 to 9,+-- or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Integer' x = 'Data.Maybe.isJust' ('integerD10Maybe' x)@+--+-- 'integralD10Maybe', 'integerD10Fail', and 'integralD10Fail'+-- are more general versions of this function.+--+-- >>> integerD10Maybe 5+-- Just D5+--+-- >>> integerD10Maybe 12+-- Nothing+--+-- >>> integerD10Maybe (-5)+-- Nothing++integerD10Maybe :: Integer -> Maybe D10+integerD10Maybe = integralD10Maybe++-- | Convert an 'Int' to a 'D10' if it is within the range 0 to 9,+-- or produce 'Nothing' otherwise.+--+-- @'D10.Predicate.isD10Int' x = 'Data.Maybe.isJust' ('intD10Maybe' x)@+--+-- 'integralD10Maybe', 'intD10Fail', and 'integralD10Fail'+-- are more general versions of this function.+--+-- >>> intD10Maybe 5+-- Just D5+--+-- >>> intD10Maybe 12+-- Nothing+--+-- >>> intD10Maybe (-5)+-- Nothing++intD10Maybe :: Int -> Maybe D10+intD10Maybe = integralD10Maybe++-- | Construct a 'D10' from any kind of number with an 'Integral'+-- instance, or produce 'Nothing' if the number falls outside the+-- range 0 to 9.+--+-- @'D10.Predicate.isD10Integral' x = 'Data.Maybe.isJust' ('integralD10Maybe' x)@+--+-- Specialized versions of this function include 'natD10Maybe',+-- 'integerD10Maybe', and 'intD10Maybe'.+--+-- 'integralD10Fail' is a more general version of this function.+--+-- >>> integralD10Maybe (5 :: Integer)+-- Just D5+--+-- >>> integralD10Maybe (12 :: Integer)+-- Nothing+--+-- >>> integralD10Maybe ((-5) :: Integer)+-- Nothing++integralD10Maybe :: Integral a => a -> Maybe D10+integralD10Maybe x =+ case x of+ 0 -> Just D0+ 1 -> Just D1+ 2 -> Just D2+ 3 -> Just D3+ 4 -> Just D4+ 5 -> Just D5+ 6 -> Just D6+ 7 -> Just D7+ 8 -> Just D8+ 9 -> Just D9+ _ -> Nothing++---------------------------------------------------++-- | Convert a 'Char' to a 'D10' if it is within the range+-- @'0'@ to @'9'@, or 'Left' with an error message otherwise.+--+-- >>> charD10Either '5'+-- Right D5+--+-- >>> charD10Either 'a'+-- Left "d10 must be between 0 and 9"++charD10Either :: Char -> Either String D10+charD10Either x =+ case x of+ '0' -> Right D0+ '1' -> Right D1+ '2' -> Right D2+ '3' -> Right D3+ '4' -> Right D4+ '5' -> Right D5+ '6' -> Right D6+ '7' -> Right D7+ '8' -> Right D8+ '9' -> Right D9+ _ -> Left "d10 must be between 0 and 9"++-- | Convert a 'String' to a 'D10' if it consists of a single+-- character and that character is within the range @'0'@ to+-- @'9'@, or 'Left' with an error message otherwise.+--+-- >>> strD10Either "5"+-- Right D5+--+-- >>> strD10Either "a"+-- Left "d10 must be between 0 and 9"+--+-- >>> strD10Either "58"+-- Left "d10 must be a single character"++strD10Either :: String -> Either String D10+strD10Either [x] = charD10Either x+strD10Either _ = Left "d10 must be a single character"++-- | Convert a 'String' to a 'D10' if all of the characters in+-- the string fall within the range @'0'@ to @'9'@, or 'Left'+-- with an error message otherwise.+--+-- >>> strD10ListEither "5"+-- Right [D5]+--+-- >>> strD10ListEither "a"+-- Left "d10 must be between 0 and 9"+--+-- >>> strD10ListEither "58"+-- Right [D5,D8]++strD10ListEither :: String -> Either String [D10]+strD10ListEither = traverse charD10Either++-- | Convert a 'Natural' to a 'D10' if it is less than 10,+-- or 'Left' with an error message otherwise.+--+-- >>> natD10Either 5+-- Right D5+--+-- >>> natD10Either 12+-- Left "d10 must be less than 10"++natD10Either :: Natural -> Either String D10+natD10Either x =+ case x of+ 0 -> Right D0+ 1 -> Right D1+ 2 -> Right D2+ 3 -> Right D3+ 4 -> Right D4+ 5 -> Right D5+ 6 -> Right D6+ 7 -> Right D7+ 8 -> Right D8+ 9 -> Right D9+ _ -> Left "d10 must be less than 10"++-- | Convert an 'Integer' to a 'D10' if it is within the+-- range 0 to 9, or 'Left' with an error message otherwise.+--+-- >>> integerD10Either 5+-- Right D5+--+-- >>> integerD10Either 12+-- Left "d10 must be between 0 and 9"+--+-- >>> integerD10Either (-5)+-- Left "d10 must be between 0 and 9"++integerD10Either :: Integer -> Either String D10+integerD10Either = integralD10Either++-- | Convert an 'Int' to a 'D10' if it is within the range+-- 0 to 9, or 'Left' with an error message otherwise.+--+-- >>> intD10Either 5+-- Right D5+--+-- >>> intD10Either 12+-- Left "d10 must be between 0 and 9"+--+-- >>> intD10Either (-5)+-- Left "d10 must be between 0 and 9"++intD10Either :: Int -> Either String D10+intD10Either = integralD10Either++-- | Convert a number of a type that has an 'Integral' instance+-- to a 'D10' if it falls within the range 0 to 9, or 'Left'+-- with an error message otherwise.+--+-- >>> integralD10Either (5 :: Integer)+-- Right D5+--+-- >>> integralD10Either (12 :: Integer)+-- Left "d10 must be between 0 and 9"+--+-- >>> integralD10Either ((-5) :: Integer)+-- Left "d10 must be between 0 and 9"++integralD10Either :: Integral a => a -> Either String D10+integralD10Either x =+ case (integralD10Maybe x) of+ Just y -> Right y+ Nothing -> Left "d10 must be between 0 and 9"++---------------------------------------------------++-- | Convert a 'Char' to a 'D10' if it is within the range+-- @'0'@ to @'9'@, or 'fail' with an error message otherwise.+--+-- 'charD10Maybe' is a specialized version of this function.+--+-- >>> charD10Fail '5' :: IO D10+-- D5+--+-- >>> charD10Fail 'a' :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)++charD10Fail :: MonadFail m => Char -> m D10+charD10Fail x =+ case x of+ '0' -> return D0+ '1' -> return D1+ '2' -> return D2+ '3' -> return D3+ '4' -> return D4+ '5' -> return D5+ '6' -> return D6+ '7' -> return D7+ '8' -> return D8+ '9' -> return D9+ _ -> fail "d10 must be between 0 and 9"++-- | Convert a 'String' to a 'D10' if it consists of a single+-- character and that character is within the range @'0'@ to+-- @'9'@, or 'fail' with an error message otherwise.+--+-- 'strD10Maybe' is a specialized version of this function.+--+-- >>> strD10Fail "5" :: IO D10+-- D5+--+-- >>> strD10Fail "a" :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> strD10Fail "58" :: IO D10+-- *** Exception: user error (d10 must be a single character)++strD10Fail :: MonadFail m => String -> m D10+strD10Fail [x] = charD10Fail x+strD10Fail _ = fail "d10 must be a single character"++-- | Convert a 'String' to a 'D10' if all of the characters in+-- the string fall within the range @'0'@ to @'9'@, or 'fail'+-- with an error message otherwise.+--+-- 'strD10ListMaybe' is a specialized version of this function.+--+-- >>> strD10ListFail "5" :: IO [D10]+-- [D5]+--+-- >>> strD10ListFail "a" :: IO [D10]+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> strD10ListFail "58" :: IO [D10]+-- [D5,D8]++strD10ListFail :: MonadFail m => String -> m [D10]+strD10ListFail = traverse charD10Fail++-- | Convert a 'Natural' to a 'D10' if it is less than 10,+-- or 'fail' with an error message otherwise.+--+-- 'natD10Maybe' is a specialized version of this function.+--+-- 'integralD10Fail' is a more general version of this function.+--+-- >>> natD10Fail 5 :: IO D10+-- D5+--+-- >>> natD10Fail 12 :: IO D10+-- *** Exception: user error (d10 must be less than 10)++natD10Fail :: MonadFail m => Natural -> m D10+natD10Fail x =+ case x of+ 0 -> return D0+ 1 -> return D1+ 2 -> return D2+ 3 -> return D3+ 4 -> return D4+ 5 -> return D5+ 6 -> return D6+ 7 -> return D7+ 8 -> return D8+ 9 -> return D9+ _ -> fail "d10 must be less than 10"++-- | Convert an 'Integer' to a 'D10' if it is within the+-- range 0 to 9, or 'fail' with an error message otherwise.+--+-- 'integerD10Maybe' is a specialized version of this function.+--+-- 'integralD10Fail' is a more general version of this function.+--+-- >>> integerD10Fail 5 :: IO D10+-- D5+--+-- >>> integerD10Fail 12 :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> integerD10Fail (-5) :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)++integerD10Fail :: MonadFail m => Integer -> m D10+integerD10Fail = integralD10Fail++-- | Convert an 'Int' to a 'D10' if it is within the range+-- 0 to 9, or 'fail' with an error message otherwise.+--+-- 'intD10Maybe' is a specialized version of this function.+--+-- 'integralD10Fail' is a more general version of this function.+--+-- >>> intD10Fail 5 :: IO D10+-- D5+--+-- >>> intD10Fail 12 :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> intD10Fail (-5) :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)++intD10Fail :: MonadFail m => Int -> m D10+intD10Fail = integralD10Fail++-- | Convert a number of a type that has an 'Integral' instance+-- to a 'D10' if it falls within the range 0 to 9, or 'fail'+-- with an error message otherwise.+--+-- 'natD10Maybe', 'integerD10Maybe', 'intD10Maybe',+-- 'integralD10Maybe', 'natD10Fail', 'integerD10Fail', and+-- 'intD10Fail' are all specialized versions of this function.+--+-- >>> integralD10Fail (5 :: Integer) :: IO D10+-- D5+--+-- >>> integralD10Fail (12 :: Integer) :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)+--+-- >>> integralD10Fail ((-5) :: Integer) :: IO D10+-- *** Exception: user error (d10 must be between 0 and 9)++integralD10Fail :: (Integral a, MonadFail m) => a -> m D10+integralD10Fail x =+ case (integralD10Maybe x) of+ Just y -> return y+ Nothing -> fail "d10 must be between 0 and 9"
+ src/D10/Safe/Quotes.hs view
@@ -0,0 +1,56 @@+{-# language Safe #-}++module D10.Safe.Quotes (d10list) where++import D10.Safe.Splices++import Control.Monad.Fail (MonadFail (fail))+import Language.Haskell.TH.Quote (QuasiQuoter (..))+import Prelude hiding (fail)++-- | A list of base-10 digits.+--+-- This quasi-quoter, when used as an expression, produces a+-- value of type @['D10']@.+--+-- >>> [d10list||]+-- []+--+-- >>> [d10list|5|]+-- [D5]+--+-- >>> [d10list|58|]+-- [D5,D8]+--+-- >>> [d10list|a|]+-- ...+-- ... d10 must be between 0 and 9+-- ...+--+-- This quasi-quoter can also be used as a pattern.+--+-- >>> :{+-- case [D5, D6] of+-- [d10list|41|] -> "A"+-- [d10list|56|] -> "B"+-- _ -> "C"+-- >>> :}+-- "B"+--+-- >>> :{+-- case [D5, D6] of+-- [d10list|4x|] -> "A"+-- [d10list|56|] -> "B"+-- _ -> "C"+-- >>> :}+-- ...+-- ... d10 must be between 0 and 9+-- ...++d10list :: QuasiQuoter+d10list = QuasiQuoter+ { quoteExp = d10ListExp+ , quotePat = d10ListPat+ , quoteType = \_ -> fail "d10list cannot be used in a type context"+ , quoteDec = \_ -> fail "d10list cannot be used in a declaration context"+ }
+ src/D10/Safe/Splices.hs view
@@ -0,0 +1,59 @@+{-# language Safe #-}++module D10.Safe.Splices+ (+ -- * Splice expressions+ d10ListExp+ -- * Splice patterns+ , d10ListPat+ ) where++import D10.Safe.Conversions (strD10ListFail)++import Control.Monad ((>=>))+import Language.Haskell.TH.Syntax (Exp (..), Pat (..), Q, dataToExpQ, dataToPatQ)++-- | Produces an expression of type @['D10']@ that can be used+-- in a Template Haskell splice.+--+-- >>> $(d10ListExp "")+-- []+--+-- >>> $(d10ListExp "5")+-- [D5]+--+-- >>> $(d10ListExp "58")+-- [D5,D8]+--+-- >>> $(d10ListExp "a")+-- ...+-- ... d10 must be between 0 and 9+-- ...+--+-- You may also be interested in 'D10.Safe.Quotes.d10list',+-- a quasi-quoter which does something similar.++d10ListExp :: String -> Q Exp+d10ListExp = strD10ListFail >=> dataToExpQ (const Nothing)++---------------------------------------------------++-- | Produces a pattern that can be used in a splice+-- to match a particular list of 'D10' values.+--+-- >>> :{+-- case [D5, D6] of+-- $(d10ListPat "42") -> "A"+-- $(d10ListPat "56") -> "B"+-- _ -> "C"+-- >>> :}+-- "B"+--+-- You may also be interested in 'D10.Safe.Quotes.d10list',+-- a quasi-quoter which does something similar.++d10ListPat :: String -> Q Pat+d10ListPat = strD10ListFail >=> \xs ->+ do+ pats <- traverse (dataToPatQ (const Nothing)) xs+ return (ListP pats)
+ src/D10/Safe/Type.hs view
@@ -0,0 +1,20 @@+{-# language Safe #-}++module D10.Safe.Type (D10 (..)) where++import Data.Data (Data)+import GHC.Generics (Generic)++-- | A whole number between /0/ and /9/+data D10+ = D0 -- ^ Zero+ | D1 -- ^ One+ | D2 -- ^ Two+ | D3 -- ^ Three+ | D4 -- ^ Four+ | D5 -- ^ Five+ | D6 -- ^ Six+ | D7 -- ^ Seven+ | D8 -- ^ Eight+ | D9 -- ^ Nine+ deriving (Bounded, Enum, Eq, Ord, Show, Data, Generic)
− src/Data/D10/Char.hs
@@ -1,917 +0,0 @@--- | Defines a 'D10' type as a newtype for 'Char', where the--- values are restricted to characters between @'0'@ and @'9'@.------ The following modules define @D10@ types in different ways--- but are otherwise very similar to this one:------ * "Data.D10.Num"--- * "Data.D10.Safe"--module Data.D10.Char- (- -- * Type- D10 (..)- -- $bounded- -- $enum-- -- * Quasi-quoters- , d10- , d10list-- -- * Splice expressions- , d10Exp- , d10ListExp-- -- * Splice patterns- , d10Pat- , d10ListPat-- -- * Converting between D10 and Char- , d10Char- , charD10Maybe- , charD10Either- , charD10Fail-- -- * Converting between D10 and String- , d10Str- , strD10Maybe- , strD10Either- , strD10Fail-- -- * Converting between [D10] and String- , strD10ListMaybe- , strD10ListEither- , strD10ListFail-- -- * Converting between D10 and Natural- , d10Nat- , natD10Maybe- , natD10Either- , natD10Fail- , natMod10-- -- * Converting between D10 and Integer- , d10Integer- , integerD10Maybe- , integerD10Either- , integerD10Fail- , integerMod10-- -- * Converting between D10 and Int- , d10Int- , intD10Maybe- , intD10Either- , intD10Fail- , intMod10-- -- * Converting between D10 and general numeric types- , d10Num- , integralD10Maybe- , integralD10Either- , integralD10Fail- , integralMod10-- -- * Modular arithmetic- , (+), (-), (*)-- ) where--import Data.D10.Predicate---- base-import Control.Monad ((>=>))-import Control.Monad.Fail (MonadFail (fail))-import Data.Char (chr, ord)-import Data.Monoid (Endo (..))-import Numeric.Natural (Natural)-import Prelude hiding (fail, (+), (-), (*))--import qualified Prelude as P---- template-haskell-import Language.Haskell.TH.Lib (litP, charL)-import Language.Haskell.TH.Quote (QuasiQuoter (..))-import Language.Haskell.TH.Syntax (Exp (..), Lift, Pat (..), Q)--------------------------------------------------------- | A 'Char' value between @'0'@ and @'9'@.------ The "Data.D10.Char" module provides many functions for--- constructing 'D10' values, including:------ * @'integerD10Maybe' :: 'Integer' -> 'Maybe' 'D10'@--- * @'integerMod10' :: 'Integer' -> 'D10'@------ With the @QuasiQuotes@ GHC extension enabled, you can write--- 'D10' literals using the quasi-quoters 'd10' and 'd10list'.--newtype D10 =- D10_Unsafe Char- -- ^ The constructor's name include the word "unsafe" as a reminder- -- that you should generally avoid using it directly, because it- -- allows constructing invalid 'D10' values.- deriving (Eq, Ord, Lift)---- $bounded--- ==== Bounded------ >>> minBound :: D10--- [d10|0|]------ >>> maxBound :: D10--- [d10|9|]--instance Bounded D10- where- minBound = D10_Unsafe '0'- maxBound = D10_Unsafe '9'---- $enum--- ==== Enum------ >>> [ [d10|5|] .. ]--- [d10list|56789|]------ >>> [ [d10|4|] .. [d10|7|] ]--- [d10list|4567|]------ >>> [ [d10|5|], [d10|4|] .. ]--- [d10list|543210|]------ >>> [ [d10|1|], [d10|3|] .. ]--- [d10list|13579|]------ >>> [ minBound .. maxBound ] :: [D10]--- [d10list|0123456789|]--instance Enum D10- where- fromEnum :: D10 -> Int- fromEnum = d10Int-- toEnum :: Int -> D10- toEnum = either error id . intD10Either-- enumFrom :: D10 -> [D10]- enumFrom x = enumFromTo x maxBound-- enumFromThen :: D10 -> D10 -> [D10]- enumFromThen x y = enumFromThenTo x y bound- where- bound | fromEnum y >= fromEnum x = maxBound- | otherwise = minBound-- succ :: D10 -> D10- succ (D10_Unsafe '9') = error "D10 overflow"- succ (D10_Unsafe x) = D10_Unsafe (succ x)-- pred :: D10 -> D10- pred (D10_Unsafe '0') = error "D10 underflow"- pred (D10_Unsafe x) = D10_Unsafe (pred x)--------------------------------------------------------- | Shows base-10 digits using the quasiquoters defined in--- "Data.D10.Char". A single digit is displayed using 'd10'.--- A list of digits is displayed using 'd10list'.--instance Show D10 where- showsPrec _ x = showString "[d10|" . showsChar x . showString "|]"- showList xs = showString "[d10list|" . showsStr xs . showString "|]"--showsChar :: D10 -> ShowS-showsChar (D10_Unsafe x) = showChar x--showsStr :: [D10] -> ShowS-showsStr = appEndo . foldMap (Endo . showsChar)--------------------------------------------------------- | Convert a 'D10' to its underlying 'Char' representation.------ >>> d10Char [d10|7|]--- '7'--d10Char :: D10 -> Char-d10Char (D10_Unsafe x) = x---- | Convert a 'D10' to a 'String'.------ @'d10Str' x = ['d10Char' x]@------ >>> d10Str [d10|7|]--- "7"--d10Str :: D10 -> String-d10Str (D10_Unsafe x) = [x]---- | Convert a 'D10' to a 'Natural'.------ 'd10Num' is a more general version of this function.------ >>> d10Nat [d10|7|]--- 7--d10Nat :: D10 -> Natural-d10Nat (D10_Unsafe x) = fromIntegral (ord x P.- ord '0')---- | Convert a 'D10' to an 'Integer'.------ 'd10Num' is a more general version of this function.------ >>> d10Integer [d10|7|]--- 7--d10Integer :: D10 -> Integer-d10Integer (D10_Unsafe x) = toInteger (ord x P.- ord '0')---- | Convert a 'D10' to an 'Int'.------ 'd10Num' is a more general version of this function.------ >>> d10Int [d10|7|]--- 7--d10Int :: D10 -> Int-d10Int (D10_Unsafe x) = ord x P.- ord '0'---- | Convert a 'D10' to any kind of number with a 'Num' instance.------ Specialized versions of this function include 'd10Nat',--- 'd10Integer', and 'd10Int'.------ >>> d10Num [d10|7|] :: Integer--- 7--d10Num :: Num a => D10 -> a-d10Num (D10_Unsafe x) = fromIntegral (ord x P.- ord '0')--------------------------------------------------------- | The 'D10' which is uniquely congruent modulo 10 to the given 'Natural'.------ 'integralMod10' is a more general version of this function.------ >>> natMod10 56--- [d10|6|]--natMod10 :: Natural -> D10-natMod10 x = D10_Unsafe (chr (ord '0' P.+ fromIntegral (x `mod` 10)))---- | The 'D10' which is uniquely congruent modulo 10 to the given 'Integer'.------ 'integralMod10' is a more general version of this function.------ >>> integerMod10 56--- [d10|6|]------ >>> integerMod10 (-56)--- [d10|4|]--integerMod10 :: Integer -> D10-integerMod10 x = D10_Unsafe (chr (ord '0' P.+ fromInteger (x `mod` 10)))---- | The 'D10' which is uniquely congruent modulo 10 to the given 'Int'.------ 'integralMod10' is a more general version of this function.------ >>> intMod10 56--- [d10|6|]------ >>> intMod10 (-56)--- [d10|4|]--intMod10 :: Int -> D10-intMod10 x = D10_Unsafe (chr (ord '0' P.+ (x `mod` 10)))---- | The 'D10' which is uniquely congruent modulo 10 to the given number--- (whose type must have an instance of the 'Integral' class).------ Specialized versions of this function include 'natMod10',--- 'integerMod10', and 'intMod10'.------ >>> integralMod10 (56 :: Integer)--- [d10|6|]------ >>> integralMod10 ((-56) :: Integer)--- [d10|4|]--integralMod10 :: Integral a => a -> D10-integralMod10 x = D10_Unsafe (chr (ord '0' P.+ fromIntegral (x `mod` 10)))--------------------------------------------------------- | Convert a 'Char' to a 'D10' if it is within the range--- @'0'@ to @'9'@, or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Char' x = 'Data.Maybe.isJust' ('charD10Maybe' x)@------ 'charD10Fail' is a more general version of this function.------ >>> charD10Maybe '5'--- Just [d10|5|]------ >>> charD10Maybe 'a'--- Nothing--charD10Maybe :: Char -> Maybe D10-charD10Maybe x- | isD10Char x = Just (D10_Unsafe x)- | otherwise = Nothing---- | Convert a 'String' to a 'D10' if it consists of exactly one--- character and that character is within the range @'0'@ to @'9'@,--- or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Str' x = 'Data.Maybe.isJust' ('strD10Maybe' x)@------ 'strD10Fail' is a more general version of this function.------ >>> strD10Maybe "5"--- Just [d10|5|]------ >>> strD10Maybe "a"--- Nothing------ >>> strD10Maybe "58"--- Nothing--strD10Maybe :: String -> Maybe D10-strD10Maybe [x] = charD10Maybe x-strD10Maybe _ = Nothing---- | Convert a 'String' to a list of 'D10' if all of the characters--- in the string are within the range @'0'@ to @'9'@, or produce--- 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10ListStr' x = 'Data.Maybe.isJust' ('strD10ListMaybe' x)@------ 'strD10ListFail' is a more general version of this function.------ >>> strD10ListMaybe "5"--- Just [d10list|5|]------ >>> strD10ListMaybe "a"--- Nothing------ >>> strD10ListMaybe "58"--- Just [d10list|58|]--strD10ListMaybe :: String -> Maybe [D10]-strD10ListMaybe = traverse charD10Maybe---- | Convert a 'Natural' to a 'D10' if it is less than 10,--- or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Nat' x = 'Data.Maybe.isJust' ('natD10Maybe' x)@------ 'integralD10Maybe', 'natD10Fail', and 'integralD10Fail'--- are more general versions of this function.------ >>> natD10Maybe 5--- Just [d10|5|]------ >>> natD10Maybe 12--- Nothing--natD10Maybe :: Natural -> Maybe D10-natD10Maybe x- | isD10Nat x = Just (D10_Unsafe (chr (fromIntegral x P.+ ord '0')))- | otherwise = Nothing---- | Convert an 'Integer' to a 'D10' if it is within the range 0 to 9,--- or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Integer' x = 'Data.Maybe.isJust' ('integerD10Maybe' x)@------ 'integralD10Maybe', 'integerD10Fail', and 'integralD10Fail'--- are more general versions of this function.------ >>> integerD10Maybe 5--- Just [d10|5|]------ >>> integerD10Maybe 12--- Nothing------ >>> integerD10Maybe (-5)--- Nothing--integerD10Maybe :: Integer -> Maybe D10-integerD10Maybe x- | isD10Integer x = Just (D10_Unsafe (chr (fromInteger x P.+ ord '0')))- | otherwise = Nothing---- | Convert an 'Int' to a 'D10' if it is within the range 0 to 9,--- or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Int' x = 'Data.Maybe.isJust' ('intD10Maybe' x)@------ 'integralD10Maybe', 'intD10Fail', and 'integralD10Fail'--- are more general versions of this function.------ >>> intD10Maybe 5--- Just [d10|5|]------ >>> intD10Maybe 12--- Nothing------ >>> intD10Maybe (-5)--- Nothing--intD10Maybe :: Int -> Maybe D10-intD10Maybe x- | isD10Int x = Just (D10_Unsafe (chr (x P.+ ord '0')))- | otherwise = Nothing---- | Construct a 'D10' from any kind of number with an 'Integral'--- instance, or produce 'Nothing' if the number falls outside the--- range 0 to 9.------ @'Data.D10.Predicate.isD10Integral' x = 'Data.Maybe.isJust' ('integralD10Maybe' x)@------ Specialized versions of this function include 'natD10Maybe',--- 'integerD10Maybe', and 'intD10Maybe'.------ 'integralD10Fail' is a more general version of this function.------ >>> integralD10Maybe (5 :: Integer)--- Just [d10|5|]------ >>> integralD10Maybe (12 :: Integer)--- Nothing------ >>> integralD10Maybe ((-5) :: Integer)--- Nothing--integralD10Maybe :: Integral a => a -> Maybe D10-integralD10Maybe x = integerD10Maybe (toInteger x)--------------------------------------------------------- | Convert a 'Char' to a 'D10' if it is within the range--- @'0'@ to @'9'@, or 'Left' with an error message otherwise.------ >>> charD10Either '5'--- Right [d10|5|]------ >>> charD10Either 'a'--- Left "d10 must be between 0 and 9"--charD10Either :: Char -> Either String D10-charD10Either x- | isD10Char x = Right (D10_Unsafe x)- | otherwise = Left "d10 must be between 0 and 9"---- | Convert a 'String' to a 'D10' if it consists of a single--- character and that character is within the range @'0'@ to--- @'9'@, or 'Left' with an error message otherwise.------ >>> strD10Either "5"--- Right [d10|5|]------ >>> strD10Either "a"--- Left "d10 must be between 0 and 9"------ >>> strD10Either "58"--- Left "d10 must be a single character"--strD10Either :: String -> Either String D10-strD10Either [x] = charD10Either x-strD10Either _ = Left "d10 must be a single character"---- | Convert a 'String' to a 'D10' if all of the characters in--- the string fall within the range @'0'@ to @'9'@, or 'Left'--- with an error message otherwise.------ >>> strD10ListEither "5"--- Right [d10list|5|]------ >>> strD10ListEither "a"--- Left "d10 must be between 0 and 9"------ >>> strD10ListEither "58"--- Right [d10list|58|]--strD10ListEither :: String -> Either String [D10]-strD10ListEither = traverse charD10Either---- | Convert a 'Natural' to a 'D10' if it is less than 10,--- or 'Left' with an error message otherwise.------ >>> natD10Either 5--- Right [d10|5|]------ >>> natD10Either 12--- Left "d10 must be less than 10"--natD10Either :: Natural -> Either String D10-natD10Either x =- case (natD10Maybe x) of- Just y -> Right y- Nothing -> Left "d10 must be less than 10"---- | Convert an 'Integer' to a 'D10' if it is within the--- range 0 to 9, or 'Left' with an error message otherwise.------ >>> integerD10Either 5--- Right [d10|5|]------ >>> integerD10Either 12--- Left "d10 must be between 0 and 9"------ >>> integerD10Either (-5)--- Left "d10 must be between 0 and 9"--integerD10Either :: Integer -> Either String D10-integerD10Either x =- case (integerD10Maybe x) of- Just y -> Right y- Nothing -> Left "d10 must be between 0 and 9"---- | Convert an 'Int' to a 'D10' if it is within the range--- 0 to 9, or 'Left' with an error message otherwise.------ >>> intD10Either 5--- Right [d10|5|]------ >>> intD10Either 12--- Left "d10 must be between 0 and 9"------ >>> intD10Either (-5)--- Left "d10 must be between 0 and 9"--intD10Either :: Int -> Either String D10-intD10Either x =- case (intD10Maybe x) of- Just y -> Right y- Nothing -> Left "d10 must be between 0 and 9"---- | Convert a number of a type that has an 'Integral' instance--- to a 'D10' if it falls within the range 0 to 9, or 'Left'--- with an error message otherwise.------ >>> integralD10Either (5 :: Integer)--- Right [d10|5|]------ >>> integralD10Either (12 :: Integer)--- Left "d10 must be between 0 and 9"------ >>> integralD10Either ((-5) :: Integer)--- Left "d10 must be between 0 and 9"--integralD10Either :: Integral a => a -> Either String D10-integralD10Either x = integerD10Either (toInteger x)--------------------------------------------------------- | Convert a 'Char' to a 'D10' if it is within the range--- @'0'@ to @'9'@, or 'fail' with an error message otherwise.------ 'charD10Maybe' is a specialized version of this function.------ >>> charD10Fail '5' :: IO D10--- [d10|5|]------ >>> charD10Fail 'a' :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)--charD10Fail :: MonadFail m => Char -> m D10-charD10Fail x- | isD10Char x = return (D10_Unsafe x)- | otherwise = fail "d10 must be between 0 and 9"---- | Convert a 'String' to a 'D10' if it consists of a single--- character and that character is within the range @'0'@ to--- @'9'@, or 'fail' with an error message otherwise.------ 'strD10Maybe' is a specialized version of this function.------ >>> strD10Fail "5" :: IO D10--- [d10|5|]------ >>> strD10Fail "a" :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)------ >>> strD10Fail "58" :: IO D10--- *** Exception: user error (d10 must be a single character)--strD10Fail :: MonadFail m => String -> m D10-strD10Fail [x] = charD10Fail x-strD10Fail _ = fail "d10 must be a single character"---- | Convert a 'String' to a 'D10' if all of the characters in--- the string fall within the range @'0'@ to @'9'@, or 'fail'--- with an error message otherwise.------ 'strD10ListMaybe' is a specialized version of this function.------ >>> strD10ListFail "5" :: IO [D10]--- [d10list|5|]------ >>> strD10ListFail "a" :: IO [D10]--- *** Exception: user error (d10 must be between 0 and 9)------ >>> strD10ListFail "58" :: IO [D10]--- [d10list|58|]--strD10ListFail :: MonadFail m => String -> m [D10]-strD10ListFail = traverse charD10Fail---- | Convert a 'Natural' to a 'D10' if it is less than 10,--- or 'fail' with an error message otherwise.------ 'natD10Maybe' is a specialized version of this function.------ 'integralD10Fail' is a more general version of this function.------ >>> natD10Fail 5 :: IO D10--- [d10|5|]------ >>> natD10Fail 12 :: IO D10--- *** Exception: user error (d10 must be less than 10)--natD10Fail :: MonadFail m => Natural -> m D10-natD10Fail x =- case (natD10Maybe x) of- Just y -> return y- Nothing -> fail "d10 must be less than 10"---- | Convert an 'Integer' to a 'D10' if it is within the--- range 0 to 9, or 'fail' with an error message otherwise.------ 'integerD10Maybe' is a specialized version of this function.------ 'integralD10Fail' is a more general version of this function.------ >>> integerD10Fail 5 :: IO D10--- [d10|5|]------ >>> integerD10Fail 12 :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)------ >>> integerD10Fail (-5) :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)--integerD10Fail :: MonadFail m => Integer -> m D10-integerD10Fail x =- case (integerD10Maybe x) of- Just y -> return y- Nothing -> fail "d10 must be between 0 and 9"---- | Convert an 'Int' to a 'D10' if it is within the range--- 0 to 9, or 'fail' with an error message otherwise.------ 'intD10Maybe' is a specialized version of this function.------ 'integralD10Fail' is a more general version of this function.------ >>> intD10Fail 5 :: IO D10--- [d10|5|]------ >>> intD10Fail 12 :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)------ >>> intD10Fail (-5) :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)--intD10Fail :: MonadFail m => Int -> m D10-intD10Fail x =- case (intD10Maybe x) of- Just y -> return y- Nothing -> fail "d10 must be between 0 and 9"---- | Convert a number of a type that has an 'Integral' instance--- to a 'D10' if it falls within the range 0 to 9, or 'fail'--- with an error message otherwise.------ 'natD10Maybe', 'integerD10Maybe', 'intD10Maybe',--- 'integralD10Maybe', 'natD10Fail', 'integerD10Fail', and--- 'intD10Fail' are all specialized versions of this function.------ >>> integralD10Fail (5 :: Integer) :: IO D10--- [d10|5|]------ >>> integralD10Fail (12 :: Integer) :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)------ >>> integralD10Fail ((-5) :: Integer) :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)--integralD10Fail :: (Integral a, MonadFail m) => a -> m D10-integralD10Fail x = integerD10Fail (toInteger x)--------------------------------------------------------- | Produces an expression of type 'D10' that can be used--- in a Template Haskell splice.------ >>> d10Nat $(d10Exp 5)--- 5------ >>> d10Nat $(d10Exp 12)--- ...--- ... d10 must be between 0 and 9--- ...------ You may also be interested in 'd10', a quasi-quoter which--- does something similar.--d10Exp :: Integer -> Q Exp-d10Exp = integerD10Fail >=> d10Exp'--d10Exp' :: D10 -> Q Exp-d10Exp' x = [| x |]---- | Produces an expression of type @['D10']@ that can be used--- in a Template Haskell splice.------ >>> d10Nat <$> $(d10ListExp "")--- []------ >>> d10Nat <$> $(d10ListExp "5")--- [5]------ >>> d10Nat <$> $(d10ListExp "58")--- [5,8]------ >>> d10Nat <$> $(d10ListExp "a")--- ...--- ... d10 must be between 0 and 9--- ...------ You may also be interested in 'd10list', a quasi-quoter which--- does something similar.--d10ListExp :: String -> Q Exp-d10ListExp = strD10ListFail >=> d10ListExp'--d10ListExp' :: [D10] -> Q Exp-d10ListExp' xs = [| xs |]--------------------------------------------------------- | Produces a pattern that can be used in a splice--- to match a particular 'D10' value.------ >>> :{--- case (charD10Maybe '5') of--- Just $(d10Pat 4) -> "A"--- Just $(d10Pat 5) -> "B"--- _ -> "C"--- >>> :}--- "B"------ You may wish to use the 'd10' quasi-quoter instead.--d10Pat :: Integer -> Q Pat-d10Pat = integerD10Fail >=> d10Pat'--d10Pat' :: D10 -> Q Pat-d10Pat' (D10_Unsafe x) = [p| D10_Unsafe $(litP $ charL x) |]---- | Produces a pattern that can be used in a splice--- to match a particular list of 'D10' values.------ >>> :{--- case (strD10ListMaybe "56") of--- Just $(d10ListPat "42") -> "A"--- Just $(d10ListPat "56") -> "B"--- _ -> "C"--- >>> :}--- "B"------ You may wish to use the 'd10list' quasi-quoter instead.--d10ListPat :: String -> Q Pat-d10ListPat = strD10ListFail >=> foldr (\x p -> [p| $(d10Pat' x) : $(p) |]) [p| [] |]-------------------------------------------------------- | A single base-10 digit.------ This quasi-quoter, when used as an expression, produces a--- value of type 'D10'.------ >>> d10Nat [d10|5|]--- 5------ >>> d10Nat [d10|a|]--- ...--- ... d10 must be between 0 and 9--- ...------ >>> d10Nat [d10|58|]--- ...--- ... d10 must be a single character--- ...------ This quasi-quoter can also be used as a pattern.------ >>> :{--- case (charD10Maybe '5') of--- Just [d10|4|] -> "A"--- Just [d10|5|] -> "B"--- _ -> "C"--- >>> :}--- "B"------ >>> :{--- case (charD10Maybe '5') of--- Just [d10|x|] -> "A"--- Just [d10|5|] -> "B"--- _ -> "C"--- >>> :}--- ...--- ... d10 must be between 0 and 9--- ...--d10 :: QuasiQuoter-d10 = QuasiQuoter- { quoteExp = strD10Fail >=> d10Exp'- , quotePat = strD10Fail >=> d10Pat'- , quoteType = \_ -> fail "d10 cannot be used in a type context"- , quoteDec = \_ -> fail "d10 cannot be used in a declaration context"- }---- | A list of base-10 digits.------ This quasi-quoter, when used as an expression, produces a--- value of type @['D10']@.------ >>> d10Nat <$> [d10list||]--- []------ >>> d10Nat <$> [d10list|5|]--- [5]------ >>> d10Nat <$> [d10list|58|]--- [5,8]------ >>> d10Nat <$> [d10list|a|]--- ...--- ... d10 must be between 0 and 9--- ...------ This quasi-quoter can also be used as a pattern.------ >>> :{--- case [d10list|56|] of--- [d10list|41|] -> "A"--- [d10list|56|] -> "B"--- _ -> "C"--- >>> :}--- "B"------ >>> :{--- case [d10list|56|] of--- [d10list|4x|] -> "A"--- [d10list|56|] -> "B"--- _ -> "C"--- >>> :}--- ...--- ... d10 must be between 0 and 9--- ...--d10list :: QuasiQuoter-d10list = QuasiQuoter- { quoteExp = strD10ListFail >=> d10ListExp'- , quotePat = d10ListPat- , quoteType = \_ -> fail "d10list cannot be used in a type context"- , quoteDec = \_ -> fail "d10list cannot be used in a declaration context"- }--------------------------------------------------------- | Addition modulo 10.------ >>> [d10|2|] + [d10|3|]--- [d10|5|]------ >>> [d10|6|] + [d10|7|]--- [d10|3|]--(+) :: D10 -> D10 -> D10-x + y = intMod10 (d10Int x P.+ d10Int y)---- | Subtraction modulo 10.------ >>> [d10|7|] - [d10|5|]--- [d10|2|]------ >>> [d10|3|] - [d10|7|]--- [d10|6|]--(-) :: D10 -> D10 -> D10-x - y = intMod10 (d10Int x P.- d10Int y)---- | Multiplication modulo 10.------ >>> [d10|2|] * [d10|4|]--- [d10|8|]------ >>> [d10|7|] * [d10|8|]--- [d10|6|]--(*) :: D10 -> D10 -> D10-x * y = intMod10 (d10Int x P.* d10Int y)
− src/Data/D10/Num.hs
@@ -1,928 +0,0 @@--- | Defines a 'D10' type as a newtype for any type with an--- instance of the 'Num' class, where the values are restricted--- to numbers between @'fromInteger' 0@ and @'fromInteger' 9@.------ The following modules define @D10@ types in different ways--- but are otherwise very similar to this one:------ * "Data.D10.Char"--- * "Data.D10.Safe"--module Data.D10.Num- (- -- * Type- D10 (..)- -- $bounded- -- $enum-- -- * Quasi-quoters- , d10- , d10list-- -- * Splice expressions- , d10Exp- , d10ListExp-- -- * Splice patterns- , d10Pat- , d10ListPat-- -- * Converting between D10 and Char- , d10Char- , charD10Maybe- , charD10Either- , charD10Fail-- -- * Converting between D10 and String- , d10Str- , strD10Maybe- , strD10Either- , strD10Fail-- -- * Converting between [D10] and String- , strD10ListMaybe- , strD10ListEither- , strD10ListFail-- -- * Converting between D10 and Natural- , d10Nat- , natD10Maybe- , natD10Either- , natD10Fail- , natMod10-- -- * Converting between D10 and Integer- , d10Integer- , integerD10Maybe- , integerD10Either- , integerD10Fail- , integerMod10-- -- * Converting between D10 and Int- , d10Int- , intD10Maybe- , intD10Either- , intD10Fail- , intMod10-- -- * Converting between D10 and general numeric types- , d10Num- , integralD10Maybe- , integralD10Either- , integralD10Fail- , integralMod10-- -- * Modular arithmetic- , (+), (-), (*)-- ) where--import Data.D10.Predicate---- base-import Control.Monad ((>=>))-import Control.Monad.Fail (MonadFail (fail))-import Data.Char (chr, ord)-import Data.Monoid (Endo (..))-import Numeric.Natural (Natural)-import Prelude hiding (fail, (+), (-), (*))--import qualified Prelude as P---- template-haskell-import Language.Haskell.TH.Lib (litP, integerL)-import Language.Haskell.TH.Quote (QuasiQuoter (..))-import Language.Haskell.TH.Syntax (Exp (..), Lift, Pat (..), Q)--------------------------------------------------------- | A value of some numeric type @a@ between--- @'fromInteger' 0@ and @'fromInteger' 9@.------ The "Data.D10.Num" module provides many functions for--- constructing 'D10' values, including:------ * @'integerD10Maybe' :: 'Integer' -> 'Maybe' 'D10'@--- * @'integerMod10' :: 'Integer' -> 'D10'@------ There are also several ways to safely write 'D10' literals--- using Template Haskell:------ * With the @QuasiQuotes@ GHC extension enabled, you can write--- use the quasi-quoters 'd10' and 'd10list'.--- * With the @TemplateHaskell@ GHC extension enabled, you can--- splice expressions produced by 'd10Exp' and 'd10ListExp'.--newtype D10 a =- D10_Unsafe a- -- ^ The constructor's name include the word "unsafe" as a reminder- -- that you should generally avoid using it directly, because it- -- allows constructing invalid 'D10' values.- deriving (Eq, Ord, Lift)---- $bounded--- ==== Bounded------ >>> minBound :: D10 Integer--- [d10|0|]------ >>> maxBound :: D10 Integer--- [d10|9|]--instance Num a => Bounded (D10 a)- where- minBound = D10_Unsafe 0- maxBound = D10_Unsafe 9---- $enum--- ==== Enum------ >>> [ [d10|5|] .. ]--- [d10list|56789|]------ >>> [ [d10|4|] .. [d10|7|] ]--- [d10list|4567|]------ >>> [ [d10|5|], [d10|4|] .. ]--- [d10list|543210|]------ >>> [ [d10|1|], [d10|3|] .. ]--- [d10list|13579|]------ >>> [ minBound .. maxBound ] :: [D10 Integer]--- [d10list|0123456789|]--instance Integral a => Enum (D10 a)- where- fromEnum :: D10 a -> Int- fromEnum = d10Int-- toEnum :: Int -> D10 a- toEnum = either error id . intD10Either-- enumFrom :: D10 a -> [D10 a]- enumFrom x = enumFromTo x maxBound-- enumFromThen :: D10 a -> D10 a -> [D10 a]- enumFromThen x y = enumFromThenTo x y bound- where- bound | fromEnum y >= fromEnum x = maxBound- | otherwise = minBound-- succ (D10_Unsafe 9) = error "D10 overflow"- succ (D10_Unsafe x) = D10_Unsafe (succ x)-- pred (D10_Unsafe 0) = error "D10 underflow"- pred (D10_Unsafe x) = D10_Unsafe (pred x)--------------------------------------------------------- | Shows base-10 digits using the quasiquoters defined in--- "Data.D10.Char". A single digit is displayed using 'd10'.--- A list of digits is displayed using 'd10list'.--instance Integral a => Show (D10 a) where- showsPrec _ x = showString "[d10|" . showsChar x . showString "|]"- showList xs = showString "[d10list|" . showsStr xs . showString "|]"--showsChar :: Integral a => D10 a -> ShowS-showsChar = showChar . d10Char--showsStr :: Integral a => [D10 a] -> ShowS-showsStr = appEndo . foldMap (Endo . showsChar)--------------------------------------------------------- | Convert a 'D10' to its underlying 'Char' representation.------ >>> d10Char [d10|7|]--- '7'--d10Char :: Integral a => D10 a -> Char-d10Char (D10_Unsafe x) = chr (ord '0' P.+ fromIntegral x)---- | Convert a 'D10' to a 'String'.------ @'d10Str' x = ['d10Char' x]@------ >>> d10Str [d10|7|]--- "7"--d10Str :: Integral a => D10 a -> String-d10Str x = [d10Char x]---- | Convert a 'D10' to a 'Natural'.------ 'd10Num' is a more general version of this function.------ >>> d10Nat [d10|7|]--- 7--d10Nat :: Integral a => D10 a -> Natural-d10Nat = d10Num---- | Convert a 'D10' to an 'Integer'.------ 'd10Num' is a more general version of this function.------ >>> d10Integer [d10|7|]--- 7--d10Integer :: Integral a => D10 a -> Integer-d10Integer = d10Num---- | Convert a 'D10' to an 'Int'.------ 'd10Num' is a more general version of this function.------ >>> d10Int [d10|7|]--- 7--d10Int :: Integral a => D10 a -> Int-d10Int = d10Num---- | Convert a 'D10' to any kind of number with a 'Num' instance.------ Specialized versions of this function include 'd10Nat',--- 'd10Integer', and 'd10Int'.------ >>> d10Num [d10|7|] :: Integer--- 7--d10Num :: (Integral b, Num a) => D10 b -> a-d10Num (D10_Unsafe x) = fromIntegral x--------------------------------------------------------- | The 'D10' which is uniquely congruent modulo 10 to the given 'Natural'.------ 'integralMod10' is a more general version of this function.------ >>> natMod10 56 :: D10 Int--- [d10|6|]--natMod10 :: Num a => Natural -> D10 a-natMod10 = integralMod10---- | The 'D10' which is uniquely congruent modulo 10 to the given 'Integer'.------ 'integralMod10' is a more general version of this function.------ >>> integerMod10 56 :: D10 Int--- [d10|6|]------ >>> integerMod10 (-56) :: D10 Int--- [d10|4|]--integerMod10 :: Num a => Integer -> D10 a-integerMod10 = integralMod10---- | The 'D10' which is uniquely congruent modulo 10 to the given 'Int'.------ 'integralMod10' is a more general version of this function.------ >>> intMod10 56 :: D10 Int--- [d10|6|]------ >>> intMod10 (-56) :: D10 Int--- [d10|4|]--intMod10 :: Num a => Int -> D10 a-intMod10 = integralMod10---- | The 'D10' which is uniquely congruent modulo 10 to the given number--- (whose type must have an instance of the 'Integral' class).------ Specialized versions of this function include 'natMod10',--- 'integerMod10', and 'intMod10'.------ >>> integralMod10 (56 :: Integer) :: D10 Int--- [d10|6|]------ >>> integralMod10 ((-56) :: Integer) :: D10 Int--- [d10|4|]--integralMod10 :: (Num b, Integral a) => a -> D10 b-integralMod10 x = D10_Unsafe (fromIntegral (x `mod` 10))--------------------------------------------------------- | Convert a 'Char' to a 'D10' if it is within the range--- @'0'@ to @'9'@, or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Char' x = 'Data.Maybe.isJust' ('charD10Maybe' x)@------ 'charD10Fail' is a more general version of this function.------ >>> charD10Maybe '5'--- Just [d10|5|]------ >>> charD10Maybe 'a'--- Nothing--charD10Maybe :: Num a => Char -> Maybe (D10 a)-charD10Maybe x- | isD10Char x = Just (D10_Unsafe (fromIntegral (ord x P.- ord '0')))- | otherwise = Nothing---- | Convert a 'String' to a 'D10' if it consists of exactly one--- character and that character is within the range @'0'@ to @'9'@,--- or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Str' x = 'Data.Maybe.isJust' ('strD10Maybe' x)@------ 'strD10Fail' is a more general version of this function.------ >>> strD10Maybe "5"--- Just [d10|5|]------ >>> strD10Maybe "a"--- Nothing------ >>> strD10Maybe "58"--- Nothing--strD10Maybe :: Num a => String -> Maybe (D10 a)-strD10Maybe [x] = charD10Maybe x-strD10Maybe _ = Nothing---- | Convert a 'String' to a list of 'D10' if all of the characters--- in the string are within the range @'0'@ to @'9'@, or produce--- 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10ListStr' x = 'Data.Maybe.isJust' ('strD10ListMaybe' x)@------ 'strD10ListFail' is a more general version of this function.------ >>> strD10ListMaybe "5"--- Just [d10list|5|]------ >>> strD10ListMaybe "a"--- Nothing------ >>> strD10ListMaybe "58"--- Just [d10list|58|]--strD10ListMaybe :: Num a => String -> Maybe [D10 a]-strD10ListMaybe = traverse charD10Maybe---- | Convert a 'Natural' to a 'D10' if it is less than 10,--- or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Nat' x = 'Data.Maybe.isJust' ('natD10Maybe' x)@------ 'integralD10Maybe', 'natD10Fail', and 'integralD10Fail'--- are more general versions of this function.------ >>> natD10Maybe 5--- Just [d10|5|]------ >>> natD10Maybe 12--- Nothing--natD10Maybe :: Num a => Natural -> Maybe (D10 a)-natD10Maybe x- | isD10Nat x = Just (D10_Unsafe (fromIntegral x))- | otherwise = Nothing---- | Convert an 'Integer' to a 'D10' if it is within the range 0 to 9,--- or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Integer' x = 'Data.Maybe.isJust' ('integerD10Maybe' x)@------ 'integralD10Maybe', 'integerD10Fail', and 'integralD10Fail'--- are more general versions of this function.------ >>> integerD10Maybe 5--- Just [d10|5|]------ >>> integerD10Maybe 12--- Nothing------ >>> integerD10Maybe (-5)--- Nothing--integerD10Maybe :: Num a => Integer -> Maybe (D10 a)-integerD10Maybe x- | isD10Integer x = Just (D10_Unsafe (fromIntegral x))- | otherwise = Nothing---- | Convert an 'Int' to a 'D10' if it is within the range 0 to 9,--- or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Int' x = 'Data.Maybe.isJust' ('intD10Maybe' x)@------ 'integralD10Maybe', 'intD10Fail', and 'integralD10Fail'--- are more general versions of this function.------ >>> intD10Maybe 5--- Just [d10|5|]------ >>> intD10Maybe 12--- Nothing------ >>> intD10Maybe (-5)--- Nothing--intD10Maybe :: Num a => Int -> Maybe (D10 a)-intD10Maybe x- | isD10Int x = Just (D10_Unsafe (fromIntegral x))- | otherwise = Nothing---- | Construct a 'D10' from any kind of number with an 'Integral'--- instance, or produce 'Nothing' if the number falls outside the--- range 0 to 9.------ @'Data.D10.Predicate.isD10Integral' x = 'Data.Maybe.isJust' ('integralD10Maybe' x)@------ Specialized versions of this function include 'natD10Maybe',--- 'integerD10Maybe', and 'intD10Maybe'.------ 'integralD10Fail' is a more general version of this function.------ >>> integralD10Maybe (5 :: Integer)--- Just [d10|5|]------ >>> integralD10Maybe (12 :: Integer)--- Nothing------ >>> integralD10Maybe ((-5) :: Integer)--- Nothing--integralD10Maybe :: (Num b, Integral a) => a -> Maybe (D10 b)-integralD10Maybe x = integerD10Maybe (toInteger x)--------------------------------------------------------- | Convert a 'Char' to a 'D10' if it is within the range--- @'0'@ to @'9'@, or 'Left' with an error message otherwise.------ >>> charD10Either '5'--- Right [d10|5|]------ >>> charD10Either 'a'--- Left "d10 must be between 0 and 9"--charD10Either :: Num a => Char -> Either String (D10 a)-charD10Either x- | isD10Char x = Right (D10_Unsafe (fromIntegral (ord x P.- ord '0')))- | otherwise = Left "d10 must be between 0 and 9"---- | Convert a 'String' to a 'D10' if it consists of a single--- character and that character is within the range @'0'@ to--- @'9'@, or 'Left' with an error message otherwise.------ >>> strD10Either "5"--- Right [d10|5|]------ >>> strD10Either "a"--- Left "d10 must be between 0 and 9"------ >>> strD10Either "58"--- Left "d10 must be a single character"--strD10Either :: Num a => String -> Either String (D10 a)-strD10Either [x] = charD10Either x-strD10Either _ = Left "d10 must be a single character"---- | Convert a 'String' to a 'D10' if all of the characters in--- the string fall within the range @'0'@ to @'9'@, or 'Left'--- with an error message otherwise.------ >>> strD10ListEither "5"--- Right [d10list|5|]------ >>> strD10ListEither "a"--- Left "d10 must be between 0 and 9"------ >>> strD10ListEither "58"--- Right [d10list|58|]--strD10ListEither :: Num a => String -> Either String [D10 a]-strD10ListEither = traverse charD10Either---- | Convert a 'Natural' to a 'D10' if it is less than 10,--- or 'Left' with an error message otherwise.------ >>> natD10Either 5--- Right [d10|5|]------ >>> natD10Either 12--- Left "d10 must be less than 10"--natD10Either :: Num a => Natural -> Either String (D10 a)-natD10Either x =- case (natD10Maybe x) of- Just y -> Right y- Nothing -> Left "d10 must be less than 10"---- | Convert an 'Integer' to a 'D10' if it is within the--- range 0 to 9, or 'Left' with an error message otherwise.------ >>> integerD10Either 5--- Right [d10|5|]------ >>> integerD10Either 12--- Left "d10 must be between 0 and 9"------ >>> integerD10Either (-5)--- Left "d10 must be between 0 and 9"--integerD10Either :: Num a => Integer -> Either String (D10 a)-integerD10Either x =- case (integerD10Maybe x) of- Just y -> Right y- Nothing -> Left "d10 must be between 0 and 9"---- | Convert an 'Int' to a 'D10' if it is within the range--- 0 to 9, or 'Left' with an error message otherwise.------ >>> intD10Either 5--- Right [d10|5|]------ >>> intD10Either 12--- Left "d10 must be between 0 and 9"------ >>> intD10Either (-5)--- Left "d10 must be between 0 and 9"--intD10Either :: Num a => Int -> Either String (D10 a)-intD10Either x =- case (intD10Maybe x) of- Just y -> Right y- Nothing -> Left "d10 must be between 0 and 9"---- | Convert a number of a type that has an 'Integral' instance--- to a 'D10' if it falls within the range 0 to 9, or 'Left'--- with an error message otherwise.------ >>> integralD10Either (5 :: Integer)--- Right [d10|5|]------ >>> integralD10Either (12 :: Integer)--- Left "d10 must be between 0 and 9"------ >>> integralD10Either ((-5) :: Integer)--- Left "d10 must be between 0 and 9"--integralD10Either :: (Num b, Integral a) => a -> Either String (D10 b)-integralD10Either x = integerD10Either (toInteger x)--------------------------------------------------------- | Convert a 'Char' to a 'D10' if it is within the range--- @'0'@ to @'9'@, or 'fail' with an error message otherwise.------ 'charD10Maybe' is a specialized version of this function.------ >>> charD10Fail '5' :: IO (D10 Int)--- [d10|5|]------ >>> charD10Fail 'a' :: IO (D10 Int)--- *** Exception: user error (d10 must be between 0 and 9)--charD10Fail :: (Num a, MonadFail m) => Char -> m (D10 a)-charD10Fail x- | isD10Char x = return (D10_Unsafe (fromIntegral (ord x P.- ord '0')))- | otherwise = fail "d10 must be between 0 and 9"---- | Convert a 'String' to a 'D10' if it consists of a single--- character and that character is within the range @'0'@ to--- @'9'@, or 'fail' with an error message otherwise.------ 'strD10Maybe' is a specialized version of this function.------ >>> strD10Fail "5" :: IO (D10 Int)--- [d10|5|]------ >>> strD10Fail "a" :: IO (D10 Int)--- *** Exception: user error (d10 must be between 0 and 9)------ >>> strD10Fail "58" :: IO (D10 Int)--- *** Exception: user error (d10 must be a single character)--strD10Fail :: (Num a, MonadFail m) => String -> m (D10 a)-strD10Fail [x] = charD10Fail x-strD10Fail _ = fail "d10 must be a single character"---- | Convert a 'String' to a 'D10' if all of the characters in--- the string fall within the range @'0'@ to @'9'@, or 'fail'--- with an error message otherwise.------ 'strD10ListMaybe' is a specialized version of this function.------ >>> strD10ListFail "5" :: IO [D10 Int]--- [d10list|5|]------ >>> strD10ListFail "a" :: IO [D10 Int]--- *** Exception: user error (d10 must be between 0 and 9)------ >>> strD10ListFail "58" :: IO [D10 Int]--- [d10list|58|]--strD10ListFail :: (Num a, MonadFail m) => String -> m [D10 a]-strD10ListFail = traverse charD10Fail---- | Convert a 'Natural' to a 'D10' if it is less than 10,--- or 'fail' with an error message otherwise.------ 'natD10Maybe' is a specialized version of this function.------ 'integralD10Fail' is a more general version of this function.------ >>> natD10Fail 5 :: IO (D10 Int)--- [d10|5|]------ >>> natD10Fail 12 :: IO (D10 Int)--- *** Exception: user error (d10 must be less than 10)--natD10Fail :: (Num a, MonadFail m) => Natural -> m (D10 a)-natD10Fail x =- case (natD10Maybe x) of- Just y -> return y- Nothing -> fail "d10 must be less than 10"---- | Convert an 'Integer' to a 'D10' if it is within the--- range 0 to 9, or 'fail' with an error message otherwise.------ 'integerD10Maybe' is a specialized version of this function.------ 'integralD10Fail' is a more general version of this function.------ >>> integerD10Fail 5 :: IO (D10 Int)--- [d10|5|]------ >>> integerD10Fail 12 :: IO (D10 Int)--- *** Exception: user error (d10 must be between 0 and 9)------ >>> integerD10Fail (-5) :: IO (D10 Int)--- *** Exception: user error (d10 must be between 0 and 9)--integerD10Fail :: (Num a, MonadFail m) => Integer -> m (D10 a)-integerD10Fail x =- case (integerD10Maybe x) of- Just y -> return y- Nothing -> fail "d10 must be between 0 and 9"---- | Convert an 'Int' to a 'D10' if it is within the range--- 0 to 9, or 'fail' with an error message otherwise.------ 'intD10Maybe' is a specialized version of this function.------ 'integralD10Fail' is a more general version of this function.------ >>> intD10Fail 5 :: IO (D10 Int)--- [d10|5|]------ >>> intD10Fail 12 :: IO (D10 Int)--- *** Exception: user error (d10 must be between 0 and 9)------ >>> intD10Fail (-5) :: IO (D10 Int)--- *** Exception: user error (d10 must be between 0 and 9)--intD10Fail :: (Num a, MonadFail m) => Int -> m (D10 a)-intD10Fail x =- case (intD10Maybe x) of- Just y -> return y- Nothing -> fail "d10 must be between 0 and 9"---- | Convert a number of a type that has an 'Integral' instance--- to a 'D10' if it falls within the range 0 to 9, or 'fail'--- with an error message otherwise.------ 'natD10Maybe', 'integerD10Maybe', 'intD10Maybe',--- 'integralD10Maybe', 'natD10Fail', 'integerD10Fail', and--- 'intD10Fail' are all specialized versions of this function.------ >>> integralD10Fail (5 :: Integer) :: IO (D10 Int)--- [d10|5|]------ >>> integralD10Fail (12 :: Integer) :: IO (D10 Int)--- *** Exception: user error (d10 must be between 0 and 9)------ >>> integralD10Fail ((-5) :: Integer) :: IO (D10 Int)--- *** Exception: user error (d10 must be between 0 and 9)--integralD10Fail :: (Num b, Integral a, MonadFail m) => a -> m (D10 b)-integralD10Fail x = integerD10Fail (toInteger x)--------------------------------------------------------- | Produces an expression of type @'D10' a@ that can be used--- in a Template Haskell splice.------ >>> d10Nat $(d10Exp 5)--- 5------ >>> d10Nat $(d10Exp 12)--- ...--- ... d10 must be between 0 and 9--- ...------ You may also be interested in 'd10', a quasi-quoter which--- does something similar.--d10Exp :: Integer -> Q Exp-d10Exp = integerD10Fail >=> d10Exp'--d10Exp' :: D10 Integer -> Q Exp-d10Exp' (D10_Unsafe x) = [| D10_Unsafe (fromInteger x) |]---- | Produces an expression of type @['D10' a]@ that can be used--- in a Template Haskell splice.------ >>> d10Nat <$> $(d10ListExp "")--- []------ >>> d10Nat <$> $(d10ListExp "5")--- [5]------ >>> d10Nat <$> $(d10ListExp "58")--- [5,8]------ >>> d10Nat <$> $(d10ListExp "a")--- ...--- ... d10 must be between 0 and 9--- ...------ You may also be interested in 'd10list', a quasi-quoter which--- does something similar.--d10ListExp :: String -> Q Exp-d10ListExp = strD10ListFail >=> d10ListExp'--d10ListExp' :: [D10 Integer] -> Q Exp-d10ListExp' xs = [| xs |]--------------------------------------------------------- | Produces a pattern that can be used in a splice--- to match a particular @'D10' a@ value.------ >>> :{--- case (charD10Maybe '5') of--- Just $(d10Pat 4) -> "A"--- Just $(d10Pat 5) -> "B"--- _ -> "C"--- >>> :}--- "B"------ You may wish to use the 'd10' quasi-quoter instead.--d10Pat :: Integer -> Q Pat-d10Pat = integerD10Fail >=> d10Pat'--d10Pat' :: D10 Integer -> Q Pat-d10Pat' (D10_Unsafe x) = [p| D10_Unsafe $(litP $ integerL x) |]--d10Pat'' :: Integral a => D10 a -> Q Pat-d10Pat'' (D10_Unsafe x) = [p| D10_Unsafe $(litP $ integerL $ toInteger x) |]---- | Produces a pattern that can be used in a splice--- to match a particular list of @'D10' a@ values.------ >>> :{--- case (strD10ListMaybe "56") of--- Just $(d10ListPat "42") -> "A"--- Just $(d10ListPat "56") -> "B"--- _ -> "C"--- >>> :}--- "B"------ You may wish to use the 'd10list' quasi-quoter instead.--d10ListPat :: String -> Q Pat-d10ListPat = strD10ListFail >=> d10ListPat'--d10ListPat' :: [D10 Integer] -> Q Pat-d10ListPat' = foldr (\x p -> [p| $(d10Pat'' x) : $(p) |]) [p| [] |]--------------------------------------------------------- | A single base-10 digit.------ This quasi-quoter, when used as an expression, produces a--- value of type @'D10' a@.------ >>> d10Nat [d10|5|]--- 5------ >>> d10Nat [d10|a|]--- ...--- ... d10 must be between 0 and 9--- ...------ >>> d10Nat [d10|58|]--- ...--- ... d10 must be a single character--- ...------ This quasi-quoter can also be used as a pattern.------ >>> :{--- case (charD10Maybe '5') of--- Just [d10|4|] -> "A"--- Just [d10|5|] -> "B"--- _ -> "C"--- >>> :}--- "B"------ >>> :{--- case (charD10Maybe '5') of--- Just [d10|x|] -> "A"--- Just [d10|5|] -> "B"--- _ -> "C"--- >>> :}--- ...--- ... d10 must be between 0 and 9--- ...--d10 :: QuasiQuoter-d10 = QuasiQuoter- { quoteExp = strD10Fail >=> d10Exp'- , quotePat = strD10Fail >=> d10Pat'- , quoteType = \_ -> fail "d10 cannot be used in a type context"- , quoteDec = \_ -> fail "d10 cannot be used in a declaration context"- }---- | A list of base-10 digits.------ This quasi-quoter, when used as an expression, produces a--- value of type @['D10' a]@.------ >>> d10Nat <$> [d10list||]--- []------ >>> d10Nat <$> [d10list|5|]--- [5]------ >>> d10Nat <$> [d10list|58|]--- [5,8]------ >>> d10Nat <$> [d10list|a|]--- ...--- ... d10 must be between 0 and 9--- ...------ This quasi-quoter can also be used as a pattern.------ >>> :{--- case [d10list|56|] of--- [d10list|41|] -> "A"--- [d10list|56|] -> "B"--- _ -> "C"--- >>> :}--- "B"------ >>> :{--- case [d10list|56|] of--- [d10list|4x|] -> "A"--- [d10list|56|] -> "B"--- _ -> "C"--- >>> :}--- ...--- ... d10 must be between 0 and 9--- ...--d10list :: QuasiQuoter-d10list = QuasiQuoter- { quoteExp = strD10ListFail >=> d10ListExp'- , quotePat = strD10ListFail >=> d10ListPat'- , quoteType = \_ -> fail "d10list cannot be used in a type context"- , quoteDec = \_ -> fail "d10list cannot be used in a declaration context"- }--------------------------------------------------------- | Addition modulo 10.------ >>> [d10|2|] + [d10|3|]--- [d10|5|]------ >>> [d10|6|] + [d10|7|]--- [d10|3|]--(+) :: Integral a => D10 a -> D10 a -> D10 a-x + y = intMod10 (d10Int x P.+ d10Int y)---- | Subtraction modulo 10.------ >>> [d10|7|] - [d10|5|]--- [d10|2|]------ >>> [d10|3|] - [d10|7|]--- [d10|6|]--(-) :: Integral a => D10 a -> D10 a -> D10 a-x - y = intMod10 (d10Int x P.- d10Int y)---- | Multiplication modulo 10.------ >>> [d10|2|] * [d10|4|]--- [d10|8|]------ >>> [d10|7|] * [d10|8|]--- [d10|6|]--(*) :: Integral a => D10 a -> D10 a -> D10 a-x * y = intMod10 (d10Int x P.* d10Int y)
− src/Data/D10/Predicate.hs
@@ -1,37 +0,0 @@--- | Functions to test whether values of various--- types represent digits in the range /0/ to /9/.-module Data.D10.Predicate where--import Numeric.Natural (Natural)---- | Determines whether a 'Char' is in the range @'0'@ to @'9'@.-isD10Char :: Char -> Bool-isD10Char x = x >= '0' && x <= '9'---- | Determines whether a 'String' consists of a single character--- and that character is within the range @'0'@ to @'9'@.-isD10Str :: String -> Bool-isD10Str [x] = isD10Char x-isD10Str _ = False---- | Determines whether a 'String' consists entirely of characters--- that are within the range @'0'@ to @'9'@.-isD10ListStr :: String -> Bool-isD10ListStr = all isD10Char---- | Determines whether a 'Natural' is in the range 0 to 9.-isD10Nat :: Natural -> Bool-isD10Nat x = x <= 9---- | Determines whether an 'Integer' is in the range 0 to 9.-isD10Integer :: Integer -> Bool-isD10Integer x = x >= 0 && x <= 9---- | Determines whether an 'Int' is in the range 0 to 9.-isD10Int :: Int -> Bool-isD10Int x = x >= 0 && x <= 9---- | Determines whether a number whose type has an 'Integral'--- instance is in the range 0 to 9.-isD10Integral :: Integral a => a -> Bool-isD10Integral x = isD10Integer (toInteger x)
− src/Data/D10/Safe.hs
@@ -1,876 +0,0 @@--- | Defines a 'D10' type as--- @'D0' | 'D1' | 'D2' | 'D3' | 'D4' | 'D5' | 'D6' | 'D7' | 'D8' | 'D9'@.------ The following modules define @D10@ types in different ways--- but are otherwise very similar to this one:------ * "Data.D10.Char"--- * "Data.D10.Num"------ This module is called "safe" because, in contrast with the--- alternative representations of a digit defined in the other--- modules, this 'D10' type does not include any possibility--- of representing an invalid non-digit value.--module Data.D10.Safe- (- -- * Type- D10 (..)- -- $bounded- -- $enum-- -- * Quasi-quoters- , d10list-- -- * Splice expressions- , d10ListExp-- -- * Splice patterns- , d10ListPat-- -- * Converting between D10 and Char- , d10Char- , charD10Maybe- , charD10Either- , charD10Fail-- -- * Converting between D10 and String- , d10Str- , strD10Maybe- , strD10Either- , strD10Fail-- -- * Converting between [D10] and String- , strD10ListMaybe- , strD10ListEither- , strD10ListFail-- -- * Converting between D10 and Natural- , d10Nat- , natD10Maybe- , natD10Either- , natD10Fail- , natMod10-- -- * Converting between D10 and Integer- , d10Integer- , integerD10Maybe- , integerD10Either- , integerD10Fail- , integerMod10-- -- * Converting between D10 and Int- , d10Int- , intD10Maybe- , intD10Either- , intD10Fail- , intMod10-- -- * Converting between D10 and general numeric types- , d10Num- , integralD10Maybe- , integralD10Either- , integralD10Fail- , integralMod10-- -- * Modular arithmetic- , (+), (-), (*)-- ) where---- base-import Control.Monad ((>=>))-import Control.Monad.Fail (MonadFail (fail))-import Data.Data (Data)-import GHC.Generics (Generic)-import Numeric.Natural (Natural)-import Prelude hiding (fail, (+), (-), (*))--import qualified Prelude as P---- template-haskell-import Language.Haskell.TH.Quote (QuasiQuoter (..))-import Language.Haskell.TH.Syntax (Exp (..), Lift, Pat (..), Q, dataToPatQ)--------------------------------------------------------- | A whole number between /0/ and /9/.--data D10- = D0 -- ^ Zero- | D1 -- ^ One- | D2 -- ^ Two- | D3 -- ^ Three- | D4 -- ^ Four- | D5 -- ^ Five- | D6 -- ^ Six- | D7 -- ^ Seven- | D8 -- ^ Eight- | D9 -- ^ Nine- deriving (Bounded, Enum, Eq, Lift, Ord, Show, Data, Generic)---- $bounded--- ==== Bounded------ >>> minBound :: D10--- D0------ >>> maxBound :: D10--- D9---- $enum--- ==== Enum------ >>> [ D5 .. ]--- [D5,D6,D7,D8,D9]------ >>> [ D4 .. D7 ]--- [D4,D5,D6,D7]------ >>> [ D5, D4 .. ]--- [D5,D4,D3,D2,D1,D0]------ >>> [ D1, D3 .. ]--- [D1,D3,D5,D7,D9]------ >>> [ minBound .. maxBound ] :: [D10]--- [D0,D1,D2,D3,D4,D5,D6,D7,D8,D9]--------------------------------------------------------- | Convert a 'D10' to its underlying 'Char' representation.------ >>> d10Char D7--- '7'--d10Char :: D10 -> Char-d10Char x =- case x of- D0 -> '0'- D1 -> '1'- D2 -> '2'- D3 -> '3'- D4 -> '4'- D5 -> '5'- D6 -> '6'- D7 -> '7'- D8 -> '8'- D9 -> '9'---- | Convert a 'D10' to a 'String'.------ @'d10Str' x = ['d10Char' x]@------ >>> d10Str D7--- "7"--d10Str :: D10 -> String-d10Str x = [d10Char x]---- | Convert a 'D10' to a 'Natural'.------ 'd10Num' is a more general version of this function.------ >>> d10Nat D7--- 7--d10Nat :: D10 -> Natural-d10Nat = d10Num---- | Convert a 'D10' to an 'Integer'.------ 'd10Num' is a more general version of this function.------ >>> d10Integer D7--- 7--d10Integer :: D10 -> Integer-d10Integer = d10Num---- | Convert a 'D10' to an 'Int'.------ 'd10Num' is a more general version of this function.------ >>> d10Int D7--- 7--d10Int :: D10 -> Int-d10Int = d10Num---- | Convert a 'D10' to any kind of number with a 'Num' instance.------ Specialized versions of this function include 'd10Nat',--- 'd10Integer', and 'd10Int'.------ >>> d10Num D7 :: Integer--- 7--d10Num :: Num a => D10 -> a-d10Num x =- case x of- D0 -> 0- D1 -> 1- D2 -> 2- D3 -> 3- D4 -> 4- D5 -> 5- D6 -> 6- D7 -> 7- D8 -> 8- D9 -> 9--------------------------------------------------------- | The 'D10' which is uniquely congruent modulo 10 to the given 'Natural'.------ 'integralMod10' is a more general version of this function.------ >>> natMod10 56--- D6--natMod10 :: Natural -> D10-natMod10 = integralMod10---- | The 'D10' which is uniquely congruent modulo 10 to the given 'Integer'.------ 'integralMod10' is a more general version of this function.------ >>> integerMod10 56--- D6------ >>> integerMod10 (-56)--- D4--integerMod10 :: Integer -> D10-integerMod10 = integralMod10---- | The 'D10' which is uniquely congruent modulo 10 to the given 'Int'.------ 'integralMod10' is a more general version of this function.------ >>> intMod10 56--- D6------ >>> intMod10 (-56)--- D4--intMod10 :: Int -> D10-intMod10 = integralMod10---- | The 'D10' which is uniquely congruent modulo 10 to the given number--- (whose type must have an instance of the 'Integral' class).------ Specialized versions of this function include 'natMod10',--- 'integerMod10', and 'intMod10'.------ >>> integralMod10 (56 :: Integer)--- D6------ >>> integralMod10 ((-56) :: Integer)--- D4--integralMod10 :: Integral a => a -> D10-integralMod10 x =- case (x `mod` 10) of- 0 -> D0- 1 -> D1- 2 -> D2- 3 -> D3- 4 -> D4- 5 -> D5- 6 -> D6- 7 -> D7- 8 -> D8- 9 -> D9- _ -> error "x `mod` 10 is not between 0 and 9"--------------------------------------------------------- | Convert a 'Char' to a 'D10' if it is within the range--- @'0'@ to @'9'@, or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Char' x = 'Data.Maybe.isJust' ('charD10Maybe' x)@------ 'charD10Fail' is a more general version of this function.------ >>> charD10Maybe '5'--- Just D5------ >>> charD10Maybe 'a'--- Nothing--charD10Maybe :: Char -> Maybe D10-charD10Maybe x =- case x of- '0' -> Just D0- '1' -> Just D1- '2' -> Just D2- '3' -> Just D3- '4' -> Just D4- '5' -> Just D5- '6' -> Just D6- '7' -> Just D7- '8' -> Just D8- '9' -> Just D9- _ -> Nothing---- | Convert a 'String' to a 'D10' if it consists of exactly one--- character and that character is within the range @'0'@ to @'9'@,--- or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Str' x = 'Data.Maybe.isJust' ('strD10Maybe' x)@------ 'strD10Fail' is a more general version of this function.------ >>> strD10Maybe "5"--- Just D5------ >>> strD10Maybe "a"--- Nothing------ >>> strD10Maybe "58"--- Nothing--strD10Maybe :: String -> Maybe D10-strD10Maybe [x] = charD10Maybe x-strD10Maybe _ = Nothing---- | Convert a 'String' to a list of 'D10' if all of the characters--- in the string are within the range @'0'@ to @'9'@, or produce--- 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10ListStr' x = 'Data.Maybe.isJust' ('strD10ListMaybe' x)@------ 'strD10ListFail' is a more general version of this function.------ >>> strD10ListMaybe "5"--- Just [D5]------ >>> strD10ListMaybe "a"--- Nothing------ >>> strD10ListMaybe "58"--- Just [D5,D8]--strD10ListMaybe :: String -> Maybe [D10]-strD10ListMaybe = traverse charD10Maybe---- | Convert a 'Natural' to a 'D10' if it is less than 10,--- or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Nat' x = 'Data.Maybe.isJust' ('natD10Maybe' x)@------ 'integralD10Maybe', 'natD10Fail', and 'integralD10Fail'--- are more general versions of this function.------ >>> natD10Maybe 5--- Just D5------ >>> natD10Maybe 12--- Nothing--natD10Maybe :: Natural -> Maybe D10-natD10Maybe = integralD10Maybe---- | Convert an 'Integer' to a 'D10' if it is within the range 0 to 9,--- or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Integer' x = 'Data.Maybe.isJust' ('integerD10Maybe' x)@------ 'integralD10Maybe', 'integerD10Fail', and 'integralD10Fail'--- are more general versions of this function.------ >>> integerD10Maybe 5--- Just D5------ >>> integerD10Maybe 12--- Nothing------ >>> integerD10Maybe (-5)--- Nothing--integerD10Maybe :: Integer -> Maybe D10-integerD10Maybe = integralD10Maybe---- | Convert an 'Int' to a 'D10' if it is within the range 0 to 9,--- or produce 'Nothing' otherwise.------ @'Data.D10.Predicate.isD10Int' x = 'Data.Maybe.isJust' ('intD10Maybe' x)@------ 'integralD10Maybe', 'intD10Fail', and 'integralD10Fail'--- are more general versions of this function.------ >>> intD10Maybe 5--- Just D5------ >>> intD10Maybe 12--- Nothing------ >>> intD10Maybe (-5)--- Nothing--intD10Maybe :: Int -> Maybe D10-intD10Maybe = integralD10Maybe---- | Construct a 'D10' from any kind of number with an 'Integral'--- instance, or produce 'Nothing' if the number falls outside the--- range 0 to 9.------ @'Data.D10.Predicate.isD10Integral' x = 'Data.Maybe.isJust' ('integralD10Maybe' x)@------ Specialized versions of this function include 'natD10Maybe',--- 'integerD10Maybe', and 'intD10Maybe'.------ 'integralD10Fail' is a more general version of this function.------ >>> integralD10Maybe (5 :: Integer)--- Just D5------ >>> integralD10Maybe (12 :: Integer)--- Nothing------ >>> integralD10Maybe ((-5) :: Integer)--- Nothing--integralD10Maybe :: Integral a => a -> Maybe D10-integralD10Maybe x =- case x of- 0 -> Just D0- 1 -> Just D1- 2 -> Just D2- 3 -> Just D3- 4 -> Just D4- 5 -> Just D5- 6 -> Just D6- 7 -> Just D7- 8 -> Just D8- 9 -> Just D9- _ -> Nothing--------------------------------------------------------- | Convert a 'Char' to a 'D10' if it is within the range--- @'0'@ to @'9'@, or 'Left' with an error message otherwise.------ >>> charD10Either '5'--- Right D5------ >>> charD10Either 'a'--- Left "d10 must be between 0 and 9"--charD10Either :: Char -> Either String D10-charD10Either x =- case x of- '0' -> Right D0- '1' -> Right D1- '2' -> Right D2- '3' -> Right D3- '4' -> Right D4- '5' -> Right D5- '6' -> Right D6- '7' -> Right D7- '8' -> Right D8- '9' -> Right D9- _ -> Left "d10 must be between 0 and 9"---- | Convert a 'String' to a 'D10' if it consists of a single--- character and that character is within the range @'0'@ to--- @'9'@, or 'Left' with an error message otherwise.------ >>> strD10Either "5"--- Right D5------ >>> strD10Either "a"--- Left "d10 must be between 0 and 9"------ >>> strD10Either "58"--- Left "d10 must be a single character"--strD10Either :: String -> Either String D10-strD10Either [x] = charD10Either x-strD10Either _ = Left "d10 must be a single character"---- | Convert a 'String' to a 'D10' if all of the characters in--- the string fall within the range @'0'@ to @'9'@, or 'Left'--- with an error message otherwise.------ >>> strD10ListEither "5"--- Right [D5]------ >>> strD10ListEither "a"--- Left "d10 must be between 0 and 9"------ >>> strD10ListEither "58"--- Right [D5,D8]--strD10ListEither :: String -> Either String [D10]-strD10ListEither = traverse charD10Either---- | Convert a 'Natural' to a 'D10' if it is less than 10,--- or 'Left' with an error message otherwise.------ >>> natD10Either 5--- Right D5------ >>> natD10Either 12--- Left "d10 must be less than 10"--natD10Either :: Natural -> Either String D10-natD10Either x =- case x of- 0 -> Right D0- 1 -> Right D1- 2 -> Right D2- 3 -> Right D3- 4 -> Right D4- 5 -> Right D5- 6 -> Right D6- 7 -> Right D7- 8 -> Right D8- 9 -> Right D9- _ -> Left "d10 must be less than 10"---- | Convert an 'Integer' to a 'D10' if it is within the--- range 0 to 9, or 'Left' with an error message otherwise.------ >>> integerD10Either 5--- Right D5------ >>> integerD10Either 12--- Left "d10 must be between 0 and 9"------ >>> integerD10Either (-5)--- Left "d10 must be between 0 and 9"--integerD10Either :: Integer -> Either String D10-integerD10Either = integralD10Either---- | Convert an 'Int' to a 'D10' if it is within the range--- 0 to 9, or 'Left' with an error message otherwise.------ >>> intD10Either 5--- Right D5------ >>> intD10Either 12--- Left "d10 must be between 0 and 9"------ >>> intD10Either (-5)--- Left "d10 must be between 0 and 9"--intD10Either :: Int -> Either String D10-intD10Either = integralD10Either---- | Convert a number of a type that has an 'Integral' instance--- to a 'D10' if it falls within the range 0 to 9, or 'Left'--- with an error message otherwise.------ >>> integralD10Either (5 :: Integer)--- Right D5------ >>> integralD10Either (12 :: Integer)--- Left "d10 must be between 0 and 9"------ >>> integralD10Either ((-5) :: Integer)--- Left "d10 must be between 0 and 9"--integralD10Either :: Integral a => a -> Either String D10-integralD10Either x =- case (integralD10Maybe x) of- Just y -> Right y- Nothing -> Left "d10 must be between 0 and 9"--------------------------------------------------------- | Convert a 'Char' to a 'D10' if it is within the range--- @'0'@ to @'9'@, or 'fail' with an error message otherwise.------ 'charD10Maybe' is a specialized version of this function.------ >>> charD10Fail '5' :: IO D10--- D5------ >>> charD10Fail 'a' :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)--charD10Fail :: MonadFail m => Char -> m D10-charD10Fail x =- case x of- '0' -> return D0- '1' -> return D1- '2' -> return D2- '3' -> return D3- '4' -> return D4- '5' -> return D5- '6' -> return D6- '7' -> return D7- '8' -> return D8- '9' -> return D9- _ -> fail "d10 must be between 0 and 9"---- | Convert a 'String' to a 'D10' if it consists of a single--- character and that character is within the range @'0'@ to--- @'9'@, or 'fail' with an error message otherwise.------ 'strD10Maybe' is a specialized version of this function.------ >>> strD10Fail "5" :: IO D10--- D5------ >>> strD10Fail "a" :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)------ >>> strD10Fail "58" :: IO D10--- *** Exception: user error (d10 must be a single character)--strD10Fail :: MonadFail m => String -> m D10-strD10Fail [x] = charD10Fail x-strD10Fail _ = fail "d10 must be a single character"---- | Convert a 'String' to a 'D10' if all of the characters in--- the string fall within the range @'0'@ to @'9'@, or 'fail'--- with an error message otherwise.------ 'strD10ListMaybe' is a specialized version of this function.------ >>> strD10ListFail "5" :: IO [D10]--- [D5]------ >>> strD10ListFail "a" :: IO [D10]--- *** Exception: user error (d10 must be between 0 and 9)------ >>> strD10ListFail "58" :: IO [D10]--- [D5,D8]--strD10ListFail :: MonadFail m => String -> m [D10]-strD10ListFail = traverse charD10Fail---- | Convert a 'Natural' to a 'D10' if it is less than 10,--- or 'fail' with an error message otherwise.------ 'natD10Maybe' is a specialized version of this function.------ 'integralD10Fail' is a more general version of this function.------ >>> natD10Fail 5 :: IO D10--- D5------ >>> natD10Fail 12 :: IO D10--- *** Exception: user error (d10 must be less than 10)--natD10Fail :: MonadFail m => Natural -> m D10-natD10Fail x =- case x of- 0 -> return D0- 1 -> return D1- 2 -> return D2- 3 -> return D3- 4 -> return D4- 5 -> return D5- 6 -> return D6- 7 -> return D7- 8 -> return D8- 9 -> return D9- _ -> fail "d10 must be less than 10"---- | Convert an 'Integer' to a 'D10' if it is within the--- range 0 to 9, or 'fail' with an error message otherwise.------ 'integerD10Maybe' is a specialized version of this function.------ 'integralD10Fail' is a more general version of this function.------ >>> integerD10Fail 5 :: IO D10--- D5------ >>> integerD10Fail 12 :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)------ >>> integerD10Fail (-5) :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)--integerD10Fail :: MonadFail m => Integer -> m D10-integerD10Fail = integralD10Fail---- | Convert an 'Int' to a 'D10' if it is within the range--- 0 to 9, or 'fail' with an error message otherwise.------ 'intD10Maybe' is a specialized version of this function.------ 'integralD10Fail' is a more general version of this function.------ >>> intD10Fail 5 :: IO D10--- D5------ >>> intD10Fail 12 :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)------ >>> intD10Fail (-5) :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)--intD10Fail :: MonadFail m => Int -> m D10-intD10Fail = integralD10Fail---- | Convert a number of a type that has an 'Integral' instance--- to a 'D10' if it falls within the range 0 to 9, or 'fail'--- with an error message otherwise.------ 'natD10Maybe', 'integerD10Maybe', 'intD10Maybe',--- 'integralD10Maybe', 'natD10Fail', 'integerD10Fail', and--- 'intD10Fail' are all specialized versions of this function.------ >>> integralD10Fail (5 :: Integer) :: IO D10--- D5------ >>> integralD10Fail (12 :: Integer) :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)------ >>> integralD10Fail ((-5) :: Integer) :: IO D10--- *** Exception: user error (d10 must be between 0 and 9)--integralD10Fail :: (Integral a, MonadFail m) => a -> m D10-integralD10Fail x =- case (integralD10Maybe x) of- Just y -> return y- Nothing -> fail "d10 must be between 0 and 9"--------------------------------------------------------- | Produces an expression of type @['D10']@ that can be used--- in a Template Haskell splice.------ >>> $(d10ListExp "")--- []------ >>> $(d10ListExp "5")--- [D5]------ >>> $(d10ListExp "58")--- [D5,D8]------ >>> $(d10ListExp "a")--- ...--- ... d10 must be between 0 and 9--- ...------ You may also be interested in 'd10list', a quasi-quoter which--- does something similar.--d10ListExp :: String -> Q Exp-d10ListExp = strD10ListFail >=> d10ListExp'--d10ListExp' :: [D10] -> Q Exp-d10ListExp' x = [| x |]-------------------------------------------------------d10Pat :: D10 -> Q Pat-d10Pat = dataToPatQ (const Nothing)---- | Produces a pattern that can be used in a splice--- to match a particular list of 'D10' values.------ >>> :{--- case [D5, D6] of--- $(d10ListPat "42") -> "A"--- $(d10ListPat "56") -> "B"--- _ -> "C"--- >>> :}--- "B"------ You may also be interested in 'd10list', a quasi-quoter which--- does something similar.--d10ListPat :: String -> Q Pat-d10ListPat = strD10ListFail >=> \xs ->- do- pats <- traverse d10Pat xs- return (ListP pats)--d10ListPat' :: [D10] -> Q Pat-d10ListPat' xs =- do- pats <- traverse d10Pat xs- return (ListP pats)--------------------------------------------------------- | A list of base-10 digits.------ This quasi-quoter, when used as an expression, produces a--- value of type @['D10']@.------ >>> [d10list||]--- []------ >>> [d10list|5|]--- [D5]------ >>> [d10list|58|]--- [D5,D8]------ >>> [d10list|a|]--- ...--- ... d10 must be between 0 and 9--- ...------ This quasi-quoter can also be used as a pattern.------ >>> :{--- case [D5, D6] of--- [d10list|41|] -> "A"--- [d10list|56|] -> "B"--- _ -> "C"--- >>> :}--- "B"------ >>> :{--- case [D5, D6] of--- [d10list|4x|] -> "A"--- [d10list|56|] -> "B"--- _ -> "C"--- >>> :}--- ...--- ... d10 must be between 0 and 9--- ...--d10list :: QuasiQuoter-d10list = QuasiQuoter- { quoteExp = strD10ListFail >=> d10ListExp'- , quotePat = strD10ListFail >=> d10ListPat'- , quoteType = \_ -> fail "d10list cannot be used in a type context"- , quoteDec = \_ -> fail "d10list cannot be used in a declaration context"- }--------------------------------------------------------- | Addition modulo 10.------ >>> D2 + D3--- D5------ >>> D6 + D7--- D3--(+) :: D10 -> D10 -> D10-x + y = intMod10 (d10Int x P.+ d10Int y)---- | Subtraction modulo 10.------ >>> D7 - D5--- D2------ >>> D3 - D7--- D6--(-) :: D10 -> D10 -> D10-x - y = intMod10 (d10Int x P.- d10Int y)---- | Multiplication modulo 10.------ >>> D2 * D4--- D8--- >>> D7 * D8--- D6--(*) :: D10 -> D10 -> D10-x * y = intMod10 (d10Int x P.* d10Int y)
test/char.hs view
@@ -1,7 +1,8 @@ import Prelude hiding (fail, (+), (-), (*)) -import Data.D10.Char-import qualified Data.D10.Predicate as Predicate+import D10.Char+import D10.Char.Arithmetic+import qualified D10.Predicate as Predicate import Hedgehog import qualified Hedgehog.Gen as Gen
test/num.hs view
@@ -1,7 +1,8 @@ import Prelude hiding (fail, (+), (-), (*)) -import Data.D10.Num-import qualified Data.D10.Predicate as Predicate+import D10.Num+import D10.Num.Arithmetic+import qualified D10.Predicate as Predicate import Hedgehog import qualified Hedgehog.Gen as Gen
test/safe.hs view
@@ -1,7 +1,8 @@ import Prelude hiding (fail, (+), (-), (*)) -import Data.D10.Safe-import qualified Data.D10.Predicate as Predicate+import D10.Safe+import D10.Safe.Arithmetic+import qualified D10.Predicate as Predicate import Hedgehog import qualified Hedgehog.Gen as Gen