-----------------------------------------------------------------------------
-- |
-- Module      :  Control.Arrow.Internals
-- Copyright   :  (c) Ross Paterson 2003
-- License     :  BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer  :  ross@soi.city.ac.uk
-- Stability   :  experimental
-- Portability :  non-portable (multi-parameter type classes)
--
-- Manipulation of composite arrow types, beyond the basic lifting and
-- encapsulation provided with each arrow transformer.
--
-- The signatures are designed to be compatible with the proposed notation
-- for arrows, cf. <http://www.haskell.org/arrows/>.

-- #hide
module Control.Arrow.Internals (
		ArrowAddState(..),
		ArrowAddReader(..),
		ArrowAddWriter(..),
		ArrowAddError(..),
		ArrowAddStream(..),
	) where

import Control.Arrow
import Control.Arrow.Operations
import Data.Stream

-- | Adding a 'Control.Arrow.Transformer.State.StateArrow' to an
-- arrow type, but not necessarily as the outer arrow transformer.
--
-- Typically a composite arrow type is built by applying a series
-- of arrow transformer to a base arrow (usually either a function
-- arrow or a 'Kleisli' arrow.  One can add a transformer to the
-- top of this stack using the 'Control.Arrow.Transformer.lift'
-- method of the 'Control.Arrow.Transformer.ArrowTransformer' class,
-- or remove a state transformer from the top of the stack using the
-- 'Control.Arrow.Transformer.State.runState' encapsulation operator.
-- The methods of this class add and remove state transformers anywhere
-- in the stack.  In the instance
--
-- >	instance Arrow a => ArrowAddState s (ArrowState s a) a
--
-- they are equivalent to 'Control.Arrow.Transformer.lift' and
-- 'Control.Arrow.Transformer.State.runState' respectively.
-- Instances are lifted through other transformers with
--
-- >	instance ArrowAddState s a a' =>
-- >		ArrowAddState s (FooArrow a) (FooArrow a')

class (ArrowState s a, Arrow a') => ArrowAddState s a a' | a -> a' where

	-- | Lift a computation from an arrow to one with an added state.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> ...
	-- >		(|liftState cmd|)

	liftState :: a' e b -> a e b

	-- | Elimination of a state transformer from a computation,
	-- exposing the initial and final states.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> do
	-- >		...
	-- >		(result, final_state) <- (|elimState cmd|) init_state

	elimState :: a e b -> a' (e,s) (b,s)

-- | Adding a 'Control.Arrow.Transformer.Reader.ReaderArrow' to an
-- arrow type, but not necessarily as the outer arrow transformer.
--
-- Typically a composite arrow type is built by applying a series
-- of arrow transformer to a base arrow (usually either a function
-- arrow or a 'Kleisli' arrow.  One can add a transformer to the
-- top of this stack using the 'Control.Arrow.Transformer.lift'
-- method of the 'Control.Arrow.Transformer.ArrowTransformer' class,
-- or remove a state transformer from the top of the stack using the
-- 'Control.Arrow.Transformer.Reader.runReader' encapsulation operator.
-- The methods of this class add and remove state transformers anywhere
-- in the stack.  In the instance
--
-- >	instance Arrow a => ArrowAddReader r (ArrowReader r a) a
--
-- they are equivalent to 'Control.Arrow.Transformer.lift' and
-- 'Control.Arrow.Transformer.Reader.runReader' respectively.
-- Instances are lifted through other transformers with
--
-- >	instance ArrowAddReader r a a' =>
-- >		ArrowAddReader r (FooArrow a) (FooArrow a')

class (ArrowReader r a, Arrow a') => ArrowAddReader r a a' | a -> a' where

	-- | Lift a computation from an arrow to one with an added environment.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> ...
	-- >		(|liftReader cmd|)

	liftReader :: a' e b -> a e b

	-- | Elimination of a state reader from a computation,
	-- taking a value for the state.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> ...
	-- >		(|elimReader cmd|) env

	elimReader :: a e b -> a' (e,r) b

-- | Adding a 'Control.Arrow.Transformer.Writer.WriterArrow' to an
-- arrow type, but not necessarily as the outer arrow transformer.
--
-- Typically a composite arrow type is built by applying a series
-- of arrow transformer to a base arrow (usually either a function
-- arrow or a 'Kleisli' arrow.  One can add a transformer to the
-- top of this stack using the 'Control.Arrow.Transformer.lift'
-- method of the 'Control.Arrow.Transformer.ArrowTransformer' class,
-- or remove a state transformer from the top of the stack using the
-- 'Control.Arrow.Transformer.Writer.runWriter' encapsulation operator.
-- The methods of this class add and remove state transformers anywhere
-- in the stack.  In the instance
--
-- >	instance Arrow a => ArrowAddWriter w (ArrowWriter w a) a
--
-- they are equivalent to 'Control.Arrow.Transformer.lift' and
-- 'Control.Arrow.Transformer.Writer.runWriter' respectively.
-- Instances are lifted through other transformers with
--
-- >	instance ArrowAddWriter w a a' =>
-- >		ArrowAddWriter w (FooArrow a) (FooArrow a')

class (ArrowWriter w a, Arrow a') => ArrowAddWriter w a a' | a -> a' where

	-- | Lift a computation from an arrow to one with added output.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> ...
	-- >		(|liftWriter cmd|)

	liftWriter :: a' e b -> a e b

	-- | Elimination of an output writer from a computation,
	-- providing the accumulated output.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> do
	-- >		...
	-- >		(result, output) <- (|elimWriter cmd|)

	elimWriter :: a e b -> a' e (b,w)

-- | Adding a 'Control.Arrow.Transformer.Error.ErrorArrow' to an
-- arrow type, but not necessarily as the outer arrow transformer.
--
-- Typically a composite arrow type is built by applying a series
-- of arrow transformer to a base arrow (usually either a function
-- arrow or a 'Kleisli' arrow.  One can add a transformer to the
-- top of this stack using the 'Control.Arrow.Transformer.lift'
-- method of the 'Control.Arrow.Transformer.ArrowTransformer' class,
-- or remove a state transformer from the top of the stack using the
-- 'Control.Arrow.Transformer.Error.runError' encapsulation operator.
-- The methods of this class add and remove state transformers anywhere
-- in the stack.  In the instance
--
-- >	instance Arrow a => ArrowAddError ex (ArrowError ex a) a
--
-- they are equivalent to 'Control.Arrow.Transformer.lift' and
-- 'Control.Arrow.Transformer.Error.runError' respectively.
-- Instances are lifted through other transformers with
--
-- >	instance ArrowAddError ex a a' =>
-- >		ArrowAddError ex (FooArrow a) (FooArrow a')
--
-- This could be combined with 'Control.Arrow.Transformer.Error.handle',
-- since the resulting arrow is always the arrow of the handler.
-- Separating them has the advantage of consistency with the other arrows,
-- and might give more helpful type error messages.

class (ArrowError ex a, Arrow a') => ArrowAddError ex a a' | a -> a' where

	-- | Lift a computation from an arrow to one with error handling.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> ...
	-- >		(|liftError cmd|)

	liftError :: a' e b -> a e b

	-- | Elimination of errors from a computation,
	-- by completely handling any errors.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> ...
	-- >		body `elimError` \ex -> handler

	elimError :: a e b -> a' (e,ex) b -> a' e b

-- | Adding a 'Control.Arrow.Transformer.Stream.StreamArrow' to an
-- arrow type, but not necessarily as the outer arrow transformer.
--
-- Typically a composite arrow type is built by applying a series
-- of arrow transformer to a base arrow (usually either a function
-- arrow or a 'Kleisli' arrow.  One can add a transformer to the
-- top of this stack using the 'Control.Arrow.Transformer.lift'
-- method of the 'Control.Arrow.Transformer.ArrowTransformer' class,
-- or remove a state transformer from the top of the stack using the
-- 'Control.Arrow.Transformer.Stream.runStream' encapsulation operator.
-- The methods of this class add and remove state transformers anywhere
-- in the stack.  In the instance
--
-- >	instance Arrow a => ArrowAddStream (ArrowStream a) a
--
-- they are equivalent to 'Control.Arrow.Transformer.lift' and
-- 'Control.Arrow.Transformer.Stream.runStream' respectively.
-- Instances are lifted through other transformers with
--
-- >	instance ArrowAddStream a a' =>
-- >		ArrowAddStream (FooArrow a) (FooArrow a')

class (ArrowCircuit a, Arrow a') => ArrowAddStream a a' | a -> a' where

	-- | Lift a computation from an arrow to a stream processing one.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> ...
	-- >		(|liftStream cmd|)

	liftStream :: a' e b -> a e b

	-- | Run a stream processor on a stream of inputs,
	-- obtaining a stream of outputs.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> do
	-- >		...
	-- >		ys <- (|elimStream (\x -> ...)|) xs
	--
	-- Here @xs@ refers to the input stream and @x@ to individual
	-- elements of that stream.  @ys@ is bound to the output stream.

	elimStream :: a (e,b) c -> a' (e,Stream b) (Stream c)