{-# LANGUAGE LambdaCase #-}

-- |
-- Module: BDCS.Utils.Monad
-- Copyright: (c) 2016-2017 Red Hat, Inc.
-- License: LGPL
--
-- Maintainer: https://github.com/weldr
-- Stability: alpha
-- Portability: portable
--
-- Monad related utility functions

module BDCS.Utils.Monad(concatForM,
                        concatMapM,
                        foldMaybeM,
                        mapMaybeM,
                        (>>?))
 where

import Data.Maybe(catMaybes)

-- | Like 'Data.List.concatMap', but with its arguments reversed (the list comes
-- first and the function comes second) and operates in a monad.
concatForM :: (Monad m, Traversable t) => t a -> (a -> m [b]) -> m [b]
concatForM lst fn = fmap concat (mapM fn lst)

-- | Like 'Data.List.concatMap' but operates in a monad.
concatMapM :: (Monad m, Traversable t) => (a -> m [b]) -> t a -> m [b]
concatMapM fn lst = fmap concat (mapM fn lst)

-- | Like 'Data.Maybe.mapMaybe' but operates in a monad.
mapMaybeM :: Monad m => (a -> m (Maybe b)) -> [a] -> m [b]
mapMaybeM fn = fmap catMaybes . mapM fn

-- Like 'Control.Monad.foldM', but skip Nothing results.
foldMaybeM :: Monad m => (b -> a -> m (Maybe b)) -> b -> [a] -> m b
foldMaybeM _ acc [] = return acc
foldMaybeM action acc (x:xs) = do
    result <- action acc x
    case result of
        -- skip this element, continue with the original accumulator
        Nothing -> foldMaybeM action acc xs
        -- Keep this one
        Just r  -> foldMaybeM action r xs

-- | Apply a function to a monadic action.  If the action is 'm Nothing', do nothing.  Otherwise, apply the
-- function and return the result as 'm (Just a)'.
infixl 1 >>?
(>>?) :: Monad m => m (Maybe a) -> (a -> m b) -> m (Maybe b)
(>>?) input action = input >>= \case
    Nothing -> return Nothing
    Just x  -> Just <$> action x