{-# LANGUAGE Safe #-}

{- |
Copyright:  (c) 2016 Stephen Diehl
            (c) 2016-2018 Serokell
            (c) 2018-2020 Kowainik
SPDX-License-Identifier: MIT
Maintainer:  Kowainik <xrom.xkov@gmail.com>
Stability:   Stable
Portability: Portable

Utility functions to work with 'Relude.Maybe' data type as monad.
-}

module Relude.Monad.Maybe
    ( -- * Combinators
      (?:)
    , whenJust
    , whenJustM
    , whenNothing
    , whenNothing_
      -- * Monadic combinators
    , whenNothingM
    , whenNothingM_
    , mapMaybeM
    ) where

import Relude.Applicative (Applicative, pass, pure)
import Relude.Foldable.Reexport (mapM)
import Relude.Function ((.))
import Relude.Functor.Reexport (fmap)
import Relude.Monad.Reexport (Maybe (..), Monad (..), catMaybes, fromMaybe)


-- $setup
-- >>> import Relude

{- | Similar to 'fromMaybe' but with flipped arguments.

>>> readMaybe "True" ?: False
True

>>> readMaybe "Tru" ?: False
False
-}
infixr 0 ?:
(?:) :: Maybe a -> a -> a
mA :: Maybe a
mA ?: :: Maybe a -> a -> a
?: b :: a
b = a -> Maybe a -> a
forall a. a -> Maybe a -> a
fromMaybe a
b Maybe a
mA
{-# INLINE (?:) #-}

{- | Specialized version of 'Relude.for_' for 'Maybe'. It's used for code readability.

Also helps to avoid space leaks:
<http://www.snoyman.com/blog/2017/01/foldable-mapm-maybe-and-recursive-functions Foldable.mapM_ space leak>.

>>> whenJust Nothing $ \b -> print (not b)
>>> whenJust (Just True) $ \b -> print (not b)
False
-}
whenJust :: Applicative f => Maybe a -> (a -> f ()) -> f ()
whenJust :: Maybe a -> (a -> f ()) -> f ()
whenJust (Just x :: a
x) f :: a -> f ()
f = a -> f ()
f a
x
whenJust Nothing _  = f ()
forall (f :: * -> *). Applicative f => f ()
pass
{-# INLINE whenJust #-}

{- | Monadic version of 'whenJust'.

>>> whenJustM (pure Nothing) $ \b -> print (not b)
>>> whenJustM (pure $ Just True) $ \b -> print (not b)
False
-}
whenJustM :: Monad m => m (Maybe a) -> (a -> m ()) -> m ()
whenJustM :: m (Maybe a) -> (a -> m ()) -> m ()
whenJustM mm :: m (Maybe a)
mm f :: a -> m ()
f = m (Maybe a)
mm m (Maybe a) -> (Maybe a -> m ()) -> m ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \m :: Maybe a
m -> Maybe a -> (a -> m ()) -> m ()
forall (f :: * -> *) a.
Applicative f =>
Maybe a -> (a -> f ()) -> f ()
whenJust Maybe a
m a -> m ()
f
{-# INLINE whenJustM #-}

{- | Performs default 'Applicative' action if 'Nothing' is given.
Otherwise returns content of 'Just' pured to 'Applicative'.

>>> whenNothing Nothing [True, False]
[True,False]
>>> whenNothing (Just True) [True, False]
[True]
-}
whenNothing :: Applicative f => Maybe a -> f a -> f a
whenNothing :: Maybe a -> f a -> f a
whenNothing (Just x :: a
x) _ = a -> f a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x
whenNothing Nothing  m :: f a
m = f a
m
{-# INLINE whenNothing #-}

{- | Performs default 'Applicative' action if 'Nothing' is given.
Do nothing for 'Just'. Convenient for discarding 'Just' content.

>>> whenNothing_ Nothing $ putTextLn "Nothing!"
Nothing!
>>> whenNothing_ (Just True) $ putTextLn "Nothing!"
-}
whenNothing_ :: Applicative f => Maybe a -> f () -> f ()
whenNothing_ :: Maybe a -> f () -> f ()
whenNothing_ Nothing m :: f ()
m = f ()
m
whenNothing_ _       _ = f ()
forall (f :: * -> *). Applicative f => f ()
pass
{-# INLINE whenNothing_ #-}

{- | Monadic version of 'whenNothingM'.

>>> whenNothingM (pure $ Just True) $ True <$ putTextLn "Is Just!"
True
>>> whenNothingM (pure Nothing) $ False <$ putTextLn "Is Nothing!"
Is Nothing!
False
-}
whenNothingM :: Monad m => m (Maybe a) -> m a -> m a
whenNothingM :: m (Maybe a) -> m a -> m a
whenNothingM mm :: m (Maybe a)
mm action :: m a
action = m (Maybe a)
mm m (Maybe a) -> (Maybe a -> m a) -> m a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \m :: Maybe a
m -> Maybe a -> m a -> m a
forall (f :: * -> *) a. Applicative f => Maybe a -> f a -> f a
whenNothing Maybe a
m m a
action
{-# INLINE whenNothingM #-}

{- | Monadic version of 'whenNothingM_'.

>>> whenNothingM_ (pure $ Just True) $ putTextLn "Is Just!"
>>> whenNothingM_ (pure Nothing) $ putTextLn "Is Nothing!"
Is Nothing!
-}
whenNothingM_ :: Monad m => m (Maybe a) -> m () -> m ()
whenNothingM_ :: m (Maybe a) -> m () -> m ()
whenNothingM_ mm :: m (Maybe a)
mm action :: m ()
action = m (Maybe a)
mm m (Maybe a) -> (Maybe a -> m ()) -> m ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \m :: Maybe a
m -> Maybe a -> m () -> m ()
forall (f :: * -> *) a. Applicative f => Maybe a -> f () -> f ()
whenNothing_ Maybe a
m m ()
action
{-# INLINE whenNothingM_ #-}


{- | The monadic version of the 'Data.Maybe.mapMaybe' function.

>>> :{
evenInHalf :: Int -> IO (Maybe Int)
evenInHalf n
    | even n = pure $ Just $ n `div` 2
    | otherwise = pure Nothing
:}

>>> mapMaybeM evenInHalf [1..10]
[1,2,3,4,5]

@since 0.6.0.0
-}
mapMaybeM :: (Monad m) => (a -> m (Maybe b)) -> [a] -> m [b]
mapMaybeM :: (a -> m (Maybe b)) -> [a] -> m [b]
mapMaybeM f :: a -> m (Maybe b)
f = ([Maybe b] -> [b]) -> m [Maybe b] -> m [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Maybe b] -> [b]
forall a. [Maybe a] -> [a]
catMaybes (m [Maybe b] -> m [b]) -> ([a] -> m [Maybe b]) -> [a] -> m [b]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> m (Maybe b)) -> [a] -> m [Maybe b]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM a -> m (Maybe b)
f
{-# INLINE mapMaybeM #-}