{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
------------------------------------------------------------------------------- |-- Module : Data.Distributive-- Copyright : (C) 2011-2014 Edward Kmett-- License : BSD-style (see the file LICENSE)---- Maintainer : Edward Kmett <ekmett@gmail.com>-- Stability : provisional-- Portability : portable------------------------------------------------------------------------------
module Data.Distributive
( Distributive(..)
, cotraverse
, comapM
) where
import Control.Applicative
import Control.Applicative.Backwards
import Control.Monad (liftM)
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 707
import Control.Monad.Instances ()
#endif
import Control.Monad.Trans.Identity
import Control.Monad.Trans.Reader
import Data.Functor.Compose
import Data.Functor.Identity
import Data.Functor.Product
import Data.Functor.Reverse
import Data.Proxy
import Data.Tagged
#ifdef HLINT
{-# ANN module "hlint: ignore Use section" #-}
#endif
-- | This is the categorical dual of 'Traversable'.---- Due to the lack of non-trivial comonoids in Haskell, we can restrict-- ourselves to requiring a 'Functor' rather than-- some Coapplicative class. Categorically every 'Distributive'-- functor is actually a right adjoint, and so it must be 'Representable'-- endofunctor and preserve all limits. This is a fancy way of saying it-- isomorphic to `(->) x` for some x.---- Minimal complete definition: 'distribute' or 'collect'---- To be distributable a container will need to have a way to consistently-- zip a potentially infinite number of copies of itself. This effectively-- means that the holes in all values of that type, must have the same-- cardinality, fixed sized vectors, infinite streams, functions, etc.-- and no extra information to try to merge together.--
class Functor g =>Distributiveg where
-- | The dual of 'Data.Traversable.sequenceA'---- >>> distribute [(+1),(+2)] 1-- [2,3]---- @'distribute' = 'collect' 'id'@distribute :: Functor f =>f (ga) -> g (fa)
distribute = collectid-- |-- @'collect' f = 'distribute' . 'fmap' f@collect :: Functor f => (a -> gb) -> fa -> g (fb)
collectf = distribute.fmapf-- | The dual of 'Data.Traversable.sequence'---- @'distributeM' = 'fmap' 'unwrapMonad' . 'distribute' . 'WrapMonad'@distributeM :: Monad m =>m (ga) -> g (ma)
distributeM = fmapunwrapMonad.distribute.WrapMonad-- |-- @'collectM' = 'distributeM' . 'liftM' f@collectM :: Monad m => (a -> gb) -> ma -> g (mb)
collectMf = distributeM.liftMf-- | The dual of 'Data.Traversable.traverse'---- @'cotraverse' f = 'fmap' f . 'distribute'@cotraverse :: (Functor f, Distributive g) => (fa -> b) -> f (ga) -> gbcotraversef = fmapf.distribute-- | The dual of 'Data.Traversable.mapM'---- @'comapM' f = 'fmap' f . 'distributeM'@comapM :: (Monad m, Distributive g) => (ma -> b) -> m (ga) -> gbcomapMf = fmapf.distributeM
instance DistributiveIdentity where
collectf = Identity.fmap (runIdentity.f)
distribute = Identity.fmaprunIdentity
instance DistributiveProxy where
collect _ _ = Proxydistribute _ = Proxy
instance Distributive (Taggedt) where
collectf = Tagged.fmap (unTagged.f)
distribute = Tagged.fmapunTagged
instance Distributive ((->)e) where
distributeae = fmap ($e) a
instance Distributive g =>Distributive (ReaderTeg) where
distributea = ReaderT$ \e -> collect (fliprunReaderTe) a
instance Distributive g =>Distributive (IdentityTg) where
collectf = IdentityT.collect (runIdentityT.f)
instance (Distributive f, Distributive g) =>Distributive (Composefg) where
distribute = Compose.fmapdistribute.collectgetCompose
instance (Distributive f, Distributive g) =>Distributive (Productfg) where
distributewp = Pair (collectfstPwp) (collectsndPwp) where
fstP (Paira _) = asndP (Pair _ b) = b
instance Distributive f =>Distributive (Backwardsf) where
distribute = Backwards.collectforwards
instance Distributive f =>Distributive (Reversef) where
distribute = Reverse.collectgetReverse