packages feed

salvia-0.1.2: src/Misc/Misc.hs

{-# LANGUAGE TypeSynonymInstances #-}

module Misc.Misc (
    eitherToMaybe
  , bool
  , pMaybe
  , (@@)
  , safeLast
  , guardMaybe
  , ifM
  , split
  , withReverse
  , intersperseS
  , trim
  , normalCase
  , safeRead
  , now
  , later
  , safeHead
  , atomModTVar
  , atomReadTVar
  , atomWithTVar
  ) where

import Control.Applicative 
import Control.Concurrent.STM
import Control.Monad
import Data.Char
import Data.Time.Clock
import Data.Time.LocalTime
import Text.ParserCombinators.Parsec hiding (many, optional, (<|>))

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

bool :: a -> a -> Bool -> a
bool a b c = if c then a else b

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

-- Conversions.

guardMaybe :: Bool -> Maybe ()
guardMaybe = bool (Just ()) Nothing

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

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

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

safeHead :: [a] -> Maybe a
safeHead = safeList head

safeLast :: [a] -> Maybe a
safeLast = safeList last

withReverse :: ([a] -> [b]) -> [a] -> [b]
withReverse f = reverse . f . reverse

trimWith :: (a -> Bool) -> [a] -> [a]
trimWith f = withReverse (dropWhile f) . dropWhile f

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

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

normalCase :: String -> String
normalCase ""     = ""
normalCase (x:xs) = toUpper x : map toLower xs

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

-- ShowS functions.

intersperseS :: ShowS -> [ShowS] -> ShowS
intersperseS _ []  = id
intersperseS _ [s] = s
intersperseS c xs  = foldl1 (\a -> ((a.c).)) xs

-------[ 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)

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

-- Helper function to quickly apply a parser.
(@@) :: GenParser Char () a -> String -> Maybe a
(@@) p b = either (const Nothing) Just $ parse (p <* eof) "" b

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

-- Make parsec both applicative and alternative.
{-instance Applicative (GenParser s a) where
  pure  = return
  (<*>) = ap

instance Alternative (GenParser s a) where
  empty = mzero
  (<|>) = mplus-}

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

later :: Integer -> IO LocalTime
later howlong = do
  zone <- getCurrentTimeZone
  time <- liftM (addUTCTime $ fromInteger howlong) getCurrentTime
  return $ utcToLocalTime zone time

now :: IO LocalTime
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)