primus-0.1.0.0: src/Primus/Num1.hs
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{- |
Module : Primus.Num1
Description : similar to 'Num' class but with failure handling
Copyright : (c) Grant Weyburne, 2022
License : BSD-3
-}
module Primus.Num1 (
Num1 (..),
withOp,
withOp2,
withOp3,
withOp4,
) where
import Control.Applicative
import Control.Monad
import Data.Int
import Data.Kind
import Data.Pos
import Data.Word
import GHC.Natural
import Primus.Enum
import Primus.Error
import Primus.Extra
-- | run a function of one integer against the underlying 'Num1' type
withOp :: Num1 a => (Integer -> Integer) -> a -> Either String a
withOp f a = fromInteger1 a (f (toInteger1 a))
-- | run a function of two integers against the underlying 'Num1' types
withOp2 :: Num1 a => (Integer -> Integer -> Integer) -> a -> a -> Either String a
withOp2 f a b = fromInteger1 a (f (toInteger1 a) (toInteger1 b))
-- | run a function of three integers against the underlying 'Num1' types
withOp3 :: Num1 a => (Integer -> Integer -> Integer -> Integer) -> a -> a -> a -> Either String a
withOp3 f a b c =
fromInteger1 a (f (toInteger1 a) (toInteger1 b) (toInteger1 c))
-- | run a function of four integers against the underlying 'Num1' types
withOp4 :: Num1 a => (Integer -> Integer -> Integer -> Integer -> Integer) -> a -> a -> a -> a -> Either String a
withOp4 f a b c d =
fromInteger1 a (f (toInteger1 a) (toInteger1 b) (toInteger1 c) (toInteger1 d))
{- | lifted version of Num class for handling failure
minimal definition requires 'toInteger1' and 'fromInteger1' unless leveraging default signatures
-}
type Num1 :: Type -> Constraint
class Num1 a where
-- | required method for converting from "a" to an 'Integer'
toInteger1 :: a -> Integer
default toInteger1 :: Enum a => a -> Integer
toInteger1 = toInteger . fromEnum @a
-- | required method for trying to convert from an 'Integer' to "a"
fromInteger1 :: a -> Integer -> Either String a
default fromInteger1 :: (Bounded a, Enum a) => a -> Integer -> Either String a
fromInteger1 = const integerToEnumSafe
(.+)
, (.-)
, (.*) ::
Either String a ->
Either String a ->
Either String a
(.+) = join .@ liftA2 (lmsg "(.+)" .@ withOp2 (+))
(.-) = join .@ liftA2 (lmsg "(.-)" .@ withOp2 (-))
(.*) = join .@ liftA2 (lmsg "(.*)" .@ withOp2 (*))
negate1
, abs1
, signum1
, succ1
, pred1 ::
Either String a ->
Either String a
negate1 = (=<<) (lmsg "negate1" . withOp negate)
signum1 = (=<<) (lmsg "signum1" . withOp signum)
abs1 = (=<<) (lmsg "abs1" . withOp abs)
succ1 = (=<<) (lmsg "succ1" . withOp succ)
pred1 = (=<<) (lmsg "pred1" . withOp pred)
infixl 7 .*
infixl 6 .+
infixl 6 .-
instance Num1 Natural where
fromInteger1 _ i
| i < 0 = Left $ "Natural: undefined for negative numbers " ++ show i
| otherwise = Right $ naturalFromInteger i
instance Num1 Pos
instance Num1 Word8
instance Num1 Word16
instance Num1 Word32
instance Num1 Word64
instance Num1 Int
instance Num1 Int8
instance Num1 Int16
instance Num1 Int32
instance Num1 Int64