{-| Module : DeepControl.Commutative Description : --- Copyright : Conor McBride and Ross Paterson 2005, (c) 2015 KONISHI Yohsuke License : BSD-style (see the LICENSE file in the distribution) Maintainer : ocean0yohsuke@gmail.com Stability : experimental Portability : --- This module is made of @'Data.Traversable'@, distilling most function names polluted with action kind of concepts into crystalized(static) ones. -} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} module DeepControl.Commutative ( -- * The 'Commutative' class Commutative(..), -- * Utility functions cmap, cfor, -- * General definitions for superclass methods fmapDefault, foldMapDefault, -- * Utility functions 2 -- ** Level-2 sink2, float2, -- ** Level-3 sink3, float3, -- ** Level-4 sink4, float4, -- ** Level-5 sink5, float5, ) where import DeepControl.Applicative import Data.Monoid import Control.Monad.Identity (Identity(..)) import Control.Monad.Except (Except, ExceptT(..), runExcept) import Control.Monad.Writer (Writer, WriterT(..), runWriter) ------------------------------------------------------------------------------ -- Level-1 -- | [], Maybe, Either, Except and Writer are all commutative each other. -- So these monads can be deepened to Monad2, Monad3, Monad4 and Monad5. -- class (Functor c) => Commutative c where -- | This method is equivalent for @'Data.Traversable.sequenceA'@ except the name. -- The only difference is the name "commute", that is to say from which no action kind of concepts smell. -- -- >>> commute $ Just [1] -- [Just 1] -- >>> commute $ [Just 1] -- Just [1] -- -- >>> commute $ Right (Just 1) -- Just (Right 1) -- >>> commute $ Just (Right 1) -- Right (Just 1) -- commute :: Applicative f => c (f a) -> f (c a) -- | Do @fmap f@ then commute, equivalent for @'Data.Traversable.traverse'@. cmap :: (Applicative f, Commutative c) => (a -> f b) -> c a -> f (c b) cmap f = commute . (f |$>) -- | The auguments-flipped function for @'cmap'@, equivalent for @'Data.Traversable.for'@. cfor :: (Applicative f, Commutative c) => c a -> (a -> f b) -> f (c b) cfor = flip cmap instance Commutative Maybe where commute (Just fa) = Just |$> fa commute Nothing = (*:) Nothing instance Commutative [] where commute = foldr (\x acc -> x <$|(:)|*> acc) ((*:) []) instance (Monoid w) => Commutative (Writer w) where commute x = let (a, b) = runWriter x in (WriterT . Identity) |$> (a <$|(,)|* b) instance Commutative (Either a) where commute (Right x) = Right |$> x commute (Left x) = (*:) $ Left x instance Commutative (Except e) where commute x = ExceptT . Identity |$> commute (runExcept x) instance Commutative (Const m) where commute (Const m) = (*:) $ Const m {- instance Commutative ((->) r) where -- TODO: If GHC could parse this expression, maybe I could write up DeepControl.Monad. commute ((r->) mv) = (r->) |$> mv -} -- | This function may be used as a value for `fmap` in a `Functor` -- instance, provided that 'commute' is defined. (Using -- `fmapDefault` with a `Commutative` instance will result in infinite recursion.) fmapDefault :: Commutative t => (a -> b) -> t a -> t b fmapDefault f = getId . cmap (Id . f) -- | This function may be used as a value for `Data.Foldable.foldMap` -- in a `Foldable` instance. foldMapDefault :: (Commutative t, Monoid m) => (a -> m) -> t a -> m foldMapDefault f = getConst . cmap (Const . f) -- local instances newtype Id a = Id { getId :: a } instance Functor Id where fmap f (Id x) = Id (f x) instance Applicative Id where pure = Id Id f <*> Id x = Id (f x) ------------------------------------------------------------------------------ -- Level-2 -- | sink2 = (commute|$>) . commute -- -- >>> sink2 $ Right (Just [1]) -- Just [Right 1] -- sink2 :: (Commutative m1, Applicative m2, Applicative m3) => m1 (m2 (m3 a)) -> m2 (m3 (m1 a)) sink2 = (commute|$>) . commute -- | float2 = commute . (commute|$>) -- -- >>> float2 $ Just [Right 1] -- Right (Just [1]) -- float2 :: (Applicative m1, Commutative m2, Commutative m3) => m2 (m3 (m1 a)) -> m1 (m2 (m3 a)) float2 = commute . (commute|$>) ------------------------------------------------------------------------------ -- Level-3 -- | sink3 = (sink2|$>) . commute -- -- >>> sink3 $ Right [Just [1]] -- [Just [Right 1]] -- sink3 :: (Commutative m1, Applicative m2, Applicative m3, Applicative m4) => m1 (m2 (m3 (m4 a))) -> m2 (m3 (m4 (m1 a))) sink3 = (sink2|$>) . commute -- | float3 = commute . (float2|$>) -- -- >>> float3 $ [Just [Right 1]] -- Right [Just [1]] -- float3 :: (Applicative m1, Commutative m2, Commutative m3, Commutative m4) => m2 (m3 (m4 (m1 a))) -> m1 (m2 (m3 (m4 a))) float3 = commute . (float2|$>) ------------------------------------------------------------------------------ -- Level-4 sink4 :: (Commutative m1, Applicative m2, Applicative m3, Applicative m4, Applicative m5) => m1 (m2 (m3 (m4 (m5 a)))) -> m2 (m3 (m4 (m5 (m1 a)))) sink4 = (sink3|$>) . commute float4 :: (Applicative m1, Commutative m2, Commutative m3, Commutative m4, Commutative m5) => m2 (m3 (m4 (m5 (m1 a)))) -> m1 (m2 (m3 (m4 (m5 a)))) float4 = commute . (float3|$>) ------------------------------------------------------------------------------ -- Level-5 sink5 :: (Commutative m1, Applicative m2, Applicative m3, Applicative m4, Applicative m5, Applicative m6) => m1 (m2 (m3 (m4 (m5 (m6 a))))) -> m2 (m3 (m4 (m5 (m6 (m1 a))))) sink5 = (sink4|$>) . commute float5 :: (Applicative m1, Commutative m2, Commutative m3, Commutative m4, Commutative m5, Commutative m6) => m2 (m3 (m4 (m5 (m6 (m1 a))))) -> m1 (m2 (m3 (m4 (m5 (m6 a))))) float5 = commute . (float4|$>)