-----------------------------------------------------------------------------
-- |
-- Module      :  Control.Comonad.Context
-- Copyright   :  2004 Dave Menendez
-- License     :  public domain
-- 
-- Maintainer  :  dan.doel@gmail.com
-- Stability   :  experimental
-- Portability :  portable
--
-- Defines the state-in-context comonad, which is dual to the state monad.
-- Each operation in the context comonad runs in a context determined
-- by /later/ operations. (Observe, for example, 'experiment', which runs
-- the preceeding operations multiple times in different contexts and
-- returns a list of results.)
--
-----------------------------------------------------------------------------

module Control.Comonad.Context
  ( Context(..)
  , get
  , modify
  , experiment
  , liftCtx
  ) where

import Control.Comonad

data Context c a = Context (c -> a) c

instance Functor (Context c) where
  fmap g (Context f c) = Context (g . f) c

instance Comonad (Context c) where
  extract   (Context f c) = f c
  duplicate (Context f c) = Context (Context f) c

-- | Returns the context
get :: Context c a -> c
get (Context _ c) = c

-- | Returns the result of the preceeding operations running in
-- a modified context
modify :: (c -> c) -> Context c a -> a
modify m (Context f c) = f (m c)

-- | Returns a list of results created by running prior operations
-- in modified contexts created by the list of context-modifiers.
experiment :: [c -> c] -> Context c a -> [a]
experiment ms (Context f c) = map (\m -> f (m c)) ms

{-|
Lifts an operation into the context comonad. Syntactic sugar
for @fmap@ when chaining comonad operations.

@
  liftCtx         == extract . fmap f
  w =>> liftCtx f == fmap f w
@
-}
liftCtx :: (a -> b) -> Context c a -> b
liftCtx g (Context f c) = g (f c)

{-
inContext :: ((c -> a) -> c -> b) -> Context c a -> b
inContext op (Context f c) = op f c

get      = inContext (\f c -> c)
modify m = inContext (\f c -> f (m c))
-}