calamity-0.2.0.0: Calamity/Internal/Utils.hs
-- | Internal utilities and instances
module Calamity.Internal.Utils (
whileMFinalIO,
untilJustFinalIO,
whenJust,
whenM,
unlessM,
lastMaybe,
leftToMaybe,
rightToMaybe,
justToEither,
(<<$>>),
(<<*>>),
(<.>),
debug,
info,
Calamity.Internal.Utils.error,
swap,
DefaultingMap (..),
AesonVector (..),
CalamityFromStringShow (..),
MaybeNull (..),
) where
import Calamity.Internal.RunIntoIO
import Calamity.Types.LogEff
import Control.Applicative
import Data.Aeson
import Data.Aeson.Encoding (null_)
import Data.Default.Class
import qualified Data.Map as M
import Data.Semigroup (Last (..))
import Data.Text
import qualified Data.Vector.Unboxing as VU
import qualified DiPolysemy as Di
import qualified Polysemy as P
import TextShow
import GHC.Generics
import qualified TextShow.Generic as TSG
{- | Like whileM, but stateful effects are not preserved to mitigate memory leaks
This means Polysemy.Error won't work to break the loop, etc.
Instead, Error/Alternative will just result in the loop quitting.
-}
whileMFinalIO :: P.Member (P.Final IO) r => P.Sem r Bool -> P.Sem r ()
whileMFinalIO action = do
action' <- runSemToIO action
P.embedFinal $ go action'
where
go action' = do
r <- action'
case r of
Just True ->
go action'
_ ->
pure ()
{- | Like untilJust, but stateful effects are not preserved to mitigate memory leaks
This means Polysemy.Error won't work to break the loop, etc.
Instead, Error/Alternative will just result in another loop.
-}
untilJustFinalIO :: P.Member (P.Final IO) r => P.Sem r (Maybe a) -> P.Sem r a
untilJustFinalIO action = do
action' <- runSemToIO action
P.embedFinal $ go action'
where
go action' = do
r <- action'
case r of
Just (Just a) ->
pure a
_ ->
go action'
whenJust :: Applicative m => Maybe a -> (a -> m ()) -> m ()
whenJust = flip $ maybe (pure ())
whenM :: Monad m => m Bool -> m () -> m ()
whenM p m =
p >>= \case
True -> m
False -> pure ()
unlessM :: Monad m => m Bool -> m () -> m ()
unlessM = whenM . (not <$>)
lastMaybe :: Maybe a -> Maybe a -> Maybe a
lastMaybe l r = getLast <$> fmap Last l <> fmap Last r
leftToMaybe :: Either e a -> Maybe e
leftToMaybe (Left x) = Just x
leftToMaybe _ = Nothing
rightToMaybe :: Either e a -> Maybe a
rightToMaybe (Right x) = Just x
rightToMaybe _ = Nothing
justToEither :: Maybe e -> Either e ()
justToEither (Just x) = Left x
justToEither _ = Right ()
(<<$>>) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
(<<$>>) = fmap . fmap
infixl 4 <<$>>
(<<*>>) :: (Applicative f, Applicative g) => f (g (a -> b)) -> f (g a) -> f (g b)
(<<*>>) = liftA2 (<*>)
infixl 4 <<*>>
(<.>) :: Functor f => (a -> b) -> (c -> f a) -> (c -> f b)
(<.>) f g x = f <$> g x
infixl 4 <.>
debug :: P.Member LogEff r => Text -> P.Sem r ()
debug = Di.debug
info :: P.Member LogEff r => Text -> P.Sem r ()
info = Di.info
error :: P.Member LogEff r => Text -> P.Sem r ()
error = Di.error
swap :: (a, b) -> (b, a)
swap ~(a, b) = (b, a)
newtype DefaultingMap k v = DefaultingMap {unDefaultingMap :: M.Map k v}
instance Default (DefaultingMap k v) where
def = DefaultingMap M.empty
newtype AesonVector a = AesonVector {unAesonVector :: VU.Vector a}
deriving (Show) via VU.Vector a
instance (FromJSON a, VU.Unboxable a) => FromJSON (AesonVector a) where
parseJSON = (AesonVector . VU.fromList <$>) . parseJSON
instance (ToJSON a, VU.Unboxable a) => ToJSON (AesonVector a) where
toJSON = toJSON . VU.toList . unAesonVector
toEncoding = toEncoding . VU.toList . unAesonVector
instance (TextShow a, VU.Unboxable a) => TextShow (AesonVector a) where
showb = showbList . VU.toList . unAesonVector
newtype CalamityFromStringShow a = CalamityFromStringShow {unCalamityFromStringShow :: a}
deriving (FromJSON, ToJSON) via a
deriving TextShow via FromStringShow a
-- | An alternative 'Maybe' type that allows us to distinguish between parsed
-- json fields that were null, and fields that didn't exist.
data MaybeNull a
= WasNull
| NotNull a
deriving (Show, Generic)
deriving (TextShow) via TSG.FromGeneric (MaybeNull a)
instance FromJSON a => FromJSON (MaybeNull a) where
parseJSON Null = pure WasNull
parseJSON x = NotNull <$> parseJSON x
instance ToJSON a => ToJSON (MaybeNull a) where
toJSON WasNull = Null
toJSON (NotNull x) = toJSON x
toEncoding WasNull = null_
toEncoding (NotNull x) = toEncoding x