{-# LANGUAGE Safe #-} {- | Module : Relude.Bool.Guard Copyright : (c) 2016 Stephen Diehl (c) 2016-2018 Serokell (c) 2018-2023 Kowainik SPDX-License-Identifier : MIT Maintainer : Kowainik <xrom.xkov@gmail.com> Stability : Stable Portability : Portable Monadic boolean combinators. -} module Relude.Bool.Guard ( guarded , guardM , ifM , unlessM , whenM , (&&^) , (||^) ) where import Relude.Applicative (Alternative, Applicative (..), empty) import Relude.Bool.Reexport (Bool (..), guard, unless, when) import Relude.Function (flip) import Relude.Monad (Monad, MonadPlus, (>>=)) -- $setup -- >>> import Relude.Applicative (pure) -- >>> import Relude.Bool.Reexport (Bool (..)) -- >>> import Relude.Debug (error) -- >>> import Relude.Function (($)) -- >>> import Relude.Monad (Maybe (..)) -- >>> import Relude.Print (putTextLn) -- >>> import Relude (Int, String, even, const) {- | Monadic version of 'when'. Conditionally executes the provided action. >>> whenM (pure False) $ putTextLn "No text :(" >>> whenM (pure True) $ putTextLn "Yes text :)" Yes text :) >>> whenM (Just True) (pure ()) Just () >>> whenM (Just False) (pure ()) Just () >>> whenM Nothing (pure ()) Nothing -} whenM :: Monad m => m Bool -> m () -> m () whenM :: forall (m :: * -> *). Monad m => m Bool -> m () -> m () whenM m Bool p m () m = m Bool p forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>= forall a b c. (a -> b -> c) -> b -> a -> c flip forall (f :: * -> *). Applicative f => Bool -> f () -> f () when m () m {-# INLINE whenM #-} {- | Monadic version of 'unless'. Reverse of 'whenM'. Conditionally don't execute the provided action. >>> unlessM (pure False) $ putTextLn "No text :(" No text :( >>> unlessM (pure True) $ putTextLn "Yes text :)" -} unlessM :: Monad m => m Bool -> m () -> m () unlessM :: forall (m :: * -> *). Monad m => m Bool -> m () -> m () unlessM m Bool p m () m = m Bool p forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>= forall a b c. (a -> b -> c) -> b -> a -> c flip forall (f :: * -> *). Applicative f => Bool -> f () -> f () unless m () m {-# INLINE unlessM #-} {- | Monadic version of @if-then-else@. >>> ifM (pure True) (putTextLn "True text") (putTextLn "False text") True text >>> ifM (pure False) (putTextLn "True text") (putTextLn "False text") False text -} ifM :: Monad m => m Bool -> m a -> m a -> m a ifM :: forall (m :: * -> *) a. Monad m => m Bool -> m a -> m a -> m a ifM m Bool p m a x m a y = m Bool p forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>= \Bool b -> if Bool b then m a x else m a y {-# INLINE ifM #-} {- | Monadic version of 'guard' that help to check that a condition ('Bool') holds inside. Works with 'Monad's that are also 'Alternative'. >>> guardM (Just True) Just () >>> guardM (Just False) Nothing >>> guardM Nothing Nothing Here some complex but real-life example: @ findSomePath :: IO (Maybe FilePath) somePath :: MaybeT IO FilePath somePath = do path <- MaybeT findSomePath guardM $ liftIO $ doesDirectoryExist path return path @ -} guardM :: MonadPlus m => m Bool -> m () guardM :: forall (m :: * -> *). MonadPlus m => m Bool -> m () guardM m Bool f = m Bool f forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>= forall (f :: * -> *). Alternative f => Bool -> f () guard {-# INLINE guardM #-} {- | Either lifts a value into an alternative context or gives a minimal value depending on a predicate. Works with 'Alternative's. >>> guarded even 3 :: [Int] [] >>> guarded even 2 :: [Int] [2] >>> guarded (const True) "hello" :: Maybe String Just "hello" >>> guarded (const False) "world" :: Maybe String Nothing You can use this function to implement smart constructors simpler: @ __newtype__ HttpHost = HttpHost { unHttpHost :: Text } mkHttpHost :: Text -> Maybe HttpHost mkHttpHost host = HttpHost \<$\> 'guarded' (not . Text.null) host @ @since 0.6.0.0 -} guarded :: Alternative f => (a -> Bool) -> a -> f a guarded :: forall (f :: * -> *) a. Alternative f => (a -> Bool) -> a -> f a guarded a -> Bool p a a = if a -> Bool p a a then forall (f :: * -> *) a. Applicative f => a -> f a pure a a else forall (f :: * -> *) a. Alternative f => f a empty {-# INLINE guarded #-} {- | Monadic version of '(Data.Bool.&&)' operator. It is lazy by the second argument (similar to '(Data.Bool.||)'), meaning that if the first argument is 'False', the function will return 'False' without evaluating the second argument. >>> Just False &&^ Just True Just False >>> Just True &&^ Just True Just True >>> Just True &&^ Nothing Nothing >>> Just False &&^ Nothing Just False >>> Just False &&^ error "Shouldn't be evaluated" Just False @since 0.4.0 -} (&&^) :: Monad m => m Bool -> m Bool -> m Bool &&^ :: forall (m :: * -> *). Monad m => m Bool -> m Bool -> m Bool (&&^) m Bool e1 m Bool e2 = forall (m :: * -> *) a. Monad m => m Bool -> m a -> m a -> m a ifM m Bool e1 m Bool e2 (forall (f :: * -> *) a. Applicative f => a -> f a pure Bool False) {-# INLINE (&&^) #-} {- | Monadic version of '(Data.Bool.||)' operator. It is lazy by the second argument (similar to '(Data.Bool.||)'), meaning that if the first argument is 'True', the function will return 'True' without evaluating the second argument. >>> Just False ||^ Just True Just True >>> Just False ||^ Just False Just False >>> Just False ||^ Nothing Nothing >>> Just True ||^ Nothing Just True >>> Just True ||^ error "Shouldn't be evaluated" Just True @since 0.4.0 -} (||^) :: Monad m => m Bool -> m Bool -> m Bool m Bool e1 ||^ :: forall (m :: * -> *). Monad m => m Bool -> m Bool -> m Bool ||^ m Bool e2 = forall (m :: * -> *) a. Monad m => m Bool -> m a -> m a -> m a ifM m Bool e1 (forall (f :: * -> *) a. Applicative f => a -> f a pure Bool True) m Bool e2 {-# INLINE (||^) #-}