2020-05-27 18:02:58 +03:00
|
|
|
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
|
2019-12-14 09:47:38 +03:00
|
|
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
2018-06-27 16:11:32 +03:00
|
|
|
module Hasura.Prelude
|
|
|
|
( module M
|
2020-04-20 11:55:09 +03:00
|
|
|
, alphabet
|
2019-12-14 09:47:38 +03:00
|
|
|
, alphaNumerics
|
2019-02-22 15:25:36 +03:00
|
|
|
, onNothing
|
|
|
|
, onJust
|
|
|
|
, onLeft
|
2019-07-22 15:47:13 +03:00
|
|
|
, choice
|
2020-04-03 03:00:13 +03:00
|
|
|
, afold
|
2019-07-11 08:37:06 +03:00
|
|
|
, bsToTxt
|
|
|
|
, txtToBs
|
2019-08-28 22:27:15 +03:00
|
|
|
, spanMaybeM
|
2020-05-27 18:02:58 +03:00
|
|
|
-- * Efficient coercions
|
|
|
|
, coerce
|
|
|
|
, coerceSet
|
2019-10-18 11:29:47 +03:00
|
|
|
, findWithIndex
|
2019-11-20 21:21:30 +03:00
|
|
|
, mapFromL
|
2020-01-16 04:56:57 +03:00
|
|
|
-- * Measuring and working with moments and durations
|
|
|
|
, withElapsedTime
|
|
|
|
, startTimer
|
|
|
|
, module Data.Time.Clock.Units
|
2018-06-27 16:11:32 +03:00
|
|
|
) where
|
|
|
|
|
2019-11-27 01:49:42 +03:00
|
|
|
import Control.Applicative as M (Alternative (..))
|
|
|
|
import Control.Arrow as M (first, second, (&&&), (***), (<<<), (>>>))
|
|
|
|
import Control.DeepSeq as M (NFData, deepseq, force)
|
|
|
|
import Control.Monad.Base as M
|
|
|
|
import Control.Monad.Except as M
|
|
|
|
import Control.Monad.Identity as M
|
|
|
|
import Control.Monad.Reader as M
|
|
|
|
import Control.Monad.State.Strict as M
|
2020-04-15 15:03:13 +03:00
|
|
|
import Control.Monad.Writer.Strict as M (MonadWriter (..), WriterT (..),
|
|
|
|
execWriterT, runWriterT)
|
2020-05-13 15:33:16 +03:00
|
|
|
import Data.Align as M (Semialign (align, alignWith))
|
2019-11-27 01:49:42 +03:00
|
|
|
import Data.Bool as M (bool)
|
|
|
|
import Data.Data as M (Data (..))
|
|
|
|
import Data.Either as M (lefts, partitionEithers, rights)
|
|
|
|
import Data.Foldable as M (asum, foldrM, for_, toList, traverse_)
|
|
|
|
import Data.Function as M (on, (&))
|
|
|
|
import Data.Functor as M (($>), (<&>))
|
|
|
|
import Data.Hashable as M (Hashable)
|
|
|
|
import Data.HashMap.Strict as M (HashMap)
|
|
|
|
import Data.HashSet as M (HashSet)
|
|
|
|
import Data.List as M (find, findIndex, foldl', group,
|
|
|
|
intercalate, intersect, lookup, sort,
|
|
|
|
sortBy, sortOn, union, unionBy, (\\))
|
2020-04-03 03:00:13 +03:00
|
|
|
import Data.List.NonEmpty as M (NonEmpty (..))
|
2019-11-27 01:49:42 +03:00
|
|
|
import Data.Maybe as M (catMaybes, fromMaybe, isJust, isNothing,
|
|
|
|
listToMaybe, mapMaybe, maybeToList)
|
2020-04-03 03:00:13 +03:00
|
|
|
import Data.Monoid as M (getAlt)
|
2019-11-27 01:49:42 +03:00
|
|
|
import Data.Ord as M (comparing)
|
|
|
|
import Data.Semigroup as M (Semigroup (..))
|
|
|
|
import Data.Sequence as M (Seq)
|
|
|
|
import Data.String as M (IsString)
|
|
|
|
import Data.Text as M (Text)
|
|
|
|
import Data.These as M (These (..), fromThese, mergeThese,
|
|
|
|
mergeTheseWith, these)
|
2020-04-03 03:00:13 +03:00
|
|
|
import Data.Time.Clock.Units
|
2019-11-27 01:49:42 +03:00
|
|
|
import Data.Traversable as M (for)
|
|
|
|
import Data.Word as M (Word64)
|
|
|
|
import GHC.Generics as M (Generic)
|
|
|
|
import Prelude as M hiding (fail, init, lookup)
|
2019-12-14 09:47:38 +03:00
|
|
|
import Test.QuickCheck.Arbitrary.Generic as M
|
2019-11-27 01:49:42 +03:00
|
|
|
import Text.Read as M (readEither, readMaybe)
|
2019-02-22 15:25:36 +03:00
|
|
|
|
2019-11-27 01:49:42 +03:00
|
|
|
import qualified Data.ByteString as B
|
2020-05-27 18:02:58 +03:00
|
|
|
import Data.Coerce
|
2019-11-27 01:49:42 +03:00
|
|
|
import qualified Data.HashMap.Strict as Map
|
2020-05-27 18:02:58 +03:00
|
|
|
import qualified Data.Set as Set
|
2019-12-14 09:47:38 +03:00
|
|
|
import qualified Data.Text as T
|
2019-11-27 01:49:42 +03:00
|
|
|
import qualified Data.Text.Encoding as TE
|
|
|
|
import qualified Data.Text.Encoding.Error as TE
|
2020-01-16 04:56:57 +03:00
|
|
|
import qualified GHC.Clock as Clock
|
2019-12-14 09:47:38 +03:00
|
|
|
import qualified Test.QuickCheck as QC
|
2020-05-27 18:02:58 +03:00
|
|
|
import Unsafe.Coerce
|
2019-12-14 09:47:38 +03:00
|
|
|
|
2020-04-20 11:55:09 +03:00
|
|
|
alphabet :: String
|
|
|
|
alphabet = ['a'..'z'] ++ ['A'..'Z']
|
|
|
|
|
2019-12-14 09:47:38 +03:00
|
|
|
alphaNumerics :: String
|
2020-04-20 11:55:09 +03:00
|
|
|
alphaNumerics = alphabet ++ "0123456789"
|
2019-12-14 09:47:38 +03:00
|
|
|
|
|
|
|
instance Arbitrary Text where
|
|
|
|
arbitrary = T.pack <$> QC.listOf (QC.elements alphaNumerics)
|
2019-07-11 08:37:06 +03:00
|
|
|
|
2019-02-22 15:25:36 +03:00
|
|
|
onNothing :: (Monad m) => Maybe a -> m a -> m a
|
|
|
|
onNothing m act = maybe act return m
|
|
|
|
|
|
|
|
onJust :: (Monad m) => Maybe a -> (a -> m ()) -> m ()
|
|
|
|
onJust m action = maybe (return ()) action m
|
|
|
|
|
|
|
|
onLeft :: (Monad m) => Either e a -> (e -> m a) -> m a
|
|
|
|
onLeft e f = either f return e
|
|
|
|
|
2019-07-22 15:47:13 +03:00
|
|
|
choice :: (Alternative f) => [f a] -> f a
|
2019-08-28 22:27:15 +03:00
|
|
|
choice = asum
|
2019-07-22 15:47:13 +03:00
|
|
|
|
2020-04-03 03:00:13 +03:00
|
|
|
afold :: (Foldable t, Alternative f) => t a -> f a
|
|
|
|
afold = getAlt . foldMap pure
|
|
|
|
|
2019-07-11 08:37:06 +03:00
|
|
|
bsToTxt :: B.ByteString -> Text
|
|
|
|
bsToTxt = TE.decodeUtf8With TE.lenientDecode
|
2019-02-22 15:25:36 +03:00
|
|
|
|
2019-07-11 08:37:06 +03:00
|
|
|
txtToBs :: Text -> B.ByteString
|
|
|
|
txtToBs = TE.encodeUtf8
|
2019-08-28 22:27:15 +03:00
|
|
|
|
|
|
|
-- Like 'span', but monadic and with a function that produces 'Maybe' instead of 'Bool'
|
|
|
|
spanMaybeM
|
|
|
|
:: (Foldable f, Monad m)
|
|
|
|
=> (a -> m (Maybe b)) -> f a -> m ([b], [a])
|
|
|
|
spanMaybeM f = go . toList
|
|
|
|
where
|
|
|
|
go [] = pure ([], [])
|
|
|
|
go l@(x:xs) = f x >>= \case
|
|
|
|
Just y -> first (y:) <$> go xs
|
|
|
|
Nothing -> pure ([], l)
|
2019-10-18 11:29:47 +03:00
|
|
|
|
2020-05-27 18:02:58 +03:00
|
|
|
-- | Efficiently coerce a set from one type to another.
|
|
|
|
--
|
|
|
|
-- This has the same safety properties as 'Set.mapMonotonic', and is equivalent
|
|
|
|
-- to @Set.mapMonotonic coerce@ but is more efficient. This is safe to use when
|
|
|
|
-- both @a@ and @b@ have automatically derived @Ord@ instances.
|
|
|
|
--
|
|
|
|
-- https://stackoverflow.com/q/57963881/176841
|
|
|
|
coerceSet :: Coercible a b=> Set.Set a -> Set.Set b
|
|
|
|
coerceSet = unsafeCoerce
|
|
|
|
|
2019-10-18 11:29:47 +03:00
|
|
|
findWithIndex :: (a -> Bool) -> [a] -> Maybe (a, Int)
|
|
|
|
findWithIndex p l = do
|
|
|
|
v <- find p l
|
|
|
|
i <- findIndex p l
|
|
|
|
pure (v, i)
|
2019-11-20 21:21:30 +03:00
|
|
|
|
|
|
|
-- TODO: Move to Data.HashMap.Strict.Extended; rename to fromListWith?
|
|
|
|
mapFromL :: (Eq k, Hashable k) => (a -> k) -> [a] -> Map.HashMap k a
|
|
|
|
mapFromL f = Map.fromList . map (\v -> (f v, v))
|
2020-01-16 04:56:57 +03:00
|
|
|
|
|
|
|
-- | Time an IO action, returning the time with microsecond precision. The
|
|
|
|
-- result of the input action will be evaluated to WHNF.
|
|
|
|
--
|
|
|
|
-- The result 'DiffTime' is guarenteed to be >= 0.
|
|
|
|
withElapsedTime :: MonadIO m=> m a -> m (DiffTime, a)
|
|
|
|
withElapsedTime ma = do
|
|
|
|
bef <- liftIO Clock.getMonotonicTimeNSec
|
|
|
|
!a <- ma
|
|
|
|
aft <- liftIO Clock.getMonotonicTimeNSec
|
|
|
|
let !dur = nanoseconds $ fromIntegral (aft - bef)
|
|
|
|
return (dur, a)
|
|
|
|
|
|
|
|
-- | Start timing and return an action to return the elapsed time since 'startTimer' was called.
|
|
|
|
--
|
|
|
|
-- @
|
|
|
|
-- timer <- startTimer
|
|
|
|
-- someStuffToTime
|
|
|
|
-- elapsed <- timer
|
|
|
|
-- moreStuff
|
|
|
|
-- elapsedBoth <- timer
|
|
|
|
-- @
|
|
|
|
startTimer :: (MonadIO m, MonadIO n)=> m (n DiffTime)
|
|
|
|
startTimer = do
|
|
|
|
!bef <- liftIO Clock.getMonotonicTimeNSec
|
|
|
|
return $ do
|
|
|
|
aft <- liftIO Clock.getMonotonicTimeNSec
|
|
|
|
return $ nanoseconds $ fromIntegral (aft - bef)
|