% MonoidPlus and other classes % [Public domain] % version 0.1 \input birdstyle \birdleftrule=1pt \emergencystretch=1em \def\hugebreak{\penalty-600\vskip 30pt plus 8pt minus 4pt\relax} \newcount\chapno \def\: #1.{\advance\chapno by 1\relax\hugebreak{\bf\S\the\chapno. #1. }} \: Introduction. This is program for related things about monoids. It also has a type for bounded numbers.
> {-# LANGUAGE FlexibleInstances #-}
Exports:
> module Data.Monoid.Plus (
>   module Data.Semigroup, MonoidPlus(..), Group(..), MonoidMinus(..),
>   MonoidNorm(..), MonoidPlusNorm(..), Semiring, Ring, (|*|), (|/|),
>   (|+|), (|-|), BoundFrac, fromBoundFrac, toBoundFrac,
>   WrapMonoidPlus(..), CatEndo(..), Possibilistic(..), Lukasiewicz(..),
>   monoidicMap, mpure, Prob, pChoose, pChoice, probNorm, uniform, probOf
> ) where {
Imports:
> import Control.Applicative;
> import Control.Category hiding (id, (.));
> import qualified Control.Category as C;
> import Control.Monad;
> import Control.Monad.Trans.Writer;
> import Data.Functor.Contravariant;
> import Data.List;
> import Data.Monoid;
> import Data.Ord;
> import Data.Semigroup hiding (First, Last, getFirst, getLast);
\: Classes. Other than {\tt Monoid} and {\tt MonoidPlus}, here are some other related classes. This class is for monoids that have another monoid operation on them following specific laws.
> class Monoid t => MonoidPlus t where {
>   mpempty :: t;
>   mpappend :: t -> t -> t;
>   mpconcat :: [t] -> t;
>   mpconcat = foldr mpappend mpempty;
> };
Instances should follow these laws:
> {-
>   mpappend mpempty x = x;
>   mpappend x mpempty = x;
>   mpappend x (mpappend y z) = mpappend (mpappend x y) z;
>   mpconcat = foldr mpappend mpempty;
>   mappend x (mpappend y z) = mpappend (mappend x y) (mappend x z);
>   mappend (mpappend y z) x = mpappend (mappend y x) (mappend z x);
> -}
That is, it is a monoid that the original monoid is distributive over. This class is for groups. All groups are monoids, and each elements can be inversed.
> class Monoid t => Group t where {
>   minverse :: t -> t;
> };
Instances should follow these laws:
> {-
>   mappend x (minverse x) = mempty;
>   mappend (minverse x) x = mempty;
> -}
The {\tt MonoidPlus} instances can also form a group, which here is called {\tt MonoidMinus}.
> class MonoidPlus t => MonoidMinus t where {
>   mpinverse :: t -> t;
> };
Instances should follow these laws:
> {-
>   mpappend x (mpinverse x) = mpempty;
>   mpappend (mpinverse x) x = mpempty;
> -}
This class is for normalizable monoids; for example, a list of numbers normalized so that they add up to 1, such as with probabilities.
> class Monoid t => MonoidNorm t where {
>   mnormfunc :: [t] -> t -> t;
>   mnormalize :: [t] -> [t];
>   mnormalize x = mnormfunc x <$> x;
> };
Instances should follow these laws:
> {-
>   mnormalize x = mnormfunc x <$> x;
>   mnormalize (mnormalize x) = mnormalize x;
>   mconcat (mnormalize x) = mempty;
>   shuffle (mnormalize x) = mnormalize (shuffle x);
> -}
for any possible bijective function {\tt shuffle :: [a] -> [a]}. Sometimes you want normalization over its distributive monoid.
> class MonoidPlus t => MonoidPlusNorm t where {
>   mpnormfunc :: [t] -> t -> t;
>   mpnormalize :: [t] -> [t];
>   mpnormalize x = mpnormfunc x <$> x;
> };
Instances should follow these laws:
> {-
>   mpnormalize x = mpnormfunc x <$> x;
>   mpnormalize (mpnormalize x) = mpnormalize x;
>   mpconcat (mpnormalize (x:y)) = mempty;
>   shuffle (mpnormalize x) = mpnormalize (shuffle x);
> -}
Some {\tt MonoidPlus} instances are semirings. There are no additional class methods for semirings.
> class MonoidPlus t => Semiring t;
Instances should follow these laws:
> {-
>   mappend mpempty x = mpempty;
>   mappend x mpempty = mpempty;
>   mpappend x y = mpappend y x;
> -}
Some semirings are rings.
> class (Semiring t, MonoidMinus t) => Ring t;
There are no additional laws; they follow from the laws of the classes that are required to make this class. \: Bounded Fractions. Due to the use of bounded fractional numbers in some rings, here is a type for bounded fractions. The type is exported without its constructor; you can use it as a number.
> newtype BoundFrac t = BoundFrac { fromBoundFrac :: t }
>  deriving (Eq, Ord);
> toBoundFrac :: (Num t, Ord t) => t -> BoundFrac t;
> toBoundFrac x = if x < 0 || x > 1 then error "Out of bounds"
>  else BoundFrac x;
> instance Show t => Show (BoundFrac t) where {
>   show (BoundFrac x) = show x;
> };
> instance (Enum t, Fractional t, Ord t) => Enum (BoundFrac t) where {
>   toEnum 0 = BoundFrac 0.0;
>   toEnum 1 = BoundFrac 1.0;
>   toEnum _ = error "Out of bounds";
>   fromEnum (BoundFrac 1.0) = 1;
>   fromEnum _ = 0;
> };
Because it is bounded, it can have a {\tt Bounded} instance too.
> instance (Enum t, Fractional t, Ord t)
>  => Bounded (BoundFrac t) where {
>   minBound = BoundFrac 0.0;
>   maxBound = BoundFrac 1.0;
> };
All numeric operations must check that it is in bounds.
> instance (Num t, Ord t) => Num (BoundFrac t) where {
>   (BoundFrac x) + (BoundFrac y) = if x + y < 0 || x + y > 1 then
>    error "Out of bounds" else BoundFrac (x + y);
>   (BoundFrac x) - (BoundFrac y) = if x - y < 0 || x - y > 1 then
>    error "Out of bounds" else BoundFrac (x - y);
>   (BoundFrac x) * (BoundFrac y) = if x * y < 0 || x * y > 1 then
>    error "Out of bounds" else BoundFrac (x * y);
>   negate (BoundFrac 0) = BoundFrac 0;
>   negate _ = error "Out of bounds";
>   abs = id;
>   signum (BoundFrac 0) = BoundFrac 0;
>   signum _ = BoundFrac 1;
>   fromInteger 0 = BoundFrac 0;
>   fromInteger 1 = BoundFrac 1;
>   fromInteger _ = error "Out of bounds";
> };
> instance (Fractional t, Ord t) => Fractional (BoundFrac t) where {
>   (BoundFrac x) / (BoundFrac y) = if x / y < 0 || x / y > 1 then
>    error "Out of bounds" else BoundFrac (x / y);
>   fromRational x = if x < 0 || x > 1 then error "Out of bounds"
>    else BoundFrac (fromRational x);
> };
> instance Real t => Real (BoundFrac t) where {
>   toRational (BoundFrac x) = toRational x;
> };
> instance (Real t, Fractional t) => RealFrac (BoundFrac t) where {
>   properFraction 1 = (1, 0);
>   properFraction x = (0, x);
> };
\: New Monoid Types. Any {\tt MonoidPlus} forms its own monoid; there can be a wrapper type to make it able to do so.
> newtype WrapMonoidPlus t = WrapMonoidPlus t deriving (Eq, Ord, Show);
> instance MonoidPlus t => Monoid (WrapMonoidPlus t) where {
>   mempty = WrapMonoidPlus mpempty;
>   mappend (WrapMonoidPlus x) (WrapMonoidPlus y) = WrapMonoidPlus
>    (mpappend x y);
> };
Endomorphisms of a category form a monoid (including the Kleisli category of a monad).
> newtype CatEndo c t = CatEndo { runCatEndo :: c t t };
> instance Category c => Monoid (CatEndo c t) where {
>   mempty = CatEndo C.id;
>   mappend (CatEndo x) (CatEndo y) = CatEndo $ x C.. y;
> };
One semiring of bounded numbers is possibilistic semiring.
> newtype Possibilistic t = Possibilistic { getPossibilistic ::
>  BoundFrac t } deriving (Eq, Ord, Show);
> instance (Num t, Ord t) => Monoid (Possibilistic t) where {
>   mempty = Possibilistic 1;
>   mappend (Possibilistic x) (Possibilistic y) = Possibilistic (x * y);
> };
> instance (Num t, Ord t) => MonoidPlus (Possibilistic t) where {
>   mpempty = Possibilistic 0;
>   mpappend (Possibilistic x) (Possibilistic y) = Possibilistic $
>    max x y;
> };
> instance (Num t, Ord t) => Semiring (Possibilistic t);
It is also a normalizable semiring; everything is multiplied so that the max value will be 1.
> instance (Fractional t, Ord t) => MonoidPlusNorm (Possibilistic t)
>  where {
>   mpnormfunc a (Possibilistic v) = if all (== Possibilistic 0) a then
>    Possibilistic 1 else (Possibilistic . toBoundFrac $ recip
>    (fromBoundFrac . getPossibilistic . last $ sort a) *
>    fromBoundFrac v);
> };
Another semiring of bounded numbers is Lukasiewicz semiring.
> newtype Lukasiewicz t = Lukasiewicz { getLukasiewicz :: BoundFrac t }
>  deriving (Eq, Ord, Show);
> instance (Num t, Ord t) => Monoid (Lukasiewicz t) where {
>   mempty = Lukasiewicz 1;
>   mappend (Lukasiewicz x) (Lukasiewicz y) = Lukasiewicz . toBoundFrac $
>    max (fromBoundFrac x + fromBoundFrac y - 1) 0;
> };
> instance (Num t, Ord t) => MonoidPlus (Lukasiewicz t) where {
>   mpempty = Lukasiewicz 1;
>   mpappend (Lukasiewicz x) (Lukasiewicz y) = Lukasiewicz $ min x y;
> };
\: Instances. The unit type easily forms a group, and is distributive since it has only one possible value. It is also the trivial ring.
> instance MonoidPlus () where {
>   mpempty = ();
>   mpappend = const $ const ();
>   mpconcat = const ();
> };
> instance Group () where {
>   minverse = id;
> };
> instance MonoidMinus () where {
>   mpinverse = id;
> };
> instance MonoidNorm () where {
>   mnormfunc = flip const;
>   mnormalize = id;
> };
> instance MonoidPlusNorm () where {
>   mpnormfunc = flip const;
>   mpnormalize = id;
> };
> instance Semiring ();
> instance Ring ();
The distribution over addition is multiplication.
> instance Num t => MonoidPlus (Product t) where {
>   mpempty = Product 0;
>   mpappend (Product x) (Product y) = Product $ x + y;
> };
The distribution over logical conjunction is disjunction, and vice versa.
> instance MonoidPlus All where {
>   mpempty = All False;
>   mpappend (All x) (All y) = All (x || y);
> };
> instance MonoidPlus Any where {
>   mpempty = Any True;
>   mpappend (Any x) (Any y) = Any (x && y);
> };
It is also a semiring; the semiring laws are followed.
> instance Semiring All;
> instance Semiring Any;
Subtraction is the inverse of addition.
> instance Num t => Group (Sum t) where {
>   minverse = Sum . negate . getSum;
> };
The minimum and maximum operations distribute over each other.
> instance (Ord t, Bounded t) => MonoidPlus (Min t) where {
>   mpempty = minBound;
>   mpappend (Min x) (Min y) = Min $ max x y;
> };
> instance (Ord t, Bounded t) => MonoidPlus (Max t) where {
>   mpempty = maxBound;
>   mpappend (Max x) (Max y) = Max $ min x y;
> };
Fractional numbers can be normalized in addition and multiplication.
> instance Fractional t => MonoidNorm (Sum t) where {
>   mnormfunc a v = Sum $ getSum v - sum (getSum <$> a)
>    / fromIntegral (length a);
> };
> instance Fractional t => MonoidPlusNorm (Product t) where {
>   mpnormfunc a v = Product $ if sum (getProduct <$> a) == 0
>    then 1 / fromIntegral (length a)
>    else getProduct v / sum (getProduct <$> a);
> };
Most kinds of numbers form a ring.
> instance Num t => MonoidMinus (Product t) where {
>   mpinverse = Product . negate . getProduct;
> };
> instance Num t => Semiring (Product t);
> instance Num t => Ring (Product t);
There is a ring of sets. This kind of implementation is the same thing as a predicate, which is also a contravariant functor.
> instance Monoid (Predicate t) where {
>   mempty = Predicate $ const True;
>   mappend (Predicate x) (Predicate y) = Predicate $ \z -> x z && y z;
> };
> instance MonoidPlus (Predicate t) where {
>   mpempty = Predicate $ const False;
>   mpappend (Predicate x) (Predicate y) = Predicate $ \z -> x z /= y z;
> };
> instance MonoidMinus (Predicate t) where {
>   mpinverse = id;
> };
> instance Semiring (Predicate t);
> instance Ring (Predicate t);
Equivalences (also a contravariant functor) can form an Abelian group.
> instance Eq t => Monoid (Equivalence t) where {
>   mempty = Equivalence (==);
>   mappend (Equivalence f) (Equivalence g) = Equivalence $ \x y ->
>    (x == y) /= (f x y /= g x y);
> };
> instance Eq t => Group (Equivalence t) where {
>   minverse = id;
> };
If you have multiple groups, their direct product is a group.
> instance (Group a, Group b) => Group (a, b) where {
>   minverse (a, b) = (minverse a, minverse b);
> };
> instance (Group a, Group b, Group c) => Group (a, b, c) where {
>   minverse (a, b, c) = (minverse a, minverse b, minverse c);
> };
\: Operators. These are infix operator forms of the other functions.
> (|*|) :: Monoid t => t -> t -> t;
> (|*|) = mappend;
> infixr 5 |*|;
> (|/|) :: Group t => t -> t -> t;
> x |/| y = mappend x (minverse y);
> infixr 5 |/|;
> (|+|) :: MonoidPlus t => t -> t -> t;
> (|+|) = mpappend;
> infixr 4 |+|;
> (|-|) :: MonoidMinus t => t -> t -> t;
> x |-| y = mpappend x (mpinverse y);
> infixr 4 |-|;
\: Monoidic Monads. Although there is applicative for pairs having a monoid type, it is not a monad instance. A monad instance can be made.
> instance Monoid t => Monad ((,) t) where {
>   return = pure;
>   x >>= f = join $ fmap f x where {
>     join :: Monoid t => (t, (t, u)) -> (t, u);
>     join (x, (y, z)) = (x |*| y, z);
>   };
> };
It can make a monad transformer, too. In fact this monad transformer is {\tt WriterT}. Here are some extra functions for its use.
> monoidicMap :: Functor m => (x -> y) -> WriterT x m a -> WriterT y m a;
> monoidicMap = mapWriterT . fmap . fmap;
> mpure :: Applicative f => w -> t -> WriterT w f t;
> mpure x y = WriterT $ pure (y, x);
It can be used for probability distributions too, as long as the probabilities are any normalizable ring. (Note: All possible outputs on the right side of a bind (or all lists in a join) must have probabilities adding up to the same total!)
> type Prob a b = WriterT a [] b;
> pChoose :: Ring p => p -> t -> t -> Prob p t;
> pChoose p x y = WriterT [(x, p), (y, mempty |-| p)];
> pChoice :: Ring p => p -> Prob p t -> Prob p t -> Prob p t;
> pChoice p x y = join $ pChoose p x y;
This function will normalize the results so that the underlying list will be equal to any one representing the same probability distribution. It requires sorting, equality testing, etc.
> probNorm :: (Semiring p, MonoidPlusNorm p, Eq p, Ord t) => Prob p t
>  -> Prob p t;
> probNorm = WriterT . uncurry zip . (\(l, r) -> (l, mpnormalize r))
>  . unzip . filter ((/=) mpempty . snd) . map (\x -> (fst $ head x,
>  mpconcat $ snd <$> x)) . groupBy (\x y -> fst x == fst y) . sortBy
>  (comparing fst) . runWriterT;
> uniform :: (Semiring p, MonoidPlusNorm p) => [t] -> Prob p t;
> uniform x = WriterT $ (flip (,) $ mpnormfunc (mempty <$ x) mempty)
>  <$> x;
> probOf :: (Semiring p, MonoidPlusNorm p, Eq p)
>  => (t -> Bool) -> Prob p t -> p;
> probOf f x = case (runWriterT $ probNorm (f <$> x)) of {
>   [_, (True, p)] -> p;
>   [(True, p)] -> p;
>   _ -> mpempty;
> };
% [Monty Hall example] %> data Door = A | B | C deriving (Eq, Ord, Show); %> doors :: [Door]; %> doors = [A, B, C]; %> hide :: Prob (Product Double) Door; %> hide = uniform doors; %> pick :: Prob (Product Double) Door; %> pick = uniform doors; %> tease :: Door -> Door -> Prob (Product Double) Door; %> tease h p = uniform (doors \\ [h, p]); %> switch :: Door -> Door -> Prob (Product Double) Door; %> switch p t = return $ head (doors \\ [p, t]); %> stick :: Door -> Door -> Prob (Product Double) Door; %> stick p t = return p; %> play :: (Door -> Door -> Prob (Product Double) Door) %> -> Prob (Product Double) Bool; %> play strategy = probNorm $ do { %> h <- hide; %> p <- pick; %> t <- tease h p; %> s <- strategy p t; %> return (s == h); %> }; % End of document (final "}" is suppressed from printout) \toks0={{
> } -- }\bye