module Data.Analysis.Fulcrum.Abstract
( Abstract
, makeAbs
, absShow
, absShowS
) where
import Data.Dynamic
import Data.Monoid
import Data.Typeable (Typeable)
-- | An abstract value is a wrapper over Dynamic that supports
-- heterogeneous comparison, equality, and retains access to
-- the underlying 'show' implementation. This allows us to use
-- selectors over possibly heterogeneous values without
-- various kinds of type-wrangling.
data Abstract = Abstract
{ absVal :: Dynamic
, absEq :: Abstract -> Bool
, absCmp :: Abstract -> Ordering
, absStr :: String
}
-- | Print out a comma-separated list of the values contained
-- in a list of 'Abstract' values.
absShowS :: [Abstract] -> String
absShowS [] = ""
absShowS [x] = absShow x
absShowS (x:xs) = absShow x ++ "," ++ absShowS xs
-- | Get the string representation of the interior value of an
-- 'Abstract'. (Note that the 'Show' instance for 'Abstract'
-- will also print out the fact that it's an 'Abstract' value,
-- whereas this will omit it.)
absShow :: Abstract -> String
absShow = absStr
over :: Typeable a => (a -> b) -> b -> Dynamic -> b
over op def dyn
| Just x <- fromDynamic dyn = op x
| otherwise = def
-- | Create an 'Abstract' value. This does mean that relevant
-- values in your rows must have a 'Typeable' constraint.
makeAbs :: (Typeable t, Ord t, Show t) => t -> Abstract
makeAbs x = Abstract
{ absVal = toDyn x
, absEq = doEq . absVal
, absCmp = doCmp . absVal
, absStr = show x
} where doEq = over (== x) False
doCmp y = over (compare x) (cmpTyp y) y
cmpTyp y = dynTypeRep (toDyn x) `compare` dynTypeRep y
instance Eq Abstract where a == b = a `absEq` b
instance Show Abstract where show a = "makeAbs (" ++ absStr a ++ ")"
instance Ord Abstract where a `compare` b = a `absCmp` b