packages feed

orchid-0.0.6: src/Misc/Commons.hs

{-# LANGUAGE TypeSynonymInstances #-}

module Misc.Commons where

import Control.Applicative 
import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad (liftM, liftM2, ap, mzero, mplus)
import Control.Monad.Identity
import Data.Char
import Data.Time.Clock
import Data.Time.LocalTime
import Test.QuickCheck
import Text.ParserCombinators.Parsec hiding (many, optional, (<|>))
import Text.ParserCombinators.Parsec.Pos (updatePosChar)
import Text.Printf (printf)
import Data.List (partition, tails, inits, isPrefixOf)
import qualified Data.Map as Map

-------[ common utilities ]----------------------------------------------------

infixl 8 .$
(.$) :: (a -> b) -> a -> b
(.$) a b = a b

dup a = (a, a)
branch f g a = (f a, g a)
swap (a, b) = (b, a)

bool a b c = if c then a else b

-- Test eithers.
left  (Left  _) = True
left  _         = False
right (Right _) = True
right _         = False

partitionEither :: [Either a b] -> ([a], [b])
partitionEither xs =
  let (a, b) = partition left xs in
    (map (\(Left  x) -> x) a,
     map (\(Right x) -> x) b)

safeRead :: Read a => String -> Maybe a
safeRead s = case reads s of
  [(x, "")] -> Just x
  _         -> Nothing

intRead :: String -> Maybe Int
intRead = safeRead

-- Conversions.

boolM = bool (Just ()) Nothing

eitherToMaybe :: Either a b -> Maybe b
eitherToMaybe = either (const Nothing) Just

listToMaybe :: [a] -> Maybe [a]
listToMaybe [] = Nothing
listToMaybe xs = Just xs

-------[ list utilities ]------------------------------------------------------

safeList :: ([a] -> b) -> [a] -> Maybe b
safeList _ [] = Nothing
safeList f xs = Just $ f xs

safeHead = safeList head
safeLast = safeList last

withReverse f = reverse . f . reverse

trimWith f = withReverse (dropWhile f) . dropWhile f

snoc a b = a ++ [b]

split :: Eq a => a -> [a] -> [[a]]
split c = splitWith (==c)

splitWith :: (a -> Bool) -> [a] -> [[a]]
splitWith f xs =
  case span (not . f) (dropWhile f xs) of
   ([], []) -> []
   (a,  []) -> [a]
   (a,  b)  -> a : splitWith f b

splitsWith :: Eq a => [a] -> [a] -> Maybe ([a], [a])
splitsWith n h = 
    fmap (fmap (drop (length n)))
  . safeHead
  . filter (isPrefixOf n . snd)
  . uncurry zip
  . fmap2 inits tails
  $ dup h

-------[ map utilities ]-------------------------------------------------------

(<->) a b c = Map.insert a b c

(<--) a b = Map.insert a b Map.empty

-------[ text manipulation ]---------------------------------------------------

normalCase (x:xs) = toUpper x : map toLower xs
upperCase         = map toUpper
lowerCase         = map toLower

-- Trim all heading and trailing whitespace.
trim = trimWith (`elem` " \t\n\r")

-- ShowS functions.

ss                 = showString
intersperseS c []  = id
intersperseS c [s] = s
intersperseS c xs  = foldl1 (\a -> ((a.c).)) xs
concatS            = foldl (.) id
concatMapS f       = concatS . map f
unlinesS           = concatMapS (. ss "\n")
eolS               = ss "\n"


-------[ second arity functor ]------------------------------------------------

class Functor2 f where
  fmap2 :: (a -> c) -> (b -> d) -> f a b -> f c d

instance Functor2 (,) where
  fmap2 f g (a, b) = (f a, g b)

instance Functor2 Either where
  fmap2 f _ (Left  a) = Left  (f a)
  fmap2 _ g (Right b) = Right (g b)

-------[ monadic expressions ]-------------------------------------------------

ifM :: Monad m => m Bool -> (a -> a) -> a -> m a
ifM t f g = t >>= (\b -> return $ if b then f g else g)

-- Thread a value through monadic expression.
constM c = (>> return c)

-------[ parsec extensions ]---------------------------------------------------

-- Helper function to quickly apply a parser.
(@@) p b = either (const Nothing) Just $ parse (p <* eof) "" b
(@!) p b = parse (p <* eof) "" b

-- Apply a parser for a minimum of `n' times.
pMin n p = (++) <$> count n p <*> many p

-- Apply a parser for a maximum of `n' times.
pMax n p | n <= 0    = return []
         | otherwise = option [] $ liftM2 (:) p (pMax (n-1) p)

-- Apply a parser for a minimum of `n' and a maximum of 'm' times.
pRange n m p = liftM2 (++) (count n p) (pMax (m-n) p)

-- Option parser with maybe result.
pMaybe :: GenParser tok st a -> GenParser tok st (Maybe a)
pMaybe = option Nothing . liftM Just

-------[ testing ]-------------------------------------------------------------

-- We will abuse QuickCheck for unit testing purposes.
unitCheck :: (Testable a) => a -> IO ()
unitCheck = quickCheck

testAll xs = mapM_ (\(s,a) -> printf "%-50s: " s >> a) xs

-------[ time utils ]----------------------------------------------------------

later howlong = do
  zone <- getCurrentTimeZone
  time <- liftM (addUTCTime $ fromInteger howlong) getCurrentTime
  return $ utcToLocalTime zone time

now = later 0

-------[ concurrency utils ]---------------------------------------------------

atomModTVar :: (a -> a) -> TVar a -> IO a
atomModTVar f v = atomically $ do
  t <- readTVar v
  writeTVar v (f t)
  return (f t)

atomReadTVar :: TVar a -> IO a
atomReadTVar = atomically . readTVar

atomWithTVar :: (a -> b) -> TVar a -> IO b
atomWithTVar f v = atomically
  $ liftM f (readTVar v)