packages feed

objective 0.6.1 → 0.6.2

raw patch · 9 files changed

+300/−71 lines, 9 filesdep +freedep +hashabledep +kan-extensionsdep ~elevator

Dependencies added: free, hashable, kan-extensions, unordered-containers, witherable

Dependency ranges changed: elevator

Files

.travis.yml view
@@ -1,1 +1,10 @@-language: haskell+language: haskell
+
+env:
+  - GHCVER=7.8.3
+
+before_install:
+  - sudo add-apt-repository -y ppa:hvr/ghc
+  - sudo apt-get update
+  - sudo apt-get install -y -qq cabal-install-1.20 ghc-$GHCVER
+  - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/1.20/bin:$PATH
CHANGELOG.md view
@@ -1,7 +1,34 @@+0.6.2
+----
+* Added `announce`, `announceMaybe` and `announceMaybeT` which invoke a method for every objects in a container.
+* Added `(@**@)` and `(@||@)`.
+* Renamed `(.>>.)` to `(@>>@)`, `.|>.` to `(@|>@)` for consistency.
+* Added `filterPush`.
+* Added `iterObject`, `iterTObject`, `iterative`, `iterativeT` for free monads.
+* Renamed `runSequential` to `(@!)`.
+* Added combinators for `ReifiedProgramT`: `(@!!)` and `sequentialT`.
+* Changed the semantics of `variable` to accept `StateT`.
+* Added `flyweight'` that relies on HashMap
+* Added `MonadObjective` constraint
+
+0.6.1
+-----
+* Fixed the wrong constraints of `request`
+
+0.6
+-----
+* `PushPull` has more `Floors`
+* Added `Applicative` instance for `Request`
+* Reformed around Control.Monad.Objective
+  * Instance f g m ==> Inst m f g
+  * `invoke` takes two transformations for lifting
+  * Added lifted versions of `new`: `newIO` and `newST`
+
 0.5.2
 -----
 * Added Process
 * Added `runSequential`, `sequential` for operational monad
+* Added `flyweight`
 
 0.5.1
 -----
@@ -18,4 +45,4 @@ -----
 * Added Request functor along with Lift
 * Supported "extensible" objects using open unions
-* AccessT is now obsolete+* AccessT is now obsolete
objective.cabal view
@@ -1,5 +1,5 @@ name:                objective
-version:             0.6.1
+version:             0.6.2
 synopsis:            Extensible objects
 description:         Stateful effect transducer
 homepage:            https://github.com/fumieval/objective
@@ -23,9 +23,9 @@                      , Control.Monad.Objective.ST
                      , Data.Functor.Request
                      , Data.Functor.PushPull
-  -- other-modules:       
+  -- other-modules:
   other-extensions:    MultiParamTypeClasses, KindSignatures, TypeFamilies
-  build-depends:       base >=4.5 && <5, transformers >= 0.3 && <0.5, clean-unions < 0.2, elevator, containers, minioperational >= 0.4 && <0.5, profunctors >= 4.0 && <5
+  build-depends:       base >=4.5 && <5, transformers >= 0.3 && <0.5, clean-unions < 0.2, elevator >= 0.1.2, containers, minioperational >= 0.4 && <0.5, profunctors >= 4.0 && <5, witherable <= 0.2, free >= 4.4 && <5, kan-extensions >= 4.1, unordered-containers, hashable >= 1.2 && <1.4
   ghc-options: -Wall
   hs-source-dirs:      src
   default-language:    Haskell2010
src/Control/Monad/Objective/Class.hs view
@@ -22,33 +22,34 @@ -----------------------------------------------------------------------------
 module Control.Monad.Objective.Class where
 import Control.Object
-
 import Control.Elevator
 import Control.Monad.Trans.State.Strict
+import Control.Monad.Operational.Mini
+import Control.Monad
 
 type Inst' f g = Inst g f g
-type Instance' f g = Inst g f g
-type Instance f g m = Inst m f g
 
-class ObjectiveBase b where
+class Monad b => ObjectiveBase b where
   data Inst b (f :: * -> *) (g :: * -> *)
   new :: Object f g -> b (Inst b f g)
-  invoke :: Monad r => (forall x. b x -> r x) -> (forall x. g x -> r x) -> Inst b f g -> f a -> r a
+  invoke :: Monad m => (forall x. b x -> m x) -> (forall x. g x -> m x) -> Inst b f g -> f a -> m a
 
-(.-) :: (ObjectiveBase b, Elevate b m, Elevate g m, Monad m) => Inst b f g -> f a -> m a
+type MonadObjective b m = (ObjectiveBase b, Elevate b m, Monad m)
+
+(.-) :: (MonadObjective b m, Elevate g m) => Inst b f g -> f a -> m a
 (.-) = invoke elevate elevate
 {-# INLINE (.-) #-}
 
 infix 3 .-
 
 -- | Invoke a method.
-(.^) :: (ObjectiveBase b, Elevate b m, Elevate g m, Monad m, Elevate e f) => Inst b f g -> e a -> m a
+(.^) :: (MonadObjective b m, Elevate g m, Elevate e f) => Inst b f g -> e a -> m a
 i .^ e = i .- elevate e
 {-# INLINE (.^) #-}
 infix 3 .^
 
--- | Specialized (.^) for StateT
-(.&) :: (ObjectiveBase b, Elevate b m, Elevate g m, Monad m, Elevate (State s) f) => Inst b f g -> StateT s m a -> m a
+-- | (.^) for StateT
+(.&) :: (MonadObjective b m, Elevate g m, Elevate (State s) f) => Inst b f g -> StateT s m a -> m a
 i .& m = do
   s <- i .^ get
   (a, s') <- runStateT m s
@@ -56,3 +57,13 @@   return a
 
 infix 3 .&
+
+(.!) :: (MonadObjective b m, Elevate g m) => Inst b f g -> Program f a -> m a
+(.!) i = interpret (i.-)
+
+infix 3 .!
+
+-- | We can convert method invocation into an object trivially.
+-- @invocation i = liftO (i.-)@
+invocation :: (MonadObjective b m, Elevate g m) => Inst b f g -> Object f m
+invocation i = Object $ liftM (\a -> (a, invocation i)). (i.-)
src/Control/Monad/Objective/IO.hs view
@@ -31,4 +31,4 @@   new v = InstIO `fmap` newMVar v
 
 newIO :: MonadIO m => Object f g -> m (Inst IO f g)
-newIO = liftIO . new+newIO = liftIO . new
src/Control/Monad/Objective/ST.hs view
@@ -33,4 +33,4 @@   new o = InstST `fmap` newSTRef o
 
 newST :: Elevate (ST s) m => Object f g -> m (Inst (ST s) f g)
-newST = elevate . new+newST = elevate . new
src/Control/Object.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE Rank2Types, FlexibleInstances, FlexibleContexts, TypeOperators, CPP #-}
+{-# LANGUAGE Rank2Types, FlexibleInstances, FlexibleContexts, TypeOperators, CPP, ConstraintKinds #-}
 #if __GLASGOW_HASKELL__ >= 707
 {-# LANGUAGE DeriveDataTypeable #-}
 #endif
@@ -23,48 +23,82 @@   oneshot,
   stateful,
   variable,
+  unfoldO,
+  unfoldOM,
+  foldP,
+  foldP',
+  sharing,
   -- * Composition
-  (.>>.),
+  (@>>@),
+  (@>>^),
+  (^>>@),
+  (@**@),
+  (@||@),
+  loner,
+  (@|>@),
   transObject,
   adaptObject,
+  -- * Monads
+  (@!),
+  (@!!),
   sequential,
-  runSequential,
-  -- * Multifunctional objects
-  loner,
-  (.|>.),
-  sharing,
+  sequentialT,
+  iterObject,
+  iterTObject,
+  iterative,
+  iterativeT,
   -- * Patterns
   flyweight,
+  flyweight',
+  announce,
+  announceMaybe,
+  announceMaybeT,
   Process(..),
-  _Process
+  _Process,
+  -- * Deprecated
+  runSequential
   )
 where
 
-import Control.Monad.Trans.State.Strict
-import Control.Monad
-import Data.Typeable
 import Control.Applicative
-import Data.OpenUnion1.Clean
-import qualified Data.Map as Map
-import Data.Functor.Request
-import Control.Monad.Operational.Mini
 import Control.Arrow
-import qualified Control.Category as C
-import Data.Profunctor
+import Control.Monad
+import Control.Monad.Operational.Mini
+import Control.Monad.Trans.Maybe
+import Control.Monad.Trans.State.Strict
+import Control.Monad.Trans.Writer.Strict
+import Control.Monad.Trans.Class
+import Control.Elevator
+import Data.Functor.Request
+import Data.Functor.PushPull
 import Data.Monoid
+import Data.OpenUnion1.Clean
+import Data.Profunctor
+import Data.Typeable
+import Data.Witherable
+import qualified Control.Category as C
+import qualified Control.Monad.Trans.Operational.Mini as T
+import qualified Control.Monad.Trans.Free as T
+import qualified Data.Map.Strict as Map
+import qualified Data.Traversable as T
+import Control.Monad.Free
+import qualified Data.HashMap.Strict as HM
+import Data.Functor.Day
+import Data.Functor.Sum as F
+import Data.Hashable
 
--- | The type 'Object e m' represents objects which can handle messages @e@, perform actions in the environment @m@.
+-- | The type 'Object f g' represents objects which can handle messages @f@, perform actions in the environment @g@.
 -- It can be thought of as an automaton that converts effects.
--- 'Object's can be composed just like functions using '.>>.'; the identity element is 'echo'.
-newtype Object e m = Object { runObject :: forall x. e x -> m (x, Object e m) }
+-- 'Object's can be composed just like functions using '@>>@'; the identity element is 'echo'.
+newtype Object f g = Object { runObject :: forall x. f x -> g (x, Object f g) }
 #if __GLASGOW_HASKELL__ >= 707
   deriving (Typeable)
 #else
-instance (Typeable1 f, Typeable1 m) => Typeable (Object f m) where
+instance (Typeable1 f, Typeable1 g) => Typeable (Object f g) where
   typeOf t = mkTyConApp objectTyCon [typeOf1 (f t), typeOf1 (g t)] where
-    f :: Object f m -> f a
+    f :: Object f g -> f a
     f = undefined
-    g :: Object f m -> m a
+    g :: Object f g -> g a
     g = undefined
 
 objectTyCon :: TyCon
@@ -76,46 +110,79 @@ {-# NOINLINE objectTyCon #-}
 #endif
 
+-- | The identity object
+echo :: Functor f => Object f f
+echo = Object (fmap (\x -> (x, echo)))
+
+-- | Object-object composition
+(@>>@) :: Functor h => Object f g -> Object g h -> Object f h
+Object m @>>@ Object n = Object $ \e -> fmap (\((x, m'), n') -> (x, m' @>>@ n')) $ n (m e)
+infixr 1 @>>@
+
+-- | Object-function composition
+(@>>^) :: Functor h => Object f g -> (forall x. g x -> h x) -> Object f h
+m0 @>>^ g = go m0 where go (Object m) = Object $ fmap (fmap go) . g . m
+infixr 1 @>>^
+
+-- | Function-object composition
+(^>>@) :: Functor h => (forall x. f x -> g x) -> Object g h -> Object f h
+f ^>>@ m0 = go m0 where go (Object m) = Object $ fmap (fmap go) . m . f
+infixr 1 ^>>@
+
+(@**@) :: Applicative m => Object f m -> Object g m -> Object (Day f g) m
+a @**@ b = Object $ \(Day f g r) -> (\(x, a') (y, b') -> (r x y, a' @**@ b')) <$> runObject a f <*> runObject b g
+infixr 3 @**@
+
+(@||@) :: Functor m => Object f m -> Object g m -> Object (F.Sum f g) m
+a @||@ b = Object $ \r -> case r of
+  InL f -> fmap (fmap (@||@b)) (runObject a f)
+  InR g -> fmap (fmap (a@||@)) (runObject b g)
+infixr 2 @||@
+
 -- | Lift a natural transformation into an object.
-liftO :: Functor f => (forall x. e x -> f x) -> Object e f
-liftO f = Object $ fmap (\x -> (x, liftO f)) . f
+liftO :: Functor g => (forall x. f x -> g x) -> Object f g
+liftO f = go where go = Object $ fmap (\x -> (x, go)) . f
+{-# INLINE liftO #-}
 
 -- | Change the workspace of the object.
 transObject :: Functor g => (forall x. f x -> g x) -> Object e f -> Object e g
-transObject f (Object m) = Object $ fmap (fmap (transObject f)) . f . m
+transObject f = (@>>^f)
 
 -- | Apply a function to the messages coming into the object.
-adaptObject :: Functor m => (forall x. e x -> f x) -> Object f m -> Object e m
-adaptObject f (Object m) = Object $ fmap (fmap (adaptObject f)) . m . f
-
--- | Parrots messages given.
-echo :: Functor e => Object e e
-echo = Object (fmap (\x -> (x, echo)))
-
--- | Compose two objects (aka Dependency Injection).
-(.>>.) :: Functor n => Object e m -> Object m n -> Object e n
-Object m .>>. Object n = Object $ \e -> fmap (\((x, m'), n') -> (x, m' .>>. n')) $ n (m e)
-
-infixr 4 .>>.
+adaptObject :: Functor m => (forall x. g x -> f x) -> Object f m -> Object g m
+adaptObject f = (f^>>@)
 
 -- | Build an object using continuation passing style.
-oneshot :: (Functor e, Monad m) => (forall a. e (m a) -> m a) -> Object e m
+oneshot :: (Functor f, Monad m) => (forall a. f (m a) -> m a) -> Object f m
 oneshot m = go where
   go = Object $ \e -> m (fmap return e) >>= \a -> return (a, go)
 {-# INLINE oneshot #-}
 
 -- | Build a stateful object.
-stateful :: Monad m => (forall a. e a -> StateT s m a) -> s -> Object e m
+-- @stateful t s = t ^>>@ variable s@
+stateful :: Monad m => (forall a. f a -> StateT s m a) -> s -> Object f m
 stateful h = go where
   go s = Object $ liftM (\(a, s') -> (a, go s')) . flip runStateT s . h
 {-# INLINE stateful #-}
 
+-- | The unwrapped analog of 'stateful'
+--     @unfoldO runObject = id@
+--     @unfoldO runSequential = sequential@
+--     @unfoldO iterObject = iterable@
+unfoldO :: Functor g => (forall a. r -> f a -> g (a, r)) -> r -> Object f g
+unfoldO h = go where go r = Object $ fmap (fmap go) . h r
+{-# INLINE unfoldO #-}
+
+unfoldOM :: Monad m => (forall a. r -> f a -> m (a, r)) -> r -> Object f m
+unfoldOM h = go where go r = Object $ liftM (fmap go) . h r
+{-# INLINE unfoldOM #-}
+
 -- | A mutable variable.
-variable :: Applicative f => s -> Object (State s) f
-variable s = Object $ \m -> let (a, s') = runState m s in pure (a, variable s')
+variable :: Monad m => s -> Object (StateT s m) m
+variable s = Object $ \m -> liftM (fmap variable) $ runStateT m s
 
 -- | Build a stateful object, sharing out the state.
-sharing :: Monad m => (forall a. e a -> StateT s m a) -> s -> Object (State s |> e |> Nil) m
+sharing :: Monad m => (forall a. f a -> StateT s m a) -> s -> Object (State s |> f |> Nil) m
 sharing m = go where
   go s = Object $ \k -> liftM (fmap go) $ ($k)
     $ (\n -> return $ runState n s)
@@ -124,30 +191,101 @@ {-# INLINE sharing #-}
 
 -- | An object that won't accept any messages.
-loner :: Functor m => Object Nil m
+loner :: Functor f => Object Nil f
 loner = liftO exhaust
 
 -- | Extend an object by adding another independent object.
-(.|>.) :: Functor m => Object f m -> Object (Union s) m -> Object (f |> Union s) m
-p .|>. q = Object $ fmap (fmap (.|>.q)) . runObject p ||> fmap (fmap (p .|>.)) . runObject q
-
-infixr 3 .|>.
+(@|>@) :: Functor g => Object f g -> Object (Union s) g -> Object (f |> Union s) g
+p @|>@ q = Object $ fmap (fmap (@|>@q)) . runObject p ||> fmap (fmap (p @|>@)) . runObject q
+infixr 3 @|>@
 
 -- | The flyweight pattern.
-flyweight :: Monad m => Ord k => (k -> m a) -> Object (Request k a) m
+flyweight :: (Monad m, Ord k) => (k -> m a) -> Object (Request k a) m
 flyweight f = go Map.empty where
   go m = Object $ \(Request k cont) -> case Map.lookup k m of
     Just a -> return (cont a, go m)
     Nothing -> f k >>= \a -> return (cont a, go $ Map.insert k a m)
 
+-- | Like 'flyweight', but it uses 'Data.HashMap.Strict' internally.
+flyweight' :: (Monad m, Eq k, Hashable k) => (k -> m a) -> Object (Request k a) m
+flyweight' f = go HM.empty where
+  go m = Object $ \(Request k cont) -> case HM.lookup k m of
+    Just a -> return (cont a, go m)
+    Nothing -> f k >>= \a -> return (cont a, go $ HM.insert k a m)
+
+(@!) :: Monad m => Object e m -> ReifiedProgram e a -> m (a, Object e m)
+obj @! Return a = return (a, obj)
+obj @! (e :>>= cont) = runObject obj e >>= \(a, obj') -> obj' @! cont a
+
+(@!!) :: Monad m => Object e m -> T.ReifiedProgramT e m a -> m (a, Object e m)
+obj @!! T.Return a = return (a, obj)
+obj @!! T.Lift m cont = m >>= (obj @!!) . cont
+obj @!! (e T.:>>= cont) = runObject obj e >>= \(a, obj') -> obj' @!! cont a
+
 runSequential :: Monad m => Object e m -> ReifiedProgram e a -> m (a, Object e m)
-runSequential obj (Return a) = return (a, obj)
-runSequential obj (e :>>= cont) = runObject obj e >>= \(a, obj') -> runSequential obj' (cont a)
+runSequential = (@!)
+{-# DEPRECATED runSequential "use (@!!) instead" #-}
 
+iterObject :: Monad m => Object f m -> Free f a -> m (a, Object f m)
+iterObject obj (Pure a) = return (a, obj)
+iterObject obj (Free f) = runObject obj f >>= \(cont, obj') -> iterObject obj' cont
+
+iterTObject :: Monad m => Object f m -> T.FreeT f m a -> m (a, Object f m)
+iterTObject obj m = T.runFreeT m >>= \r -> case r of
+  T.Pure a -> return (a, obj)
+  T.Free f -> runObject obj f >>= \(cont, obj') -> iterTObject obj' cont
+
 -- | Let object handle sequential methods.
 sequential :: Monad m => Object e m -> Object (ReifiedProgram e) m
-sequential obj = Object $ liftM (fmap sequential) . runSequential obj
+sequential = unfoldOM (@!)
 
+-- | Let object handle sequential methods.
+sequentialT :: Monad m => Object e m -> Object (T.ReifiedProgramT e m) m
+sequentialT = unfoldOM (@!!)
+
+iterative :: Monad m => Object f m -> Object (Free f) m
+iterative = unfoldOM iterObject
+
+iterativeT :: Monad m => Object f m -> Object (T.FreeT f m) m
+iterativeT = unfoldOM iterTObject
+
+foldP :: Applicative f => (a -> r -> f r) -> r -> Object (PushPull a r) f
+foldP f = go where
+  go r = Object $ \pp -> case pp of
+    Push a c -> fmap (\z -> (c, z `seq` go z)) (f a r)
+    Pull cont -> pure (cont r, go r)
+{-# INLINE foldP #-}
+
+foldP' :: Applicative f => (a -> r -> r) -> r -> Object (PushPull a r) f
+foldP' f = go where
+  go r = Object $ \pp -> case pp of
+    Push a c -> let z = f a r in pure (c, z `seq` go z)
+    Pull cont -> pure (cont r, go r)
+{-# INLINE foldP' #-}
+
+announce :: (T.Traversable t, Monad m, Elevate (State (t (Object f g))) m, Elevate g m) => f a -> m [a]
+announce f = do
+  t <- elevate get
+  (t', Endo e) <- runWriterT $ T.mapM (\obj -> (lift . elevate) (runObject obj f)
+      >>= \(x, obj') -> writer (obj', Endo (x:))) t
+  elevate (put t')
+  return (e [])
+
+announceMaybe :: (Witherable t, Monad m, Elevate (State (t (Object f Maybe))) m) => f a -> m [a]
+announceMaybe f = elevate $ state
+  $ \t -> let (t', Endo e) = runWriter
+                $ witherM (\obj -> case runObject obj f of
+                  Just (x, obj') -> lift $ writer (obj', Endo (x:))
+                  Nothing -> mzero) t in (e [], t')
+
+announceMaybeT :: (Witherable t, Monad m, State (t (Object f (MaybeT g))) ∈ Floors1 m, g ∈ Floors1 m, Tower m) => f a -> m [a]
+announceMaybeT f = do
+  t <- elevate get
+  (t', Endo e) <- runWriterT $ witherM (\obj -> mapMaybeT (lift . elevate) (runObject obj f)
+      >>= \(x, obj') -> lift (writer (obj', Endo (x:)))) t
+  elevate (put t')
+  return (e [])
+
 -- | An object which is specialized to be a Mealy machine
 newtype Process m a b = Process { unProcess :: Object (Request a b) m }
 
@@ -182,12 +320,18 @@     go = Object $ \(Request a cont) -> return (cont (f a), go)
   first (Process f0) = Process $ go f0 where
     go f = Object $ \(Request (a, c) cont) -> liftM (\(b, f') -> (cont (b, c), go f')) $ runObject f (Request a id)
+  second (Process f0) = Process $ go f0 where
+    go f = Object $ \(Request (a, c) cont) -> liftM (\(d, f') -> (cont (a, d), go f')) $ runObject f (Request c id)
 
 instance Monad m => ArrowChoice (Process m) where
   left (Process f0) = Process $ go f0 where
     go f = Object $ \(Request e cont) -> case e of
       Left a -> liftM (\(b, f') -> (cont (Left b), go f')) $ runObject f (Request a id)
       Right c -> return (cont (Right c), go f)
+  right (Process f0) = Process $ go f0 where
+    go f = Object $ \(Request e cont) -> case e of
+      Right a -> liftM (\(b, f') -> (cont (Right b), go f')) $ runObject f (Request a id)
+      Left c -> return (cont (Left c), go f)
 
 instance Monad m => Profunctor (Process m) where
   dimap f g (Process f0) = Process (go f0) where
@@ -203,7 +347,7 @@ instance Monad m => Choice (Process m) where
   left' = left
   {-# INLINE left' #-}
-  right' = right 
+  right' = right
   {-# INLINE right' #-}
 
 instance (Applicative m, Num o) => Num (Process m i o) where
src/Data/Functor/PushPull.hs view
@@ -14,16 +14,46 @@ import Data.Typeable
 import Control.Elevator
 import Data.OpenUnion1.Clean
+import Control.Applicative
+import Data.Profunctor
+import Data.Functor.Day
 
 -- | The type for asynchronous input/output.
 data PushPull a b r = Push a r | Pull (b -> r) deriving (Functor, Typeable)
 
+type PushPull' a = PushPull a a
+
+instance Profunctor (PushPull a) where
+  dimap _ g (Push a r) = Push a (g r)
+  dimap f g (Pull br) = Pull (dimap f g br)
+
 instance Tower (PushPull a b) where
   type Floors (PushPull a b) = (,) a :> (->) b :> Empty
   toLoft = uncurry Push ||> Pull ||> exhaust
 
+mapPush :: (a -> a') -> PushPull a b r -> PushPull a' b r
+mapPush f (Push a r) = Push (f a) r
+mapPush _ (Pull br) = Pull br
+
 push :: (Elevate (PushPull a b) f) => a -> f ()
 push a = elevate (Push a ())
 
 pull :: (Elevate (PushPull a b) f) => f b
-pull = elevate (Pull id)+pull = elevate (Pull id)
+
+bipush :: (i -> (a, c)) -> (b -> d -> o) -> PushPull i o r -> Day (PushPull a b) (PushPull c d) r
+bipush f g = go where
+  go (Pull r) = Day (Pull id) (Pull id) (fmap r . g)
+  go (Push i r) = let (a, b) = f i in Day (Push a ()) (Push b ()) (\_ _ -> r)
+{-# INLINE bipush #-}
+
+bipull :: (a -> b -> c) -> PushPull i c r -> Day (PushPull i a) (PushPull i b) r
+bipull = bipush (\x -> (x, x))
+{-# INLINE bipull #-}
+
+-- | @filterPush :: (a -> Bool) -> PushPull a b r -> Program (PushPull a b) r@
+filterPush :: (Applicative f, Elevate (PushPull a b) f) => (a -> Bool) -> PushPull a b r -> f r
+filterPush p e@(Push a r)
+  | p a = elevate e
+  | otherwise = pure r
+filterPush _ e = elevate e
src/Data/Functor/Request.hs view
@@ -17,10 +17,18 @@ import Data.Monoid
 import Control.Applicative
 import Data.OpenUnion1.Clean
-import Data.Functor.Identity
+import Data.Profunctor
 
 -- | 'Request a b' is the type of a request that sends @a@ to receive @b@.
 data Request a b r = Request a (b -> r) deriving (Functor, Typeable)
+
+mapRequest :: (a -> a') -> Request a b r -> Request a' b r
+mapRequest f (Request a br) = Request (f a) br
+{-# INLINE mapRequest #-}
+
+instance Profunctor (Request a) where
+  dimap f g (Request a br) = Request a (dimap f g br)
+  {-# INLINE dimap #-}
 
 instance Monoid a => Applicative (Request a b) where
   pure a = Request mempty (const a)