{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

-- ------------------------------------------------------------

{- |
   Module     : Control.Arrow.ArrowState
   Copyright  : Copyright (C) 2005 Uwe Schmidt
   License    : MIT

   Maintainer : Uwe Schmidt (uwe\@fh-wedel.de)
   Stability  : experimental
   Portability: multi parameter classes and functional depenedencies required

   Arrows for managing an explicit state

   State arrows work similar to state monads.
   A state value is threaded through the application of arrows.

-}

-- ------------------------------------------------------------

module Control.Arrow.ArrowState
    ( ArrowState(..)
    )
where

import Control.Arrow

-- | The interface for accessing and changing the state component.
--
-- Multi parameter classes and functional dependencies are required.

class Arrow a => ArrowState s a | a -> s where

    -- | change the state of a state arrow by applying a function
    -- for computing a new state from the old and the arrow input.
    -- Result is the arrow input

    changeState         :: (s -> b -> s) -> a b b

    -- | access the state with a function using the arrow input
    -- as data for selecting state components.

    accessState         :: (s -> b -> c) -> a b c

    -- | read the complete state, ignore arrow input
    --
    -- definition: @ getState = accessState (\\ s x -> s) @

    getState            :: a b s
    getState            = accessState (\ s _x -> s)
    {-# INLINE getState #-}

    -- | overwrite the old state
    --
    -- definition: @ setState = changeState (\\ s x -> x) @
    setState            :: a s s
    setState            = changeState (\ _s x -> x)     -- changeState (const id)
    {-# INLINE setState #-}

    -- | change state (and ignore input) and return new state
    --
    -- convenience function,
    -- usefull for generating e.g. unique identifiers:
    --
    -- example with SLA state list arrows
    --
    -- > newId :: SLA Int b String
    -- > newId = nextState (+1)
    -- >         >>>
    -- >         arr (('#':) . show)
    -- >
    -- > runSLA 0 (newId <+> newId <+> newId) undefined
    -- >   = ["#1", "#2", "#3"]

    nextState           :: (s -> s) -> a b s
    nextState sf        = changeState (\s -> const (sf s))
                          >>>
                          getState

-- ------------------------------------------------------------