packages feed

objective 1.1 → 1.1.1

raw patch · 7 files changed

+708/−693 lines, 7 filesdep ~either

Dependency ranges changed: either

Files

CHANGELOG.md view
@@ -1,114 +1,114 @@-1.1------* Removed `HProfunctor`, `(..-)`, `invokeOnSTM`, `newSTM`, `snapshot`-* Added `(?-)`--1.0.5------* Added `filterO` and `filteredO`-* Renamed `announcesOf` to `invokesOf`-* Added `invokes`-* Added `(@!=)`-* Shaved unnecessary `Monoid r` off in `apprisesOf`--1.0.4------* Simplified `Instance`-* Added `snapshot`-* Added `cascade`, `cascadeObject`-* Added `announcesOf`-* Added `(@||@)`-* Safe Haskell--1.0.3------* Added `apprisesOf`-* Added `(@~)`--1.0.2------* Made `(.-)` exception-safe--1.0.1------* Switched to use TMVar for instances-* Added atomic operations-* Add `apprises`-* Re-added `accept`-* Added `(>~~>)`-* Added `accumulator`--1.0------* No longer support `extensible`, `elevator`, and `minioperational`-* Removed `Data.Functor.PushPull`-* Removed `Control.Object.Process`-* Removed `Control.Object.Stream`-* Removed `Control.Monad.Objective`-* Added `apprise`-* Generalized `(^>>@)` and `(@>>^)` so that they also work on instances--0.6.5------* Supported `elevator >= 0.2`.--0.6.3.1------* Reverted the fixity.--0.6.3------* Added `Variable`, an alias for `variable` objects.-* Added `Mortal`.-* Increased fixity of invocation operators to 5.-* Added stream connection operators.-* Added `transit` and `animate`.--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`, `bipush`, `bipull`.-* 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-------* Added PushPull functor-* Removed `sequential`--0.5-------* Lift has gone-* Use elevator instead of Lift-* Moved Request to a separate module--0.4-------* Added Request functor along with Lift-* Supported "extensible" objects using open unions-* AccessT is now obsolete+1.1
+----
+* Removed `HProfunctor`, `(..-)`, `invokeOnSTM`, `newSTM`, `snapshot`
+* Added `(?-)`
+
+1.0.5
+----
+* Added `filterO` and `filteredO`
+* Renamed `announcesOf` to `invokesOf`
+* Added `invokes`
+* Added `(@!=)`
+* Shaved unnecessary `Monoid r` off in `apprisesOf`
+
+1.0.4
+----
+* Simplified `Instance`
+* Added `snapshot`
+* Added `cascade`, `cascadeObject`
+* Added `announcesOf`
+* Added `(@||@)`
+* Safe Haskell
+
+1.0.3
+----
+* Added `apprisesOf`
+* Added `(@~)`
+
+1.0.2
+----
+* Made `(.-)` exception-safe
+
+1.0.1
+----
+* Switched to use TMVar for instances
+* Added atomic operations
+* Add `apprises`
+* Re-added `accept`
+* Added `(>~~>)`
+* Added `accumulator`
+
+1.0
+----
+* No longer support `extensible`, `elevator`, and `minioperational`
+* Removed `Data.Functor.PushPull`
+* Removed `Control.Object.Process`
+* Removed `Control.Object.Stream`
+* Removed `Control.Monad.Objective`
+* Added `apprise`
+* Generalized `(^>>@)` and `(@>>^)` so that they also work on instances
+
+0.6.5
+----
+* Supported `elevator >= 0.2`.
+
+0.6.3.1
+----
+* Reverted the fixity.
+
+0.6.3
+----
+* Added `Variable`, an alias for `variable` objects.
+* Added `Mortal`.
+* Increased fixity of invocation operators to 5.
+* Added stream connection operators.
+* Added `transit` and `animate`.
+
+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`, `bipush`, `bipull`.
+* 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
+-----
+* Added PushPull functor
+* Removed `sequential`
+
+0.5
+-----
+* Lift has gone
+* Use elevator instead of Lift
+* Moved Request to a separate module
+
+0.4
+-----
+* Added Request functor along with Lift
+* Supported "extensible" objects using open unions
+* AccessT is now obsolete
objective.cabal view
@@ -1,48 +1,48 @@-name:                objective-version:             1.1-synopsis:            Composable objects-description:         Composable objects-homepage:            https://github.com/fumieval/objective-bug-reports:         http://github.com/fumieval/objective/issues-license:             BSD3-license-file:        LICENSE-author:              Fumiaki Kinoshita-maintainer:          Fumiaki Kinoshita <fumiexcel@gmail.com>-copyright:           Copyright (c) 2014 Fumiaki Kinoshita-category:            Control-build-type:          Simple-extra-source-files:-  CHANGELOG.md-  .travis.yml-cabal-version:       >=1.10--source-repository head-  type: git-  location: https://github.com/fumieval/objective.git--library-  exposed-modules:-    Control.Object-      , Control.Object.Object-      , Control.Object.Instance-      , Control.Object.Mortal-      , Data.Functor.Request-  other-extensions:    MultiParamTypeClasses, KindSignatures, TypeFamilies-  build-depends:       base >=4.6 && <5-    , either >= 4.3 && <4.5-    , exceptions >= 0.8-    , containers >= 0.5.0.0 && <0.6-    , unordered-containers >= 0.2.0.0 && <0.3-    , transformers >= 0.3 && <0.6-    , transformers-compat-    , free >= 4 && <5-    , hashable-    , mtl-    , profunctors-    , void-    , witherable >= 0.1.3-    , monad-skeleton >= 0.1.1 && <0.3-    , template-haskell-  ghc-options: -Wall-  hs-source-dirs:      src-  default-language:    Haskell2010+name:                objective
+version:             1.1.1
+synopsis:            Composable objects
+description:         Composable objects
+homepage:            https://github.com/fumieval/objective
+bug-reports:         http://github.com/fumieval/objective/issues
+license:             BSD3
+license-file:        LICENSE
+author:              Fumiaki Kinoshita
+maintainer:          Fumiaki Kinoshita <fumiexcel@gmail.com>
+copyright:           Copyright (c) 2014 Fumiaki Kinoshita
+category:            Control
+build-type:          Simple
+extra-source-files:
+  CHANGELOG.md
+  .travis.yml
+cabal-version:       >=1.10
+
+source-repository head
+  type: git
+  location: https://github.com/fumieval/objective.git
+
+library
+  exposed-modules:
+    Control.Object
+      , Control.Object.Object
+      , Control.Object.Instance
+      , Control.Object.Mortal
+      , Data.Functor.Request
+  other-extensions:    MultiParamTypeClasses, KindSignatures, TypeFamilies
+  build-depends:       base >=4.6 && <5
+    , either >= 4.3 && <4.5
+    , exceptions >= 0.8
+    , containers >= 0.5.0.0 && <0.6
+    , unordered-containers >= 0.2.0.0 && <0.3
+    , transformers >= 0.3 && <0.6
+    , transformers-compat
+    , free >= 4 && <5
+    , hashable
+    , mtl
+    , profunctors
+    , void
+    , witherable >= 0.1.3
+    , monad-skeleton >= 0.1.1 && <0.3
+    , template-haskell
+  ghc-options: -Wall
+  hs-source-dirs:      src
+  default-language:    Haskell2010
src/Control/Object.hs view
@@ -1,20 +1,20 @@--------------------------------------------------------------------------------- |--- Module      :  Control.Object--- Copyright   :  (c) Fumiaki Kinoshita 2015--- License     :  BSD3------ Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>--- Stability   :  experimental--- Portability :  non-portable----------------------------------------------------------------------------------module Control.Object-  ( module Control.Object.Object,-    module Control.Object.Mortal,-    module Control.Object.Instance,-  ) where--import Control.Object.Object-import Control.Object.Mortal+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Control.Object
+-- Copyright   :  (c) Fumiaki Kinoshita 2015
+-- License     :  BSD3
+--
+-- Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-----------------------------------------------------------------------------
+module Control.Object
+  ( module Control.Object.Object,
+    module Control.Object.Mortal,
+    module Control.Object.Instance,
+  ) where
+
+import Control.Object.Object
+import Control.Object.Mortal
 import Control.Object.Instance
src/Control/Object/Instance.hs view
@@ -1,64 +1,79 @@-{-# LANGUAGE GADTs, Rank2Types, LambdaCase #-}--------------------------------------------------------------------------------- |--- Module      :  Control.Object.Instance--- Copyright   :  (c) Fumiaki Kinoshita 2015--- License     :  BSD3------ Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>--- Stability   :  provisional--- Portability :  GADTs, Rank2Types----------------------------------------------------------------------------------module Control.Object.Instance (-  -- * Instantiation-  Instance-  , new-  , newSettle-  -- * Invocation-  , invokeOn-  , (.-)-  , (?-)-  ) where-import Control.Concurrent-import Control.Exception (evaluate)-import Control.Object.Object-import Control.Monad.IO.Class-import Control.Monad.Catch--type Instance f g = MVar (Object f g)---- | Invoke a method with an explicit landing function.--- In case of exception, the original object will be set.-invokeOn :: (MonadIO m, MonadMask m)-         => (forall x. g x -> m x) -> MVar (Object f g) -> f a -> m a-invokeOn m v f = mask $ \restore -> do-  obj <- liftIO $ takeMVar v-  (a, obj') <- restore (m (runObject obj f) >>= liftIO . evaluate) `onException` liftIO (putMVar v obj)-  liftIO $ putMVar v obj'-  return a---- | Invoke a method.-(.-) :: (MonadIO m, MonadMask m) => MVar (Object f m) -> f a -> m a-(.-) = invokeOn id-{-# INLINE (.-) #-}-infixr 3 .----- | Try to invoke a method. If the instance is unavailable, it returns Nothing.-(?-) :: (MonadIO m, MonadMask m) => MVar (Object f m) -> f a -> m (Maybe a)-v ?- f = mask $ \restore -> liftIO (tryTakeMVar v) >>= \case-  Just obj -> do-    (a, obj') <- restore (runObject obj f >>= liftIO . evaluate) `onException` liftIO (putMVar v obj)-    liftIO $ putMVar v obj'-    return (Just a)-  Nothing -> return Nothing---- | Create a new instance. This can be used inside 'unsafePerformIO' to create top-level instances.-new :: MonadIO m => Object f g -> m (Instance f g)-new = liftIO . newMVar-{-# INLINE new #-}---- | Create a new instance, having it sitting on the current environment.-newSettle :: MonadIO m => Object f m -> m (Instance f m)-newSettle = new-{-# INLINE newSettle #-}+{-# LANGUAGE GADTs, Rank2Types, LambdaCase #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Control.Object.Instance
+-- Copyright   :  (c) Fumiaki Kinoshita 2015
+-- License     :  BSD3
+--
+-- Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>
+-- Stability   :  provisional
+-- Portability :  GADTs, Rank2Types
+--
+-----------------------------------------------------------------------------
+module Control.Object.Instance (
+  -- * Instantiation
+  Instance
+  , new
+  , newSettle
+  -- * Invocation
+  , invokeOnUsing
+  , invokeOn
+  , (.-)
+  , (..-)
+  , (?-)
+  ) where
+import Control.Concurrent
+import Control.Exception (evaluate)
+import Control.Object.Object
+import Control.Monad.IO.Class
+import Control.Monad.Catch
+import Control.Monad.Skeleton
+
+type Instance f g = MVar (Object f g)
+
+invokeOnUsing :: (MonadIO m, MonadMask m)
+  => (Object f g -> t a -> g (a, Object f g))
+  -> (forall x. g x -> m x) -> MVar (Object f g) -> t a -> m a
+invokeOnUsing run m v f = mask $ \restore -> do
+  obj <- liftIO $ takeMVar v
+  (a, obj') <- restore (m (run obj f) >>= liftIO . evaluate) `onException` liftIO (putMVar v obj)
+  liftIO $ putMVar v obj'
+  return a
+
+-- | Invoke a method with an explicit landing function.
+-- In case of exception, the original object will be set.
+invokeOn :: (MonadIO m, MonadMask m)
+         => (forall x. g x -> m x) -> MVar (Object f g) -> f a -> m a
+invokeOn = invokeOnUsing runObject
+{-# INLINE invokeOn #-}
+
+-- | Invoke a method.
+(.-) :: (MonadIO m, MonadMask m) => MVar (Object f m) -> f a -> m a
+(.-) = invokeOn id
+{-# INLINE (.-) #-}
+infixr 3 .-
+
+(..-) :: (MonadIO m, MonadMask m)
+    => MVar (Object t m) -> Skeleton t a -> m a
+(..-) = invokeOnUsing cascadeObject id
+{-# INLINE (..-) #-}
+infixr 3 ..-
+
+-- | Try to invoke a method. If the instance is unavailable, it returns Nothing.
+(?-) :: (MonadIO m, MonadMask m) => MVar (Object f m) -> f a -> m (Maybe a)
+v ?- f = mask $ \restore -> liftIO (tryTakeMVar v) >>= \case
+  Just obj -> do
+    (a, obj') <- restore (runObject obj f >>= liftIO . evaluate) `onException` liftIO (putMVar v obj)
+    liftIO $ putMVar v obj'
+    return (Just a)
+  Nothing -> return Nothing
+
+-- | Create a new instance. This can be used inside 'unsafePerformIO' to create top-level instances.
+new :: MonadIO m => Object f g -> m (Instance f g)
+new = liftIO . newMVar
+{-# INLINE new #-}
+
+-- | Create a new instance, having it sitting on the current environment.
+newSettle :: MonadIO m => Object f m -> m (Instance f m)
+newSettle = new
+{-# INLINE newSettle #-}
src/Control/Object/Mortal.hs view
@@ -1,115 +1,115 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE Trustworthy #-}-{-# LANGUAGE Rank2Types #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE BangPatterns #-}--------------------------------------------------------------------------------- |--- Module      :  Control.Object.Mortal--- Copyright   :  (c) Fumiaki Kinoshita 2015--- License     :  BSD3------ Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>--- Stability   :  provisional--- Portability :  GADTs, Rank2Types----------------------------------------------------------------------------------module Control.Object.Mortal (-    Mortal(..),-    mortal,-    mortal_,-    runMortal,-    immortal,-    apprisesOf,-    apprises,-    apprise-    ) where--import Control.Object.Object-#if !MIN_VERSION_base(4,8,0)-import Control.Applicative-#endif-import Control.Monad.Trans.Either-import Control.Monad-import Control.Monad.Trans.Class-import Control.Monad.Trans.State.Strict-import Control.Monad.Trans.Writer.Strict-import Data.Monoid-import Data.Witherable-import Data.Tuple (swap)-import Control.Arrow ((***))-import Unsafe.Coerce---- | A 'Mortal' is an object that may die.--- A mortal yields a final result upon death.--- @'Mortal' f g@ forms a 'Monad':--- 'return' is a dead object and ('>>=') prolongs the life of the left object.------ @Object f g ≡ Mortal f g Void@----newtype Mortal f g a = Mortal { unMortal :: Object f (EitherT a g) }--instance (Functor m, Monad m) => Functor (Mortal f m) where-  fmap f (Mortal obj) = Mortal (obj @>>^ bimapEitherT f id)-  {-# INLINE fmap #-}--instance (Functor m, Monad m) => Applicative (Mortal f m) where-  pure = return-  {-# INLINE pure #-}-  (<*>) = ap-  {-# INLINE (<*>) #-}--instance Monad m => Monad (Mortal f m) where-  return a = mortal $ const $ left a-  {-# INLINE return #-}-  m >>= k = mortal $ \f -> lift (runEitherT $ runMortal m f) >>= \r -> case r of-    Left a -> runMortal (k a) f-    Right (x, m') -> return (x, m' >>= k)--instance MonadTrans (Mortal f) where-  lift m = mortal $ const $ EitherT $ liftM Left m-  {-# INLINE lift #-}---- | Construct a mortal in a 'Object' construction manner.-mortal :: Monad m => (forall x. f x -> EitherT a m (x, Mortal f m a)) -> Mortal f m a-mortal f = unsafeCoerce f `asTypeOf` Mortal (Object (fmap (fmap unMortal) . f))-{-# INLINE mortal #-}---- | Send a message to a mortal.-runMortal :: Monad m => Mortal f m a -> f x -> EitherT a m (x, Mortal f m a)-runMortal = unsafeCoerce `asTypeOf` ((fmap (fmap Mortal) . ) . runObject . unMortal)-{-# INLINE runMortal #-}---- | Restricted 'Mortal' constuctor which can be applied to 'transit', 'fromFoldable' without ambiguousness.-mortal_ :: Object f (EitherT () g) -> Mortal f g ()-mortal_ = Mortal-{-# INLINE mortal_ #-}---- | Turn an object into a mortal without death.-immortal :: Monad m => Object f m -> Mortal f m x-immortal obj = Mortal (obj @>>^ lift)-{-# INLINE immortal #-}---- | Send a message to mortals through a filter.-apprisesOf :: Monad m-  => FilterLike' (WriterT r m) s (Mortal f m b)-  -> f a -> (a -> r) -> (b -> r) -> StateT s m r-apprisesOf l f p q = StateT $ \t -> liftM swap $ runWriterT $ flip l t-    $ \obj -> WriterT $ runEitherT (runMortal obj f) >>= \case-      Left r -> return (Nothing, q r)-      Right (x, obj') -> return (Just obj', p x)-{-# INLINABLE apprisesOf #-}---- | Send a message to mortals in a 'Witherable' container.------ @apprises = apprisesOf wither@----apprises :: (Witherable t, Monad m, Applicative m, Monoid r) => f a -> (a -> r) -> (b -> r) -> StateT (t (Mortal f m b)) m r-apprises = apprisesOf wither-{-# INLINE apprises #-}---- | Send a message to mortals in a container.-apprise :: (Witherable t, Monad m, Applicative m) => f a -> StateT (t (Mortal f m r)) m ([a], [r])-apprise f = fmap (flip appEndo [] *** flip appEndo [])-  $ apprises f (\a -> (Endo (a:), mempty)) (\b -> (mempty, Endo (b:)))-{-# INLINE apprise #-}+{-# LANGUAGE CPP #-}
+{-# LANGUAGE Trustworthy #-}
+{-# LANGUAGE Rank2Types #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE BangPatterns #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Control.Object.Mortal
+-- Copyright   :  (c) Fumiaki Kinoshita 2015
+-- License     :  BSD3
+--
+-- Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>
+-- Stability   :  provisional
+-- Portability :  GADTs, Rank2Types
+--
+-----------------------------------------------------------------------------
+module Control.Object.Mortal (
+    Mortal(..),
+    mortal,
+    mortal_,
+    runMortal,
+    immortal,
+    apprisesOf,
+    apprises,
+    apprise
+    ) where
+
+import Control.Object.Object
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative
+#endif
+import Control.Monad.Trans.Either
+import Control.Monad
+import Control.Monad.Trans.Class
+import Control.Monad.Trans.State.Strict
+import Control.Monad.Trans.Writer.Strict
+import Data.Monoid
+import Data.Witherable
+import Data.Tuple (swap)
+import Control.Arrow ((***))
+import Unsafe.Coerce
+
+-- | A 'Mortal' is an object that may die.
+-- A mortal yields a final result upon death.
+-- @'Mortal' f g@ forms a 'Monad':
+-- 'return' is a dead object and ('>>=') prolongs the life of the left object.
+--
+-- @Object f g ≡ Mortal f g Void@
+--
+newtype Mortal f g a = Mortal { unMortal :: Object f (EitherT a g) }
+
+instance (Functor m, Monad m) => Functor (Mortal f m) where
+  fmap f (Mortal obj) = Mortal (obj @>>^ bimapEitherT f id)
+  {-# INLINE fmap #-}
+
+instance (Functor m, Monad m) => Applicative (Mortal f m) where
+  pure = return
+  {-# INLINE pure #-}
+  (<*>) = ap
+  {-# INLINE (<*>) #-}
+
+instance Monad m => Monad (Mortal f m) where
+  return a = mortal $ const $ left a
+  {-# INLINE return #-}
+  m >>= k = mortal $ \f -> lift (runEitherT $ runMortal m f) >>= \r -> case r of
+    Left a -> runMortal (k a) f
+    Right (x, m') -> return (x, m' >>= k)
+
+instance MonadTrans (Mortal f) where
+  lift m = mortal $ const $ EitherT $ liftM Left m
+  {-# INLINE lift #-}
+
+-- | Construct a mortal in a 'Object' construction manner.
+mortal :: Monad m => (forall x. f x -> EitherT a m (x, Mortal f m a)) -> Mortal f m a
+mortal f = unsafeCoerce f `asTypeOf` Mortal (Object (fmap (fmap unMortal) . f))
+{-# INLINE mortal #-}
+
+-- | Send a message to a mortal.
+runMortal :: Monad m => Mortal f m a -> f x -> EitherT a m (x, Mortal f m a)
+runMortal = unsafeCoerce `asTypeOf` ((fmap (fmap Mortal) . ) . runObject . unMortal)
+{-# INLINE runMortal #-}
+
+-- | Restricted 'Mortal' constuctor which can be applied to 'transit', 'fromFoldable' without ambiguousness.
+mortal_ :: Object f (EitherT () g) -> Mortal f g ()
+mortal_ = Mortal
+{-# INLINE mortal_ #-}
+
+-- | Turn an object into a mortal without death.
+immortal :: Monad m => Object f m -> Mortal f m x
+immortal obj = Mortal (obj @>>^ lift)
+{-# INLINE immortal #-}
+
+-- | Send a message to mortals through a filter.
+apprisesOf :: Monad m
+  => FilterLike' (WriterT r m) s (Mortal f m b)
+  -> f a -> (a -> r) -> (b -> r) -> StateT s m r
+apprisesOf l f p q = StateT $ \t -> liftM swap $ runWriterT $ flip l t
+    $ \obj -> WriterT $ runEitherT (runMortal obj f) >>= \case
+      Left r -> return (Nothing, q r)
+      Right (x, obj') -> return (Just obj', p x)
+{-# INLINABLE apprisesOf #-}
+
+-- | Send a message to mortals in a 'Witherable' container.
+--
+-- @apprises = apprisesOf wither@
+--
+apprises :: (Witherable t, Monad m, Applicative m, Monoid r) => f a -> (a -> r) -> (b -> r) -> StateT (t (Mortal f m b)) m r
+apprises = apprisesOf wither
+{-# INLINE apprises #-}
+
+-- | Send a message to mortals in a container.
+apprise :: (Witherable t, Monad m, Applicative m) => f a -> StateT (t (Mortal f m r)) m ([a], [r])
+apprise f = fmap (flip appEndo [] *** flip appEndo [])
+  $ apprises f (\a -> (Endo (a:), mempty)) (\b -> (mempty, Endo (b:)))
+{-# INLINE apprise #-}
src/Control/Object/Object.hs view
@@ -1,240 +1,240 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE Rank2Types, TupleSections, TypeOperators #-}-{-# LANGUAGE GADTs #-}-#if __GLASGOW_HASKELL__ >= 707-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE Safe #-}-#else-{-# LANGUAGE Trustworthy #-}-#endif--------------------------------------------------------------------------------- |--- Module      :  Control.Object.Object--- Copyright   :  (c) Fumiaki Kinoshita 2015--- License     :  BSD3------ Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>--- Stability   :  provisional--- Portability :  GADTs, Rank2Types----------------------------------------------------------------------------------module Control.Object.Object (Object(..)-  , echo-  , (@>>@)-  , (@<<@)-  , liftO-  , (^>>@)-  , (@>>^)-  , (@||@)-  -- * Stateful construction-  , unfoldO-  , unfoldOM-  , stateful-  , (@~)-  , variable-  -- * Method cascading-  , (@-)-  , iterObject-  , iterative-  , cascadeObject-  , cascading-  -- * Filtering-  , Fallible(..)-  , filteredO-  , filterO-  -- * Manipulation on StateT-  , invokesOf-  , invokes-  , (@!=)-  , announce-  , withBuilder-  ) where-import Data.Typeable-import Control.Monad.Trans.State.Strict-import Control.Monad.Free-import Control.Monad-import Control.Monad.Skeleton-import Data.Traversable as T-import Control.Monad.Trans.Writer.Strict-import Data.Monoid-import Data.Tuple (swap)-import qualified Data.Functor.Sum as Functor---- | 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 transforms effects.--- 'Object's can be composed just like functions using '@>>@'; the identity element is 'echo'.--- Objects are morphisms of the category of actions.------ [/Naturality/]---     @runObject obj . fmap f ≡ fmap f . runObject obj@----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 g) => Typeable (Object f g) where-  typeOf t = mkTyConApp objectTyCon [typeOf1 (f t), typeOf1 (g t)] where-    f :: Object f g -> f a-    f = undefined-    g :: Object f g -> g a-    g = undefined--objectTyCon :: TyCon-#if __GLASGOW_HASKELL__ < 704-objectTyCon = mkTyCon "Control.Object.Object"-#else-objectTyCon = mkTyCon3 "objective" "Control.Object" "Object"-#endif-{-# NOINLINE objectTyCon #-}-#endif---- | An infix alias for 'runObject'-(@-) :: Object f g -> f x -> g (x, Object f g)-(@-) = runObject-{-# INLINE (@-) #-}-infixr 3 @---infixr 1 ^>>@-infixr 1 @>>^--(^>>@) :: 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-{-# INLINE (^>>@) #-}--(@>>^) :: 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-{-# INLINE (@>>^) #-}---- | The trivial object-echo :: Functor f => Object f f-echo = Object $ fmap (,echo)---- | Lift a natural transformation into an object.-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 #-}---- | The categorical composition of objects.-(@>>@) :: Functor h => Object f g -> Object g h -> Object f h-Object m @>>@ Object n = Object $ fmap (\((x, m'), n') -> (x, m' @>>@ n')) . n . m-infixr 1 @>>@---- | Reversed '(@>>@)'-(@<<@) :: Functor h => Object g h -> Object f g -> Object f h-(@<<@) = flip (@>>@)-{-# INLINE (@<<@) #-}-infixl 1 @<<@---- | Combine objects so as to handle a 'Functor.Sum' of interfaces.-(@||@) :: Functor h => Object f h -> Object g h -> Object (f `Functor.Sum` g) h-a @||@ b = Object $ \r -> case r of-  Functor.InL f -> fmap (fmap (@||@b)) (runObject a f)-  Functor.InR g -> fmap (fmap (a@||@)) (runObject b g)---- | An unwrapped analog of 'stateful'---     @id = unfoldO runObject@---     @'iterative' = unfoldO 'iterObject'@---     @'cascading' = unfoldO 'cascadeObject'@-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 #-}---- | Same as 'unfoldO' but requires 'Monad' instead-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 #-}---- | Build a stateful object.------ @stateful t s = t ^>>\@ variable s@----stateful :: Monad m => (forall a. t a -> StateT s m a) -> s -> Object t m-stateful h = go where-  go s = Object $ \f -> runStateT (h f) s >>= \(a, s') -> s' `seq` return (a, go s')-{-# INLINE stateful #-}---- | Flipped 'stateful'.--- it is super convenient to use with the LambdaCase extension.-(@~) :: Monad m => s -> (forall a. t a -> StateT s m a) -> Object t m-s @~ h = stateful h s-{-# INLINE (@~) #-}-infix 1 @~---- | Cascading-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---- | Objects can consume free monads. 'cascading' is more preferred.-iterative :: Monad m => Object f m -> Object (Free f) m-iterative = unfoldOM iterObject-{-# INLINE iterative #-}---- | A mutable variable.------ @variable = stateful id@----variable :: Monad m => s -> Object (StateT s m) m-variable = stateful id-{-# INLINE variable #-}---- | Pass zero or more messages to an object.-cascadeObject :: Monad m => Object t m -> Skeleton t a -> m (a, Object t m)-cascadeObject obj sk = case unbone sk of-  Return a -> return (a, obj)-  t :>>= k -> runObject obj t >>= \(a, obj') -> cascadeObject obj' (k a)---- | Add capability to handle multiple messages at once.-cascading :: Monad m => Object t m -> Object (Skeleton t) m-cascading = unfoldOM cascadeObject-{-# INLINE cascading #-}---- | Send a message to an object through a lens.-invokesOf :: Monad m-  => ((Object t m -> WriterT r m (Object t m)) -> s -> WriterT r m s)-  -> t a -> (a -> r) -> StateT s m r-invokesOf t f c = StateT $ liftM swap . runWriterT-  . t (\obj -> WriterT $ runObject obj f >>= \(x, obj') -> return (obj', c x))-{-# INLINABLE invokesOf #-}--invokes :: (T.Traversable t, Monad m, Monoid r)-  => f a -> (a -> r) -> StateT (t (Object f m)) m r-invokes = invokesOf T.mapM-{-# INLINE invokes #-}---- | Send a message to objects in a traversable container.------ @announce = withBuilder . invokesOf traverse@----announce :: (T.Traversable t, Monad m) => f a -> StateT (t (Object f m)) m [a]-announce f = withBuilderM (invokes f)-{-# INLINABLE announce #-}---- | A method invocation operator on 'StateT'.-(@!=) :: Monad m-  => ((Object t m -> WriterT a m (Object t m)) -> s -> WriterT a m s)-  -> t a -> StateT s m a-l @!= f = invokesOf l f id-{-# INLINE (@!=) #-}--withBuilder :: Functor f => ((a -> Endo [a]) -> f (Endo [a])) -> f [a]-withBuilder f = fmap (flip appEndo []) (f (Endo . (:)))-{-# INLINABLE withBuilder #-}--withBuilderM :: Monad f => ((a -> Endo [a]) -> f (Endo [a])) -> f [a]-withBuilderM f = liftM (flip appEndo []) (f (Endo . (:)))-{-# INLINABLE withBuilderM #-}--data Fallible t a where-  Fallible :: t a -> Fallible t (Maybe a)--filteredO :: Monad m-       => (forall x. t x -> Bool)-       -> Object t m -> Object (Fallible t) m-filteredO p obj = Object $ \(Fallible t) -> if p t-  then runObject obj t >>= \(a, obj') -> return (Just a, filteredO p obj')-  else return (Nothing, filteredO p obj)--filterO :: (forall x. t x -> Bool) -> Object (Fallible t) (Skeleton t)-filterO p = filteredO p (liftO bone)-{-# INLINE filterO #-}+{-# LANGUAGE CPP #-}
+{-# LANGUAGE Rank2Types, TupleSections, TypeOperators #-}
+{-# LANGUAGE GADTs #-}
+#if __GLASGOW_HASKELL__ >= 707
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE Safe #-}
+#else
+{-# LANGUAGE Trustworthy #-}
+#endif
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Control.Object.Object
+-- Copyright   :  (c) Fumiaki Kinoshita 2015
+-- License     :  BSD3
+--
+-- Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>
+-- Stability   :  provisional
+-- Portability :  GADTs, Rank2Types
+--
+-----------------------------------------------------------------------------
+module Control.Object.Object (Object(..)
+  , echo
+  , (@>>@)
+  , (@<<@)
+  , liftO
+  , (^>>@)
+  , (@>>^)
+  , (@||@)
+  -- * Stateful construction
+  , unfoldO
+  , unfoldOM
+  , stateful
+  , (@~)
+  , variable
+  -- * Method cascading
+  , (@-)
+  , iterObject
+  , iterative
+  , cascadeObject
+  , cascading
+  -- * Filtering
+  , Fallible(..)
+  , filteredO
+  , filterO
+  -- * Manipulation on StateT
+  , invokesOf
+  , invokes
+  , (@!=)
+  , announce
+  , withBuilder
+  ) where
+import Data.Typeable
+import Control.Monad.Trans.State.Strict
+import Control.Monad.Free
+import Control.Monad
+import Control.Monad.Skeleton
+import Data.Traversable as T
+import Control.Monad.Trans.Writer.Strict
+import Data.Monoid
+import Data.Tuple (swap)
+import qualified Data.Functor.Sum as Functor
+
+-- | 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 transforms effects.
+-- 'Object's can be composed just like functions using '@>>@'; the identity element is 'echo'.
+-- Objects are morphisms of the category of actions.
+--
+-- [/Naturality/]
+--     @runObject obj . fmap f ≡ fmap f . runObject obj@
+--
+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 g) => Typeable (Object f g) where
+  typeOf t = mkTyConApp objectTyCon [typeOf1 (f t), typeOf1 (g t)] where
+    f :: Object f g -> f a
+    f = undefined
+    g :: Object f g -> g a
+    g = undefined
+
+objectTyCon :: TyCon
+#if __GLASGOW_HASKELL__ < 704
+objectTyCon = mkTyCon "Control.Object.Object"
+#else
+objectTyCon = mkTyCon3 "objective" "Control.Object" "Object"
+#endif
+{-# NOINLINE objectTyCon #-}
+#endif
+
+-- | An infix alias for 'runObject'
+(@-) :: Object f g -> f x -> g (x, Object f g)
+(@-) = runObject
+{-# INLINE (@-) #-}
+infixr 3 @-
+
+infixr 1 ^>>@
+infixr 1 @>>^
+
+(^>>@) :: 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
+{-# INLINE (^>>@) #-}
+
+(@>>^) :: 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
+{-# INLINE (@>>^) #-}
+
+-- | The trivial object
+echo :: Functor f => Object f f
+echo = Object $ fmap (,echo)
+
+-- | Lift a natural transformation into an object.
+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 #-}
+
+-- | The categorical composition of objects.
+(@>>@) :: Functor h => Object f g -> Object g h -> Object f h
+Object m @>>@ Object n = Object $ fmap (\((x, m'), n') -> (x, m' @>>@ n')) . n . m
+infixr 1 @>>@
+
+-- | Reversed '(@>>@)'
+(@<<@) :: Functor h => Object g h -> Object f g -> Object f h
+(@<<@) = flip (@>>@)
+{-# INLINE (@<<@) #-}
+infixl 1 @<<@
+
+-- | Combine objects so as to handle a 'Functor.Sum' of interfaces.
+(@||@) :: Functor h => Object f h -> Object g h -> Object (f `Functor.Sum` g) h
+a @||@ b = Object $ \r -> case r of
+  Functor.InL f -> fmap (fmap (@||@b)) (runObject a f)
+  Functor.InR g -> fmap (fmap (a@||@)) (runObject b g)
+
+-- | An unwrapped analog of 'stateful'
+--     @id = unfoldO runObject@
+--     @'iterative' = unfoldO 'iterObject'@
+--     @'cascading' = unfoldO 'cascadeObject'@
+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 #-}
+
+-- | Same as 'unfoldO' but requires 'Monad' instead
+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 #-}
+
+-- | Build a stateful object.
+--
+-- @stateful t s = t ^>>\@ variable s@
+--
+stateful :: Monad m => (forall a. t a -> StateT s m a) -> s -> Object t m
+stateful h = go where
+  go s = Object $ \f -> runStateT (h f) s >>= \(a, s') -> s' `seq` return (a, go s')
+{-# INLINE stateful #-}
+
+-- | Flipped 'stateful'.
+-- it is super convenient to use with the LambdaCase extension.
+(@~) :: Monad m => s -> (forall a. t a -> StateT s m a) -> Object t m
+s @~ h = stateful h s
+{-# INLINE (@~) #-}
+infix 1 @~
+
+-- | Cascading
+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
+
+-- | Objects can consume free monads. 'cascading' is more preferred.
+iterative :: Monad m => Object f m -> Object (Free f) m
+iterative = unfoldOM iterObject
+{-# INLINE iterative #-}
+
+-- | A mutable variable.
+--
+-- @variable = stateful id@
+--
+variable :: Monad m => s -> Object (StateT s m) m
+variable = stateful id
+{-# INLINE variable #-}
+
+-- | Pass zero or more messages to an object.
+cascadeObject :: Monad m => Object t m -> Skeleton t a -> m (a, Object t m)
+cascadeObject obj sk = case unbone sk of
+  Return a -> return (a, obj)
+  t :>>= k -> runObject obj t >>= \(a, obj') -> cascadeObject obj' (k a)
+
+-- | Add capability to handle multiple messages at once.
+cascading :: Monad m => Object t m -> Object (Skeleton t) m
+cascading = unfoldOM cascadeObject
+{-# INLINE cascading #-}
+
+-- | Send a message to an object through a lens.
+invokesOf :: Monad m
+  => ((Object t m -> WriterT r m (Object t m)) -> s -> WriterT r m s)
+  -> t a -> (a -> r) -> StateT s m r
+invokesOf t f c = StateT $ liftM swap . runWriterT
+  . t (\obj -> WriterT $ runObject obj f >>= \(x, obj') -> return (obj', c x))
+{-# INLINABLE invokesOf #-}
+
+invokes :: (T.Traversable t, Monad m, Monoid r)
+  => f a -> (a -> r) -> StateT (t (Object f m)) m r
+invokes = invokesOf T.mapM
+{-# INLINE invokes #-}
+
+-- | Send a message to objects in a traversable container.
+--
+-- @announce = withBuilder . invokesOf traverse@
+--
+announce :: (T.Traversable t, Monad m) => f a -> StateT (t (Object f m)) m [a]
+announce f = withBuilderM (invokes f)
+{-# INLINABLE announce #-}
+
+-- | A method invocation operator on 'StateT'.
+(@!=) :: Monad m
+  => ((Object t m -> WriterT a m (Object t m)) -> s -> WriterT a m s)
+  -> t a -> StateT s m a
+l @!= f = invokesOf l f id
+{-# INLINE (@!=) #-}
+
+withBuilder :: Functor f => ((a -> Endo [a]) -> f (Endo [a])) -> f [a]
+withBuilder f = fmap (flip appEndo []) (f (Endo . (:)))
+{-# INLINABLE withBuilder #-}
+
+withBuilderM :: Monad f => ((a -> Endo [a]) -> f (Endo [a])) -> f [a]
+withBuilderM f = liftM (flip appEndo []) (f (Endo . (:)))
+{-# INLINABLE withBuilderM #-}
+
+data Fallible t a where
+  Fallible :: t a -> Fallible t (Maybe a)
+
+filteredO :: Monad m
+       => (forall x. t x -> Bool)
+       -> Object t m -> Object (Fallible t) m
+filteredO p obj = Object $ \(Fallible t) -> if p t
+  then runObject obj t >>= \(a, obj') -> return (Just a, filteredO p obj')
+  else return (Nothing, filteredO p obj)
+
+filterO :: (forall x. t x -> Bool) -> Object (Fallible t) (Skeleton t)
+filterO p = filteredO p (liftO bone)
+{-# INLINE filterO #-}
src/Data/Functor/Request.hs view
@@ -1,93 +1,93 @@-{-# LANGUAGE CPP, DeriveFunctor, DeriveDataTypeable, ConstraintKinds, FlexibleContexts, TypeOperators, DataKinds, TypeFamilies #-}--------------------------------------------------------------------------------- |--- Module      :  Data.Functor.Request--- Copyright   :  (c) Fumiaki Kinoshita 2015--- License     :  BSD3------ Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>--- Stability   :  experimental--- Portability :  non-portable----------------------------------------------------------------------------------module Data.Functor.Request where-import Data.Typeable-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid-#endif-import Control.Applicative-import Data.Profunctor-import Control.Object.Object-import qualified Data.HashMap.Strict as HM-import Data.Hashable-import Control.Arrow---- | @'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)---- | Apply a function to the body of 'Request'-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)-  {-# INLINE pure #-}-  Request a c <*> Request b d = Request (mappend a b) (c <*> d)-  {-# INLINE (<*>) #-}---- | Create a 'Request'.-request :: a -> Request a b b-request a = Request a id-{-# INLINE request #-}---- | Handle a 'Request', smashing the continuation.-accept :: Functor m => (a -> m b) -> Request a b r -> m r-accept f = \(Request a cont) -> cont <$> f a-{-# INLINE accept #-}---- | Add a step as a mealy machine-mealy :: Functor m => (a -> m (b, Object (Request a b) m)) -> Object (Request a b) m-mealy f = Object $ \(Request a cont) -> first cont <$> f a-{-# INLINE mealy #-}---- | The flyweight object-flyweight :: (Applicative m, Eq k, Hashable k) => (k -> m a) -> Object (Request k a) m-flyweight f = go HM.empty where-  go m = mealy $ \k -> case HM.lookup k m of-    Just a -> pure (a, go m)-    Nothing -> (\a -> (a, go $ HM.insert k a m)) <$> f k-{-# INLINE flyweight #-}---- | Compose mealy machines-(>~~>) :: Monad m => Object (Request a b) m -> Object (Request b c) m -> Object (Request a c) m-p >~~> q = Object $ \(Request a cont) -> do-  (b, p') <- runObject p (Request a id)-  (r, q') <- runObject q (Request b cont)-  return (r, p' >~~> q')--accumulator :: Applicative m => (b -> a -> b) -> b -> Object (Request a b) m-accumulator f = go where-  go b = mealy $ \a -> pure (b, go (f b a))-{-# INLINE accumulator #-}---- | Create a mealy machine from a time-varying action.------ @ animate f ≡ accumulator (+) 0 >~~> liftO (accept f)@----animate :: (Applicative m, Num t) => (t -> m a) -> Object (Request t a) m-animate f = go 0 where-  go t = mealy $ \dt -> flip (,) (go (t + dt)) <$> f t-{-# INLINE animate #-}---- | Like 'animate', but the life is limited.-transit :: (Alternative m, Fractional t, Ord t) => t -> (t -> m a) -> Object (Request t a) m-transit len f = animate go where-  go t-    | t >= len = empty-    | otherwise = f (t / len)-{-# INLINE transit #-}+{-# LANGUAGE CPP, DeriveFunctor, DeriveDataTypeable, ConstraintKinds, FlexibleContexts, TypeOperators, DataKinds, TypeFamilies #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Functor.Request
+-- Copyright   :  (c) Fumiaki Kinoshita 2015
+-- License     :  BSD3
+--
+-- Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-----------------------------------------------------------------------------
+module Data.Functor.Request where
+import Data.Typeable
+#if !MIN_VERSION_base(4,8,0)
+import Data.Monoid
+#endif
+import Control.Applicative
+import Data.Profunctor
+import Control.Object.Object
+import qualified Data.HashMap.Strict as HM
+import Data.Hashable
+import Control.Arrow
+
+-- | @'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)
+
+-- | Apply a function to the body of 'Request'
+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)
+  {-# INLINE pure #-}
+  Request a c <*> Request b d = Request (mappend a b) (c <*> d)
+  {-# INLINE (<*>) #-}
+
+-- | Create a 'Request'.
+request :: a -> Request a b b
+request a = Request a id
+{-# INLINE request #-}
+
+-- | Handle a 'Request', smashing the continuation.
+accept :: Functor m => (a -> m b) -> Request a b r -> m r
+accept f = \(Request a cont) -> cont <$> f a
+{-# INLINE accept #-}
+
+-- | Add a step as a mealy machine
+mealy :: Functor m => (a -> m (b, Object (Request a b) m)) -> Object (Request a b) m
+mealy f = Object $ \(Request a cont) -> first cont <$> f a
+{-# INLINE mealy #-}
+
+-- | The flyweight object
+flyweight :: (Applicative m, Eq k, Hashable k) => (k -> m a) -> Object (Request k a) m
+flyweight f = go HM.empty where
+  go m = mealy $ \k -> case HM.lookup k m of
+    Just a -> pure (a, go m)
+    Nothing -> (\a -> (a, go $ HM.insert k a m)) <$> f k
+{-# INLINE flyweight #-}
+
+-- | Compose mealy machines
+(>~~>) :: Monad m => Object (Request a b) m -> Object (Request b c) m -> Object (Request a c) m
+p >~~> q = Object $ \(Request a cont) -> do
+  (b, p') <- runObject p (Request a id)
+  (r, q') <- runObject q (Request b cont)
+  return (r, p' >~~> q')
+
+accumulator :: Applicative m => (b -> a -> b) -> b -> Object (Request a b) m
+accumulator f = go where
+  go b = mealy $ \a -> pure (b, go (f b a))
+{-# INLINE accumulator #-}
+
+-- | Create a mealy machine from a time-varying action.
+--
+-- @ animate f ≡ accumulator (+) 0 >~~> liftO (accept f)@
+--
+animate :: (Applicative m, Num t) => (t -> m a) -> Object (Request t a) m
+animate f = go 0 where
+  go t = mealy $ \dt -> flip (,) (go (t + dt)) <$> f t
+{-# INLINE animate #-}
+
+-- | Like 'animate', but the life is limited.
+transit :: (Alternative m, Fractional t, Ord t) => t -> (t -> m a) -> Object (Request t a) m
+transit len f = animate go where
+  go t
+    | t >= len = empty
+    | otherwise = f (t / len)
+{-# INLINE transit #-}