packages feed

quickspec-2.2.1: src/QuickSpec/Internal/Utils.hs

-- | Miscellaneous utility functions.
{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE CPP, ScopedTypeVariables #-}
module QuickSpec.Internal.Utils where

import Control.Arrow((&&&))
import Control.Exception
import Control.Spoon
import Data.List(groupBy, sortBy)
#if !MIN_VERSION_base(4,8,0)
import Data.Monoid
#endif
import Data.Ord(comparing)
import System.IO
import qualified Control.Category as Category
import qualified Data.Map.Strict as Map
import Data.Map(Map)
import Language.Haskell.TH.Syntax
import Data.Lens.Light
import Twee.Base hiding (lookup)
import Control.Monad.Trans.State.Strict
import Control.Monad
import Data.Typeable

(#) :: Category.Category cat => cat b c -> cat a b -> cat a c
(#) = (Category..)

key :: Ord a => a -> Lens (Map a b) (Maybe b)
key x = lens (Map.lookup x) (\my m -> Map.alter (const my) x m)

keyDefault :: Ord a => a -> b -> Lens (Map a b) b
keyDefault x y = lens (Map.findWithDefault y x) (\y m -> Map.insert x y m)

reading :: (a -> Lens a b) -> Lens a b
reading f = lens (\x -> getL (f x) x) (\y x -> setL (f x) y x)

fstLens :: Lens (a, b) a
fstLens = lens fst (\x (_, y) -> (x, y))

sndLens :: Lens (a, b) b
sndLens = lens snd (\y (x, _) -> (x, y))

makeLensAs :: Name -> [(String, String)] -> Q [Dec]
makeLensAs ty names =
  nameMakeLens ty (\x -> lookup x names)

repeatM :: Monad m => m a -> m [a]
repeatM = sequence . repeat

partitionBy :: Ord b => (a -> b) -> [a] -> [[a]]
partitionBy value =
  map (map fst) .
  groupBy (\x y -> snd x == snd y) .
  sortBy (comparing snd) .
  map (id &&& value)

collate :: Ord a => ([b] -> c) -> [(a, b)] -> [(a, c)]
collate f = map g . partitionBy fst
  where
    g xs = (fst (head xs), f (map snd xs))

isSorted :: Ord a => [a] -> Bool
isSorted xs = and (zipWith (<=) xs (tail xs))

isSortedBy :: Ord b => (a -> b) -> [a] -> Bool
isSortedBy f xs = isSorted (map f xs)

usort :: Ord a => [a] -> [a]
usort = usortBy compare

usortBy :: (a -> a -> Ordering) -> [a] -> [a]
usortBy f = map head . groupBy (\x y -> f x y == EQ) . sortBy f

sortBy' :: Ord b => (a -> b) -> [a] -> [a]
sortBy' f = map snd . sortBy (comparing fst) . map (\x -> (f x, x))

usortBy' :: Ord b => (a -> b) -> [a] -> [a]
usortBy' f = map snd . usortBy (comparing fst) . map (\x -> (f x, x))

orElse :: Ordering -> Ordering -> Ordering
EQ `orElse` x = x
x  `orElse` _ = x

unbuffered :: IO a -> IO a
unbuffered x = do
  buf <- hGetBuffering stdout
  bracket_
    (hSetBuffering stdout NoBuffering)
    (hSetBuffering stdout buf)
    x

spoony :: Eq a => a -> Maybe a
spoony x = teaspoon ((x == x) `seq` x)

labelM :: Monad m => (a -> m b) -> [a] -> m [(a, b)]
labelM f = mapM (\x -> do { y <- f x; return (x, y) })

#if __GLASGOW_HASKELL__ < 710
isSubsequenceOf :: Ord a => [a] -> [a] -> Bool
[] `isSubsequenceOf` ys = True
(x:xs) `isSubsequenceOf` [] = False
(x:xs) `isSubsequenceOf` (y:ys)
  | x == y = xs `isSubsequenceOf` ys
  | otherwise = (x:xs) `isSubsequenceOf` ys
#endif

appendAt :: Int -> [a] -> [[a]] -> [[a]]
appendAt n xs [] = appendAt n xs [[]]
appendAt 0 xs (ys:yss) = (ys ++ xs):yss
appendAt n xs (ys:yss) = ys:appendAt (n-1) xs yss

-- Should be in Twee.Base.
antiunify :: Ord f => Term f -> Term f -> Term f
antiunify t u =
  build $ evalState (loop t u) (succ (snd (bound t) `max` snd (bound u)), Map.empty)
  where
    loop (App f ts) (App g us)
      | f == g =
        app f <$> zipWithM loop (unpack ts) (unpack us)
    loop (Var x) (Var y)
      | x == y =
        return (var x)
    loop t u = do
      (next, m) <- get
      case Map.lookup (t, u) m of
        Just v -> return (var v)
        Nothing -> do
          put (succ next, Map.insert (t, u) next m)
          return (var next)

{-# INLINE fixpoint #-}
fixpoint :: Eq a => (a -> a) -> a -> a
fixpoint f x = fxp x
  where
    fxp x
      | x == y = x
      | otherwise = fxp y
      where
        y = f x

isResourceLimitException :: SomeException -> Bool
isResourceLimitException ex =
  fromException ex == Just StackOverflow ||
  fromException ex == Just HeapOverflow ||
  isTimeout ex ||
  case fromException ex of
    Just (SomeAsyncException ex) -> isResourceLimitException (SomeException ex)
    Nothing -> False
  where
    -- The Timeout type wasn't exported until GHC 8.10,
    -- otherwise we could just do:
    -- isJust (fromException ex :: Maybe Timeout)
    isTimeout (SomeException ex) =
      tyConModule (typeRepTyCon (typeOf ex)) == "System.Timeout"