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)