packages feed

alms-0.5.0: src/Util.hs

-- | Utility functions
{-# LANGUAGE
      CPP,
      FlexibleContexts
      #-}
module Util (
  -- * List combinators
  -- ** Shallow mapping
  mapCons, mapHead, mapTail,
  -- ** Two-list versions
  foldl2, foldr2, all2, any2,
  -- ** Monadic version
  foldrM, anyM, allM, anyM2, allM2,
  concatMapM,
  -- ** Applicative versions
  mapA,
  -- ** Unfold with an accumulator
  unscanr, unscanl,
  -- ** Map in CPS
  mapCont, mapCont_,
  -- ** Monad generalization of map and sequence
  GSequence(..),

  -- * More convenience
  -- ** Maybe functions
  (?:),
  -- ** Either funtions
  isLeft, isRight,
  -- ** List functions
  splitBy,
  -- ** Monomorphic @ord@ and @chr@
  char2integer, integer2char,
  -- ** For defining 'Ord'
  thenCmp,
  -- ** Versions of fmap
  (>>!),
  (<$$>), (<$$$>), (<$$$$>), (<$$$$$>),

  -- * Re-exports
  module Data.Maybe,
  module Control.Arrow,
  module Control.Monad,
  module Control.Applicative
) where

import Data.Char (chr, ord)
import Data.Maybe
import Control.Arrow hiding (loop, (<+>))
import Control.Monad
import Control.Applicative (Applicative(..), (<$>), (<$), (<**>))

#if PARSEC_VERSION == 2

import Text.ParserCombinators.Parsec (GenParser)
-- | Parsec parsers are Applicatives, which lets us write slightly
--   more pleasant, non-monadic-looking parsers
instance Applicative (GenParser a b) where
  pure  = return
  (<*>) = ap
#endif

-- | Right-associative monadic fold
foldrM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
foldrM _ z []     = return z
foldrM f z (b:bs) = foldrM f z bs >>= flip f b

-- | Like 'Prelude.any' with a monadic predicate
anyM :: Monad m => (a -> m Bool) -> [a] -> m Bool
anyM p (x:xs) = do
  b <- p x
  if b
    then return True
    else anyM p xs
anyM _    _      = return False

-- | Like 'Prelude.all' with a monadic predicate
allM :: Monad m => (a -> m Bool) -> [a] -> m Bool
allM p = liftM not . anyM (liftM not . p)

-- | Two-list, monadic 'any'
anyM2 :: Monad m => (a -> b -> m Bool) -> [a] -> [b] -> m Bool
anyM2 p as bs = anyM (uncurry p) (zip as bs)

-- | Two-list, monadic 'all'
allM2 :: Monad m => (a -> b -> m Bool) -> [a] -> [b] -> m Bool
allM2 p as bs = allM (uncurry p) (zip as bs)

concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
concatMapM f xs = concat `liftM` mapM f xs

-- | Map an applicative over a list
mapA         :: Applicative t => (a -> t b) -> [a] -> t [b]
mapA _ []     = pure []
mapA f (x:xs) = (:) <$> f x <*> mapA f xs

-- | Apply one function to the head of a list and another to the
--   tail
mapCons :: (a -> b) -> ([a] -> [b]) -> [a] -> [b]
mapCons _  _  []     = []
mapCons fh ft (x:xs) = fh x : ft xs

-- | Map a function over only the first element of a list
mapHead  :: (a -> a) -> [a] -> [a]
mapHead f = mapCons f id

-- | Map a function over all but the first element of a list
mapTail  :: (a -> a) -> [a] -> [a]
mapTail   = mapCons id . map

-- | Left-associative fold over two lists
foldl2 :: (c -> a -> b -> c) -> c -> [a] -> [b] -> c
foldl2 f z (x:xs) (y:ys) = foldl2 f (f z x y) xs ys
foldl2 _ z _      _      = z

-- | Right-associative fold over two lists
foldr2 :: (a -> b -> c -> c) -> c -> [a] -> [b] -> c
foldr2 f z (x:xs) (y:ys) = f x y (foldr2 f z xs ys)
foldr2 _ z _      _      = z

-- | Two-list 'all'
all2 :: (a -> b -> Bool) -> [a] -> [b] -> Bool
all2 p xs ys = and (zipWith p xs ys)

-- | Two-list 'any'
any2 :: (a -> b -> Bool) -> [a] -> [b] -> Bool
any2 p xs ys = or (zipWith p xs ys)

-- | The ASCII value of a character
char2integer :: Char -> Integer
char2integer  = fromIntegral . ord

-- | The character of an ASCII value
integer2char :: Integer -> Char
integer2char  = chr . fromIntegral

-- | Break a list where the given preducate answers true
splitBy :: (a -> Bool) -> [a] -> [[a]]
splitBy _ [] = []
splitBy p xs = let (ys, zs) = break p xs 
                in ys : splitBy p (drop 1 zs)

-- | Maybe cons, maybe not
(?:) :: Maybe a -> [a] -> [a]
Nothing ?: xs = xs
Just x  ?: xs = x : xs

infixr 5 ?:

isLeft, isRight :: Either a b -> Bool
isLeft (Left _)   = True
isLeft _          = False
isRight (Right _) = True
isRight _         = False

-- | Unfold a list, left-to-right, returning the final state
unscanr :: (b -> Maybe (a, b)) -> b -> ([a], b)
unscanr f b = case f b of
  Just (a, b') -> (a : fst rest, snd rest) where rest = unscanr f b'
  Nothing      -> ([], b)

-- | Unfold a list, right-to-left, returning the final state
unscanl :: (b -> Maybe (a, b)) -> b -> ([a], b)
unscanl f = loop [] where
  loop acc b = case f b of
    Just (a, b') -> loop (a : acc) b'
    Nothing      -> (acc, b)

-- | To combine two 'Ordering's in lexigraphic order
thenCmp :: Ordering -> Ordering -> Ordering
thenCmp EQ k2 = k2
thenCmp k1 _  = k1
infixr 4 `thenCmp`

-- | 2nd order fmap
(<$$>) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
(<$$>)  = (<$>) . (<$>)

-- | 3rd order fmap
(<$$$>) :: (Functor f, Functor g, Functor h) =>
           (a -> b) -> f (g (h a)) -> f (g (h b))
(<$$$>)  = (<$$>) . (<$>)

-- | 4th order fmap
(<$$$$>) :: (Functor f, Functor g, Functor h, Functor j) =>
            (a -> b) -> f (g (h (j a))) -> f (g (h (j b)))
(<$$$$>)  = (<$$$>) . (<$>)

-- | 5th order fmap
(<$$$$$>) :: (Functor f, Functor g, Functor h, Functor j, Functor k) =>
             (a -> b) -> f (g (h (j (k a)))) -> f (g (h (j (k b))))
(<$$$$$>)  = (<$$$$>) . (<$>)

infixl 4 <$$>, <$$$>, <$$$$>, <$$$$$>

-- | @flip fmap@
(>>!) :: Functor f => f a -> (a -> b) -> f b
(>>!)  = flip fmap

infixl 1 >>!

-- | CPS version of 'map'
mapCont :: (a -> (b -> r) -> r) -> [a] -> ([b] -> r) -> r
mapCont _ []     k = k []
mapCont f (x:xs) k = f x $ \x' ->
                     mapCont f xs $ \xs' ->
                       k (x' : xs')

-- | CPS version of 'map_'
mapCont_ :: (a -> r -> r) -> [a] -> r -> r
mapCont_ _ []     k = k
mapCont_ f (x:xs) k = f x $ mapCont_ f xs $ k

-- | Generalize 'map' and 'sequence' to a few other monads
class GSequence m where
  gsequence   :: Monad m' => m (m' a) -> m' (m a)
  gsequence_  :: Monad m' => m (m' a) -> m' ()
  gsequence_ m = gsequence m >> return ()
  gmapM       :: (Monad m, Monad m') => (a -> m' b) -> m a -> m' (m b)
  gmapM f      = gsequence . liftM f
  gmapM_      :: (Monad m, Monad m') => (a -> m' b) -> m a -> m' ()
  gmapM_ f     = gsequence_ . liftM f
  gforM       :: (Monad m, Monad m') => m a -> (a -> m' b) -> m' (m b)
  gforM        = flip gmapM
  gforM_      :: (Monad m, Monad m') => m a -> (a -> m' b) -> m' ()
  gforM_       = flip gmapM_

instance GSequence [] where
  gsequence  = sequence
  gsequence_ = sequence_
  gmapM      = mapM
  gmapM_     = mapM_

instance GSequence Maybe where
  gsequence  = maybe (return Nothing) (liftM return)
  gsequence_ = maybe (return ()) (>> return ())