-- ------------------------------------------------------------ {- | Module : Control.Arrow.ArrowIf Copyright : Copyright (C) 2005 Uwe Schmidt License : MIT Maintainer : Uwe Schmidt (uwe\@fh-wedel.de) Stability : experimental Portability: portable Conditionals for List Arrows This module defines conditional combinators for list arrows. The empty list as result represents False, none empty lists True. -} -- ------------------------------------------------------------ module Control.Arrow.ArrowIf ( module Control.Arrow.ArrowIf ) where import Control.Arrow import Control.Arrow.ArrowList import Data.List ( partition ) -- ------------------------------------------------------------ -- | The interface for arrows as conditionals. -- -- Requires list arrows because False is represented as empty list, True as none empty lists. -- -- Only 'ifA' and 'orElse' don't have default implementations class ArrowList a => ArrowIf a where -- | if lifted to arrows ifA :: a b c -> a b d -> a b d -> a b d -- | shortcut: @ ifP p = ifA (isA p) @ ifP :: (b -> Bool) -> a b d -> a b d -> a b d ifP p = ifA (isA p) {-# INLINE ifP #-} -- | negation: @ neg f = ifA f none this @ neg :: a b c -> a b b neg f = ifA f none this {-# INLINE neg #-} -- | @ f \`when\` g @ : when the predicate g holds, f is applied, else the identity filter this when :: a b b -> a b c -> a b b f `when` g = ifA g f this {-# INLINE when #-} -- | shortcut: @ f \`whenP\` p = f \`when\` (isA p) @ whenP :: a b b -> (b -> Bool) -> a b b f `whenP` g = ifP g f this {-# INLINE whenP #-} -- | @ f \`whenNot\` g @ : when the predicate g does not hold, f is applied, else the identity filter this whenNot :: a b b -> a b c -> a b b f `whenNot` g = ifA g this f {-# INLINE whenNot #-} -- | like 'whenP' whenNotP :: a b b -> (b -> Bool) -> a b b f `whenNotP` g = ifP g this f {-# INLINE whenNotP #-} -- | @ g \`guards\` f @ : when the predicate g holds, f is applied, else none guards :: a b c -> a b d -> a b d f `guards` g = ifA f g none {-# INLINE guards #-} -- | like 'whenP' guardsP :: (b -> Bool) -> a b d -> a b d f `guardsP` g = ifP f g none {-# INLINE guardsP #-} -- | shortcut for @ f `guards` this @ filterA :: a b c -> a b b filterA f = ifA f this none {-# INLINE filterA #-} -- | @ f \`containing\` g @ : keep only those results from f for which g holds -- -- definition: @ f \`containing\` g = f >>> g \`guards\` this @ containing :: a b c -> a c d -> a b c f `containing` g = f >>> g `guards` this {-# INLINE containing #-} -- | @ f \`notContaining\` g @ : keep only those results from f for which g does not hold -- -- definition: @ f \`notContaining\` g = f >>> ifA g none this @ notContaining :: a b c -> a c d -> a b c f `notContaining` g = f >>> ifA g none this {-# INLINE notContaining #-} -- | @ f \`orElse\` g @ : directional choice: if f succeeds, the result of f is the result, else g is applied orElse :: a b c -> a b c -> a b c -- | generalisation of 'orElse' for multi way branches like in case expressions. -- -- An auxiliary data type 'IfThen' with an infix constructor ':->' is used for writing multi way branches -- -- example: @ choiceA [ p1 :-> e1, p2 :-> e2, this :-> default ] @ choiceA :: [IfThen (a b c) (a b d)] -> a b d choiceA = foldr ifA' none where ifA' (g :-> f) = ifA g f -- | tag a value with Left or Right, if arrow has success, input is tagged with Left, else with Right tagA :: a b c -> a b (Either b b) tagA p = ifA p (arr Left) (arr Right) -- | split a list value with an arrow and returns a pair of lists. -- This is the arrow version of 'span'. The arrow is deterministic. -- -- example: @ runLA (spanA (isA (\/= \'-\'))) \"abc-def\" @ gives @ [(\"abc\",\"-def\")] @ as result spanA :: a b b -> a [b] ([b],[b]) spanA p = ifA ( arrL (take 1) >>> p ) ( arr head &&& (arr tail >>> spanA p) >>> arr (\ ~(x, ~(xs,ys)) -> (x : xs, ys)) ) ( arr (\ l -> ([],l)) ) -- | partition a list of values into a pair of lists -- -- This is the arrow Version of 'Data.List.partition' partitionA :: a b b -> a [b] ([b],[b]) partitionA p = listA ( arrL id >>> tagA p ) >>^ ( (\ ~(l1, l2) -> (unTag l1, unTag l2) ) . partition (isLeft) ) where isLeft (Left _) = True isLeft _ = False unTag = map (either id id) -- ------------------------------------------------------------ -- | an auxiliary data type for 'choiceA' data IfThen a b = a :-> b -- ------------------------------------------------------------