arrow-list-0.6.1.4: src/Control/Arrow/ArrowList.hs
{-# LANGUAGE TypeOperators, Arrows #-}
{- |
The `ArrowList' type class, and a collection of list arrow related functions.
This typeclass can be used to embed functions producing multiple outputs into a
an arrow.
-}
module Control.Arrow.ArrowList
(
-- * ArrowList type class.
ArrowList (..)
-- * Creating list arrows.
, unlist
, unite
, none
, concatA
-- * Collecting the results.
, list
, empty
-- * Conditional and filter arrows.
, isA
, ifA
, when
, guards
, filterA
, notA
, orElse
-- * Optionality.
, maybeL
, optional
)
where
import Control.Monad hiding (when)
import Control.Category
import Control.Arrow
import Prelude hiding ((.), id)
-- | The `ArrowList' class represents two possible actions:
--
-- 1. Lifting functions from one value to a list of values into a list arrow.
--
-- 2. Mapping a function over the result list of a list arrow.
class Arrow arr => ArrowList arr where
arrL :: (a -> [b]) -> a `arr` b
mapL :: ([b] -> [c]) -> (a `arr` b) -> (a `arr` c)
-- | Create a list arrow of an input list.
unlist :: ArrowList arr => [b] `arr` b
unlist = arrL id
-- | Take the output of an arrow producing two results and concatenate them
-- into the result of the list arrow.
unite :: ArrowList arr => (a `arr` (b, b)) -> a `arr` b
unite = mapL (concatMap (\(a, b) -> [a, b]))
-- | Ignore the input and produce no results. Like `zeroArrow'.
none :: ArrowList arr => a `arr` b
none = arrL (const [])
-- | Collect the results of applying multiple arrows to the same input.
concatA :: ArrowPlus arr => [a `arr` b] -> a `arr` b
concatA = foldr (<+>) zeroArrow
-- | Collect the entire results of an list arrow as a singleton value in the
-- result list.
list :: ArrowList arr => (a `arr` b) -> a `arr` [b]
list = mapL return
-- | Returns a `Bool' indicating whether the input arrow produce any results.
empty :: ArrowList arr => (a `arr` b) -> a `arr` Bool
empty = mapL (\xs -> [if null xs then True else False])
-- | Create a filtering list arrow by mapping a predicate function over the
-- input. When the predicate returns `True' the input will be returned in the
-- output list, when `False' the empty list is returned.
isA :: ArrowList arr => (a -> Bool) -> a `arr` a
isA f = arrL (\a -> if f a then [a] else [])
-- | Use the result a list arrow as a conditional, like an if-then-else arrow.
-- When the first arrow produces any results the /then/ arrow will be used,
-- when the first arrow produces no results the /else/ arrow will be used.
ifA :: (ArrowList arr, ArrowChoice arr)
=> (a `arr` c) -- ^ Arrow used as condition.
-> (a `arr` b) -- ^ Arrow to use when condition has results.
-> (a `arr` b) -- ^ Arrow to use when condition has no results.
-> a `arr` b
ifA c t e = proc i -> do x <- empty c -< i; if x then e -< i else t -< i
-- | Apply a list arrow only when a conditional arrow produces any results.
-- When the conditional produces no results the output arrow /behaves like the identity/.
-- The /second/ input arrow is used as the conditional, this allow
-- you to write: @ a \`when\` c @
infix 8 `when`
when :: (ArrowList arr, ArrowChoice arr)
=> (a `arr` a) -- ^ The arrow to apply,
-> (a `arr` b) -- ^ when this conditional holds.
-> a `arr` a
when a c = ifA c a id
-- | Apply a list arrow only when a conditional arrow produces any results.
-- When the conditional produces no results the output arrow /produces no results/.
-- The /first/ input arrow is used as the conditional, this allow you
-- to write: @ c \`guards\` a @
infix 8 `guards`
guards :: (ArrowList arr, ArrowChoice arr)
=> (a `arr` c) -- ^ When this condition holds,
-> (a `arr` b) -- ^ then apply this arrow.
-> a `arr` b
guards c a = ifA c a none
-- | Filter the results of an arrow with a predicate arrow, when the filter
-- condition produces results the input is accepted otherwise it is excluded.
filterA :: (ArrowChoice arr, ArrowList arr) => (a `arr` c) -> a `arr` a
filterA c = ifA c id none
-- | Negation list arrow. Only accept the input when the condition produces no
-- output.
notA :: (ArrowList arr, ArrowChoice arr) => (a `arr` c) -> a `arr` a
notA c = ifA c none id
-- | Apply the input arrow, when the arrow does not produces any results the
-- second fallback arrow is applied.
-- Likely written infix like this @ a \`orElse\` b @
infix 8 `orElse`
orElse :: (ArrowList arr, ArrowChoice arr) => (a `arr` b) -> (a `arr` b) -> a `arr` b
orElse a = ifA a a
-- | Map a `Maybe' input to a list output. When the Maybe is a `Nothing' an
-- empty list will be returned, `Just' will result in a singleton list.
maybeL :: ArrowList arr => Maybe a `arr` a
maybeL = arrL (maybe [] return)
-- | Apply a list arrow, when there are no results a `Nothing' will be
-- returned, otherwise the results will be wrapped in a `Just'. This function
-- always produces result.
optional :: (ArrowChoice arr, ArrowList arr) => (a `arr` b) -> a `arr` Maybe b
optional a = ifA a (arr Just . a) (arr (const Nothing))