{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, DeriveDataTypeable, DeriveFunctor, DeriveTraversable, DeriveFoldable, MultiParamTypeClasses, TypeFamilies #-} -- | -- Defines two variants of @(,)@ with lifted instances for the standard type classes. -- -- The 'Functor', 'Applicative' and 'Comonad' instances are the standard instances. The -- 'Monad' instances are not in base (but should argubly be there). All of these instances -- are equivalent to 'Writer' in transformers. -- -- 'Applicative' is used to lift 'Monoid' and the standard numeric classes. -- -- The only difference between 'Twain' and 'Couple' is the handling of 'Eq' and 'Ord': -- 'Twain' compares only the second value, while 'Couple' compares both. Thus 'Couple' needs -- an extra @Ord b@ constraint for all sub-classes of 'Ord'. -- module Data.Functor.Couple (Twain(..), Couple(..)) where import Data.Bifunctor import Data.Functor.Product import Data.Functor.Identity import Data.Foldable import Data.Traversable import Data.Functor.Adjunction (unzipR) import Data.Semigroup import Data.Typeable import Control.Applicative import Control.Comonad import Data.PairMonad () import Control.Lens (Wrapped(..), Rewrapped(..), iso) -- | -- A variant of pair/writer with lifted instances for the numeric classes, using 'Applicative'. -- newtype Twain b a = Twain { getTwain :: (b, a) } deriving (Show, Functor, Traversable, Foldable, Typeable, Applicative, Monad, Comonad, Semigroup, Monoid) instance Wrapped (Twain b a) where type Unwrapped (Twain b a) = (b, a) _Wrapped' = iso getTwain Twain instance Rewrapped (Twain c a) (Twain c b) instance (Monoid b, Num a) => Num (Twain b a) where (+) = liftA2 (+) (*) = liftA2 (*) (-) = liftA2 (-) abs = fmap abs signum = fmap signum fromInteger = pure . fromInteger instance (Monoid b, Fractional a) => Fractional (Twain b a) where recip = fmap recip fromRational = pure . fromRational instance (Monoid b, Floating a) => Floating (Twain b a) where pi = pure pi sqrt = fmap sqrt exp = fmap exp log = fmap log sin = fmap sin cos = fmap cos asin = fmap asin atan = fmap atan acos = fmap acos sinh = fmap sinh cosh = fmap cosh asinh = fmap asinh atanh = fmap atanh acosh = fmap acos instance (Monoid b, Enum a) => Enum (Twain b a) where toEnum = pure . toEnum fromEnum = fromEnum . extract instance (Monoid b, Bounded a) => Bounded (Twain b a) where minBound = pure minBound maxBound = pure maxBound -- -- Eq, Ord and their subclasses -- -- If comparison takes both values into account, we must add and (Ord b) -- constraint to all of the following instances. Instead, follow the -- spirit of the Num et al instances to compare just the second argument. -- instance Eq a => Eq (Twain b a) where Twain (b,a) == Twain (b',a') = a == a' instance Ord a => Ord (Twain b a) where Twain (b,a) < Twain (b',a') = a < a' instance (Monoid b, Real a, Enum a, Integral a) => Integral (Twain b a) where quot = liftA2 quot rem = liftA2 rem quotRem = fmap (fmap unzipR) (liftA2 quotRem) toInteger = toInteger . extract instance (Monoid b, Real a) => Real (Twain b a) where toRational = toRational . extract instance (Monoid b, RealFrac a) => RealFrac (Twain b a) where properFraction = first extract . unzipR . fmap properFraction -- | -- A variant of pair/writer with lifted instances for the numeric classes, using 'Applicative'. -- newtype Couple b a = Couple { getCouple :: (b, a) } deriving (Show, Functor, Foldable, Traversable, Typeable, Applicative, Monad, Comonad, Semigroup, Monoid) instance Wrapped (Couple b a) where type Unwrapped (Couple b a) = (b, a) _Wrapped' = iso getCouple Couple instance Rewrapped (Couple c a) (Couple c b) instance (Monoid b, Num a) => Num (Couple b a) where (+) = liftA2 (+) (*) = liftA2 (*) (-) = liftA2 (-) abs = fmap abs signum = fmap signum fromInteger = pure . fromInteger instance (Monoid b, Fractional a) => Fractional (Couple b a) where recip = fmap recip fromRational = pure . fromRational instance (Monoid b, Floating a) => Floating (Couple b a) where pi = pure pi sqrt = fmap sqrt exp = fmap exp log = fmap log sin = fmap sin cos = fmap cos asin = fmap asin atan = fmap atan acos = fmap acos sinh = fmap sinh cosh = fmap cosh asinh = fmap asinh atanh = fmap atanh acosh = fmap acos instance (Monoid b, Enum a) => Enum (Couple b a) where toEnum = pure . toEnum fromEnum = fromEnum . extract instance (Monoid b, Bounded a) => Bounded (Couple b a) where minBound = pure minBound maxBound = pure maxBound instance (Eq b, Eq a) => Eq (Couple b a) where Couple ((b,a)) == Couple (b',a') = (b,a) == (b',a') instance (Ord b, Ord a) => Ord (Couple b a) where Couple (b,a) < Couple (b',a') = (b,a) < (b',a') instance (Monoid b, Ord b, Real a, Enum a, Integral a) => Integral (Couple b a) where quot = liftA2 quot rem = liftA2 rem quotRem = fmap (fmap unzipR) (liftA2 quotRem) toInteger = toInteger . extract instance (Monoid b, Ord b, Real a) => Real (Couple b a) where toRational = toRational . extract instance (Monoid b, Ord b, RealFrac a) => RealFrac (Couple b a) where properFraction = first extract . unzipR . fmap properFraction