mappings-0.4.0.0: src/Data/Mapping.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Mapping where
import Prelude hiding (not, (&&), (||))
import Data.Algebra.Boolean (Boolean(..), AllB(..))
import Data.Bool (bool)
import Data.Constraint.Trivial (Unconstrained)
import Data.Foldable.WithIndex (FoldableWithIndex(..))
import Data.Function (on)
import Data.Functor.Const (Const(..))
import Data.Functor.Identity (Identity(..))
import Data.Kind (Constraint, Type)
import Data.PartialOrd
import Data.Set (Set)
import qualified Data.Set as S
import Data.Void (Void)
-- | If @Mapping k m@, then @m v@ represents a function @k -> v@.
--
-- `Mapping` requires an instance of `Foldable`, folding over the
-- values that appear. Given that a value can be associated with a
-- very large collection of keys, the only folds that normally make
-- sense are those over idempotent monoids.
class (Foldable m, forall x. c x => c (m x)) => Mapping (c :: Type -> Constraint) (k :: Type) (m :: Type -> Type) | m -> k, m -> c where
-- | Form a constant Mapping
cst :: v -> m v
-- | Use a Mapping as a function
act :: m v -> k -> v
-- | Is the Mapping a constant (and if so with what value)?
isConst :: c v => m v -> Maybe v
-- | A constrained Traverse
mtraverse :: (Applicative f, c v) => (u -> f v) -> m u -> f (m v)
-- | A constrained Functor
mmap :: c v => (u -> v) -> m u -> m v
mmap p = runIdentity . mtraverse (Identity . p)
-- | Merge two Mappings, valued in an applicative
mergeA :: (Applicative f, c w) => (u -> v -> f w) -> m u -> m v -> f (m w)
-- | Merge two Mappings
merge :: c w => (u -> v -> w) -> m u -> m v -> m w
merge p m n = let
q x y = Identity $ p x y
in runIdentity $ mergeA q m n
-- | Merge three Mappings
merge3 :: c x
=> (u -> v -> w -> x)
-> m u -> m v -> m w -> m x
-- | Merge three Mappings
mergeA3 :: (Applicative f, c x)
=> (u -> v -> w -> f x)
-> m u -> m v -> m w -> f (m x)
-- | A simultaneous foldMap over two maps; pairMappings is to
-- foldMap as mmerge is to mmap
pairMappings :: forall a u v. (Monoid a, c Void) => (u -> v -> a) -> m u -> m v -> a
pairMappings p m n = let
q :: u -> v -> Const a Void
q x y = Const $ p x y
in getConst (mergeA q m n)
-- | A monad-like structure
bind :: (c u, c v, Ord u, c (Either v u)) => (u -> m v) -> m u -> m v
bind f m = let
start [] = error "bind: expected some values"
start [x] = f x
start (x:xs) = let
p y z = if y == x then Left z else Right y
in continue xs $ merge p m (f x)
continue [] _ = error "bind: expected more values"
continue [x] n = let
p (Left z) _ = z
p (Right _) z = z
in merge p n (f x)
continue (x:xs) n = let
p (Left z) _ = Left z
p (Right y) z = if y == x then Left z else Right y
in continue xs $ merge p n (f x)
in start . S.toList $ values m
-- | This commonly-used function is the reason why we have three-way
-- merges in the typeclass
boolBind :: (Mapping c k m, c v) => m v -> m v -> m Bool -> m v
boolBind = merge3 bool
values :: (Mapping c k m, Ord v) => m v -> Set v
values = foldMap S.singleton
-- | What values can these two take simultaneously?
mutualValues :: (Ord u, Ord v, c Void, Mapping c k m) => m u -> m v -> Set (u, v)
mutualValues = pairMappings $ curry S.singleton
-- | A class representing data structures which have a concept of neighbouring
-- values
class Neighbourly m where
neighbours :: Ord v => m v -> Set (v, v)
-- | A wrapper for representing pointwise algebraic structures on a Mapping
--
-- Eventually would like to use this only for "deriving via"
newtype AlgebraWrapper (c :: Type -> Constraint) k m a = AlgebraWrapper { algebraUnwrap :: m a }
instance (Mapping c k m, c a, Semigroup a) => Semigroup (AlgebraWrapper c k m a) where
(<>) = (AlgebraWrapper .) . merge (<>) `on` algebraUnwrap
instance (Mapping c k m, c a, Monoid a) => Monoid (AlgebraWrapper c k m a) where
mempty = AlgebraWrapper $ cst mempty
instance (Mapping c k m, c a, Num a) => Num (AlgebraWrapper c k m a) where
(+) = (AlgebraWrapper .) . merge (+) `on` algebraUnwrap
(-) = (AlgebraWrapper .) . merge (-) `on` algebraUnwrap
(*) = (AlgebraWrapper .) . merge (*) `on` algebraUnwrap
fromInteger = AlgebraWrapper . cst . fromInteger
abs = AlgebraWrapper . mmap abs . algebraUnwrap
negate = AlgebraWrapper . mmap negate . algebraUnwrap
signum = AlgebraWrapper . mmap signum . algebraUnwrap
instance (Mapping c k m, c a, Boolean a) => Boolean (AlgebraWrapper c k m a) where
true = AlgebraWrapper $ cst true
false = AlgebraWrapper $ cst false
not = AlgebraWrapper . mmap not . algebraUnwrap
(&&) = (AlgebraWrapper .) . merge (&&) `on` algebraUnwrap
(||) = (AlgebraWrapper .) . merge (||) `on` algebraUnwrap
xor = (AlgebraWrapper .) . merge xor `on` algebraUnwrap
(-->) = (AlgebraWrapper .) . merge (-->) `on` algebraUnwrap
(<-->) = (AlgebraWrapper .) . merge (<-->) `on` algebraUnwrap
-- | Constant functions (on any domain)
newtype Constant k v = Constant { constantValue :: v }
deriving (Eq, Ord)
instance Foldable (Constant k) where
foldMap f (Constant a) = f a
instance Mapping Unconstrained k (Constant k) where
cst = Constant
act (Constant x) _ = x
isConst (Constant x) = Just x
mmap f (Constant x) = Constant $ f x
mtraverse f (Constant x) = Constant <$> f x
merge f (Constant x) (Constant y) = Constant $ f x y
merge3 f (Constant x) (Constant y) (Constant z) = Constant $ f x y z
mergeA f (Constant x) (Constant y) = Constant <$> f x y
mergeA3 f (Constant x) (Constant y) (Constant z) = Constant <$> f x y z
pairMappings f (Constant x) (Constant y) = f x y
bind f (Constant x) = f x
instance Neighbourly (Constant k) where
neighbours = const S.empty
-- Haven't been able to make deriving via work for this one
instance (Semigroup v) => Semigroup (Constant k v) where
Constant x <> Constant y = Constant (x <> y)
-- Haven't been able to make deriving via work for this one
instance (Monoid v) => Monoid (Constant k v) where
mempty = Constant mempty
-- Haven't been able to make deriving via work for this one
instance (Num v) => Num (Constant k v) where
Constant x + Constant y = Constant (x + y)
Constant x - Constant y = Constant (x - y)
Constant x * Constant y = Constant (x * y)
abs (Constant x) = Constant (abs x)
negate (Constant x) = Constant (negate x)
signum (Constant x) = Constant (signum x)
fromInteger = Constant . fromInteger
-- Haven't been able to make deriving via work for this one
instance (Boolean v) => Boolean (Constant k v) where
false = Constant false
true = Constant true
not (Constant x) = Constant (not x)
Constant x && Constant y = Constant (x && y)
Constant x || Constant y = Constant (x || y)
xor (Constant x) (Constant y) = Constant (xor x y)
Constant x <--> Constant y = Constant (x <--> y)
Constant x --> Constant y = Constant (x --> y)
-- | Binary decisions, as functions defined on Bool
data OnBool a = OnBool {
onFalse :: a,
onTrue :: a
} deriving (Eq, Ord, Show, Functor)
instance Foldable OnBool where
foldMap p (OnBool x y) = p x <> p y
instance FoldableWithIndex Bool OnBool where
ifoldMap p (OnBool x y) = p False x <> p True y
instance Traversable OnBool where
traverse f (OnBool x y) = liftA2 OnBool (f x) (f y)
instance Mapping Eq Bool OnBool where
cst x = OnBool x x
act (OnBool x _) False = x
act (OnBool _ x) True = x
isConst (OnBool x y)
| x == y = Just x
| otherwise = Nothing
mmap = fmap
mtraverse = traverse
mergeA h (OnBool x1 y1) (OnBool x2 y2) =
liftA2 OnBool (h x1 x2) (h y1 y2)
merge h (OnBool x1 y1) (OnBool x2 y2) =
OnBool (h x1 x2) (h y1 y2)
mergeA3 h (OnBool x1 y1) (OnBool x2 y2) (OnBool x3 y3) =
liftA2 OnBool (h x1 x2 x3) (h y1 y2 y3)
merge3 h (OnBool x1 y1) (OnBool x2 y2) (OnBool x3 y3) =
OnBool (h x1 x2 x3) (h y1 y2 y3)
pairMappings p (OnBool x1 y1) (OnBool x2 y2) = p x1 x2 <> p y1 y2
bind f (OnBool u v) = OnBool (onFalse (f u)) (onTrue (f v))
instance Neighbourly OnBool where
neighbours (OnBool x y)
| x == y = S.empty
| otherwise = S.singleton (x, y)
deriving via (AlgebraWrapper Eq Bool OnBool a)
instance (Eq a, Semigroup a) => Semigroup (OnBool a)
deriving via (AlgebraWrapper Eq Bool OnBool a)
instance (Eq a, Monoid a) => Monoid (OnBool a)
deriving via (AlgebraWrapper Eq Bool OnBool a)
instance (Eq a, Num a) => Num (OnBool a)
deriving via (AlgebraWrapper Eq Bool OnBool b)
instance (Eq b, Boolean b) => Boolean (OnBool b)
-- | Maps on Maybe
data OnMaybe c k m v = OnMaybe {
onNothing :: v,
onJust :: m v
} deriving (Eq, Ord)
instance Foldable m => Foldable (OnMaybe c k m) where
foldMap f (OnMaybe x a) = f x <> foldMap f a
instance FoldableWithIndex k m => FoldableWithIndex (Maybe k) (OnMaybe c k m) where
ifoldMap f (OnMaybe x a) = f Nothing x <> ifoldMap (f . Just) a
instance (forall x. c x => Eq x, forall x. c x => c (OnMaybe c k m x), Mapping c k m) => Mapping c (Maybe k) (OnMaybe c k m) where
cst x = OnMaybe x $ cst x
mmap p (OnMaybe x a) = OnMaybe (p x) (mmap p a)
mtraverse p (OnMaybe x a) = liftA2 OnMaybe (p x) (mtraverse p a)
act (OnMaybe x _) Nothing = x
act (OnMaybe _ a) (Just y) = act a y
isConst (OnMaybe x a) = do
y <- isConst a
if x == y then Just x else Nothing
merge h (OnMaybe x a) (OnMaybe y b) =
OnMaybe (h x y) (merge h a b)
mergeA h (OnMaybe x a) (OnMaybe y b) =
liftA2 OnMaybe (h x y) (mergeA h a b)
merge3 h (OnMaybe x a) (OnMaybe y b) (OnMaybe z c) =
OnMaybe (h x y z) (merge3 h a b c)
mergeA3 h (OnMaybe x a) (OnMaybe y b) (OnMaybe z c) =
liftA2 OnMaybe (h x y z) (mergeA3 h a b c)
pairMappings p (OnMaybe x a) (OnMaybe y b) = p x y <> pairMappings p a b
bind f (OnMaybe x m) = OnMaybe (onNothing (f x)) (bind (onJust . f) m)
deriving via (AlgebraWrapper c (Maybe k) (OnMaybe c k m) a)
instance (forall x. c x => Eq x, forall x. c x => c (OnMaybe c k m x), Mapping c k m, c a, Semigroup a) => Semigroup (OnMaybe c k m a)
deriving via (AlgebraWrapper c (Maybe k) (OnMaybe c k m) a)
instance (forall x. c x => Eq x, forall x. c x => c (OnMaybe c k m x), Mapping c k m, c a, Monoid a) => Monoid (OnMaybe c k m a)
deriving via (AlgebraWrapper c (Maybe k) (OnMaybe c k m) a)
instance (forall x. c x => Eq x, forall x. c x => c (OnMaybe c k m x), Mapping c k m, c a, Num a) => Num (OnMaybe c k m a)
deriving via (AlgebraWrapper c (Maybe k) (OnMaybe c k m) a)
instance (forall x. c x => Eq x, forall x. c x => c (OnMaybe c k m x), Mapping c k m, c a, Boolean a) => Boolean (OnMaybe c k m a)
-- | Maps on Either
data OnEither (c :: Type -> Constraint) (k :: Type) (l :: Type) (m :: Type -> Type) (n :: Type -> Type) (v :: Type) = OnEither {
onLeft :: m v,
onRight :: n v
} deriving (Eq, Ord)
instance (Foldable m, Foldable n) => Foldable (OnEither c k l m n) where
foldMap p (OnEither f g) = foldMap p f <> foldMap p g
instance (FoldableWithIndex k m, FoldableWithIndex l n)
=> FoldableWithIndex (Either k l) (OnEither c k l m n) where
ifoldMap p (OnEither f g) = ifoldMap (p . Left) f <> ifoldMap (p . Right) g
instance (Mapping c k m,
Mapping c l n,
forall x. c x => Eq x,
forall x. c x => c (OnEither c k l m n x))
=> Mapping c (Either k l) (OnEither c k l m n) where
cst x = OnEither (cst x) (cst x)
mmap p (OnEither f g) = OnEither (mmap p f) (mmap p g)
mtraverse p (OnEither f g) = liftA2 OnEither (mtraverse p f) (mtraverse p g)
act (OnEither f _) (Left x) = act f x
act (OnEither _ g) (Right y) = act g y
isConst (OnEither f g) = do
x <- isConst f
y <- isConst g
if x == y then Just x else Nothing
mergeA h (OnEither f1 g1) (OnEither f2 g2) =
liftA2 OnEither (mergeA h f1 f2) (mergeA h g1 g2)
merge h (OnEither f1 g1) (OnEither f2 g2) =
OnEither (merge h f1 f2) (merge h g1 g2)
mergeA3 p (OnEither f1 g1) (OnEither f2 g2) (OnEither f3 g3) =
liftA2 OnEither (mergeA3 p f1 f2 f3) (mergeA3 p g1 g2 g3)
merge3 p (OnEither f1 g1) (OnEither f2 g2) (OnEither f3 g3) =
OnEither (merge3 p f1 f2 f3) (merge3 p g1 g2 g3)
pairMappings p (OnEither f1 g1) (OnEither f2 g2) = pairMappings p f1 f2 <> pairMappings p g1 g2
bind f (OnEither u v) = OnEither (bind (onLeft . f) u) (bind (onRight . f) v)
deriving via (AlgebraWrapper c (Either k l) (OnEither c k l (m :: Type -> Type) n) a)
instance (Mapping c k m,
Mapping c l n,
c a,
forall x. c x => Eq x,
forall x. c x => c (OnEither c k l m n x),
Semigroup a) => Semigroup (OnEither c k l m n a)
deriving via (AlgebraWrapper c (Either k l) (OnEither c k l (m :: Type -> Type) n) a)
instance (Mapping c k m,
Mapping c l n,
c a,
forall x. c x => Eq x,
forall x. c x => c (OnEither c k l m n x),
Monoid a) => Monoid (OnEither c k l m n a)
deriving via (AlgebraWrapper c (Either k l) (OnEither c k l (m :: Type -> Type) n) a)
instance (Mapping c k m,
Mapping c l n,
c a,
forall x. c x => Eq x,
forall x. c x => c (OnEither c k l m n x),
Num a) => Num (OnEither c k l m n a)
deriving via (AlgebraWrapper c (Either k l) (OnEither c k l (m :: Type -> Type) n) a)
instance (Mapping c k m,
Mapping c l n,
c a,
forall x. c x => Eq x,
forall x. c x => c (OnEither c k l m n x),
Boolean a) => Boolean (OnEither c k l m n a)
newtype Reconstrained (c :: Type -> Constraint) (d :: Type -> Constraint) (k :: Type) (m :: Type -> Type) (a :: Type) = Reconstrained {
preconstrained :: m a
} deriving (Eq, Ord, Foldable, FoldableWithIndex k, Neighbourly)
instance (forall x. d x => c x, forall x. d x => d (Reconstrained c d k m x), Mapping c k m) => Mapping d k (Reconstrained c d k m) where
cst = Reconstrained . cst
act = act . preconstrained
isConst = isConst . preconstrained
mtraverse f = fmap Reconstrained . mtraverse f . preconstrained
mmap f = Reconstrained . mmap f . preconstrained
mergeA p (Reconstrained m) (Reconstrained n) = Reconstrained <$> mergeA p m n
merge p (Reconstrained m) (Reconstrained n) = Reconstrained $ merge p m n
mergeA3 p (Reconstrained m) (Reconstrained n) (Reconstrained o) = Reconstrained <$> mergeA3 p m n o
merge3 p (Reconstrained m) (Reconstrained n) (Reconstrained o) = Reconstrained $ merge3 p m n o
-- | Maps on pairs
newtype OnPair c k l m n v = OnPair {
asComposite :: m (n v)
} deriving (Eq, Ord)
instance (Foldable m, Foldable n) => Foldable (OnPair c k l m n) where
foldMap p (OnPair f) = foldMap (foldMap p) f
instance (FoldableWithIndex k m, FoldableWithIndex l n) => FoldableWithIndex (k,l) (OnPair c k l m n) where
ifoldMap p (OnPair f) = let
h x = ifoldMap (p . (x,))
in ifoldMap h f
instance (Mapping c k m,
Mapping c l n,
forall x. c x => c (OnPair c k l m n x))
=> Mapping c (k, l) (OnPair c k l m n) where
cst x = OnPair . cst $ cst x
mmap p (OnPair f) = OnPair (mmap (mmap p) f)
mtraverse p (OnPair f) = OnPair <$> mtraverse (mtraverse p) f
act (OnPair f) (x, y) = act (act f x) y
isConst (OnPair f) = isConst =<< isConst f
mergeA h (OnPair f) (OnPair g) = OnPair <$> mergeA (mergeA h) f g
merge h (OnPair f) (OnPair g) = OnPair $ merge (merge h) f g
merge3 p (OnPair f) (OnPair g) (OnPair h) = OnPair $ merge3 (merge3 p) f g h
mergeA3 p (OnPair f) (OnPair g) (OnPair h) = OnPair <$> mergeA3 (mergeA3 p) f g h
pairMappings p (OnPair f) (OnPair g) = pairMappings (pairMappings p) f g
deriving via (AlgebraWrapper (c :: Type -> Constraint) (k, l) (OnPair c k l (m :: Type -> Type) (n :: Type -> Type)) a)
instance (Mapping c k m, Mapping c l n,
forall x. c x => c (OnPair c k l m n x), c a, Semigroup a) => Semigroup (OnPair c k l m n a)
deriving via (AlgebraWrapper (c :: Type -> Constraint) (k, l) (OnPair c k l (m :: Type -> Type) (n :: Type -> Type)) a)
instance (Mapping c k m, Mapping c l n,
forall x. c x => c (OnPair c k l m n x), c a, Monoid a) => Monoid (OnPair c k l m n a)
deriving via (AlgebraWrapper (c :: Type -> Constraint) (k, l) (OnPair c k l (m :: Type -> Type) (n :: Type -> Type)) a)
instance (Mapping c k m, Mapping c l n,
forall x. c x => c (OnPair c k l m n x), c a, Num a) => Num (OnPair c k l m n a)
deriving via (AlgebraWrapper (c :: Type -> Constraint) (k, l) (OnPair c k l (m :: Type -> Type) (n :: Type -> Type)) b)
instance (Mapping c k m, Mapping c l n,
forall x. c x => c (OnPair c k l m n x), c b, Boolean b) => Boolean (OnPair c k l m n b)
-- | Is the first a subset of the second?
isSubset :: (Boolean b, c Void, Mapping c k m) => m b -> m b -> b
isSubset m n = let
p x y = AllB (x --> y)
in getAllB $ pairMappings p m n
-- | Are the two true on distinct values?
isDisjoint :: (Boolean b, c Void, Mapping c k m) => m b -> m b -> b
isDisjoint m n = let
p x y = AllB (not (x && y))
in getAllB $ pairMappings p m n
-- | A wrapper to allow defining `PartialOrd` instances on mappings whose keys
-- have an `Ord` instance.
newtype OrdWrapper c k m v = OrdWrapper {
getOrdMapping :: m v
}
instance (Mapping c k m, c Void, Ord v) => PartialOrd (OrdWrapper c k m v) where
compare' (OrdWrapper u) (OrdWrapper v) = pairMappings fromCompare u v
-- | A wrapper to allow defining `PartialOrd` instances on mappings whose keys
-- have a `PartialOrd` instance.
newtype PartialOrdWrapper c k m v = PartialOrdWrapper {
getPartialOrdMapping :: m v
}
instance (Mapping c k m, c Void, PartialOrd v) => PartialOrd (PartialOrdWrapper c k m v) where
compare' (PartialOrdWrapper u) (PartialOrdWrapper v) = pairMappings compare' u v