module Data.Convert.Bound where
import Data.Convert.Base
import Data.Typeable
import Control.Applicative
import Language.Haskell.TH hiding (Type, Safety, Safe, Unsafe)
import Data.Monoid
import GHC.TypeLits
import Prelude hiding (Bounded, maxBound, minBound)
import qualified Prelude as Prelude
boundedConversion :: (Bounded b, Ord a, Convertible a Rational, Convertible (Bounds b) (Bounds Rational))
=> (a -> b) -> (a -> Either BoundError b)
boundedConversion (func :: a -> b) inp = if (convert inp) `boundedBy` (convert (bounds :: Bounds b) :: Bounds Rational)
then Right $ func inp
else Left BoundError
data BoundError = BoundError deriving (Show)
data Conversion = Conversion (Q Exp) Type Type
class Conversions a b where
conversions :: Q Exp -> a -> b -> [Conversion]
genConversion :: Conversion -> Q Dec
genConversion c@(Conversion qexp (Type name bounds) (Type name' bounds')) = do
exp <- qexp :: Q Exp
let convf name fmod = [ValD (VarP $ mkName name) (NormalB $ fmod exp) []]
return $ if bounds `isSubBound` bounds'
then InstanceD [] (AppT (AppT (ConT $ mkName "Convertible") (ConT name)) (ConT name')) $ convf "convert" id
else InstanceD [] (AppT (AppT (AppT (ConT $ mkName "MaybeConvertible") (ConT name)) (ConT $ mkName "BoundError")) (ConT name')) $ convf "tryConvert" $ AppE (VarE $ mkName "boundedConversion")
genConversions :: [Conversion] -> Q [Dec]
genConversions = mapM genConversion
instance Conversions [Type] [Type] where conversions f a b = Conversion f <$> a <*> b
instance Conversions Type [Type] where conversions f a b = Conversion f a <$> b
instance Conversions [Type] Type where conversions f a b = flip (Conversion f) b <$> a
instance Conversions Type Type where conversions f a b = [Conversion f a b]
instance Show Conversion where
show (Conversion _ a b) = "Conversion "
data Type = Type { name :: Name
, tbounds :: (Bounds Integer)
} deriving (Show, Eq)
data Layout = IntLayout Sign Integer
| InfiniteLayout
deriving (Show, Eq)
data Sign = Signed
| Unsigned
deriving (Show, Eq)
layoutBounds :: Layout -> Bounds Integer
layoutBounds = \case
InfiniteLayout -> infiniteBounds
IntLayout s i -> Bounds (Value ( base)) (Value $ base 1)
where base = case s of
Signed -> 2 ^ (i 1)
Unsigned -> 2 ^ i
data Value a = MinusInfinity
| Value a
| Infinity
deriving (Show, Functor, Eq)
instance Num a => Num (Value a) where
fromInteger = Value . fromInteger
instance Convertible a b => Convertible (Value a) (Value b) where
convert (Value a) = Value $ convert a
instance Ord a => Ord (Value a) where
compare MinusInfinity MinusInfinity = EQ
compare MinusInfinity a = LT
compare a MinusInfinity = GT
compare Infinity Infinity = EQ
compare Infinity a = GT
compare a Infinity = LT
compare (Value a) (Value a') = compare a a'
data Bounds a = Bounds (Value a) (Value a) deriving (Show, Functor, Eq)
class Bounded a where
bounds :: Bounds a
infiniteBounds :: Bounds a
infiniteBounds = Bounds MinusInfinity Infinity
isSubBound :: Ord a => Bounds a -> Bounds a -> Bool
isSubBound (Bounds min max) (Bounds min' max') = min >= min' && max <= max'
boundedBy :: Ord a => a -> Bounds a -> Bool
boundedBy (Value -> a) (Bounds min max) = a >= min && a <= max
instance Convertible a b => Convertible (Bounds a) (Bounds b) where
convert (Bounds a b) = Bounds (convert a) (convert b)
instance (Prelude.Bounded a)
=> Bounded a where
bounds = Bounds (Value Prelude.minBound) (Value Prelude.maxBound)
instance Bounded Float where bounds = infiniteBounds
instance Bounded Double where bounds = infiniteBounds
instance Bounded Rational where bounds = infiniteBounds
instance Bounded Integer where bounds = infiniteBounds