-----------------------------------------------------------------------------
-- |
-- 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)