I have the following code:
import System.Directory
import System.FilePath
import Control.Monad (filterM)
filesAndDirs dir = do
entries <- getDirectoryContents dir
let filtered = [dir </> e | e <- entries, e `notElem` [".", ".."]]
files <- filterM doesFileExist filtered
dirs <- filterM doesDirectoryExist filtered
return (files, dirs)
What I would like to write is something like return $ partitionM doesFileExist filtered
. Is there a way to reuse or lift partition
or is the double use of filterM
the best way?
A search for partitionM
on Hayoo will return you at least 2 libraries implementing that function. This means that you can either depend on them or study their source.
Here's a more readable translation of this implementation:
partitionM :: (Monad m) => (a -> m Bool) -> [a] -> m ([a], [a])
partitionM p xs = foldM f ([], []) xs
where
f (a, b) x = do
flag <- p x
return $ if flag
then (x : a, b)
else (a, x : b)
Concerning your question on how to lift the partition
function to partitionM
, I came up with the following implementation of a lifting function:
liftSplitter :: (Monad m) =>
((a -> Bool) -> [a] -> ([a], [a])) ->
(a -> m Bool) -> [a] -> m ([a], [a])
liftSplitter splitter kleisliPredicate list = do
predicateResultsAndItems <- sequence $ do
item <- list
return $ do
predicateResult <- kleisliPredicate item
return (predicateResult, item)
return $ results $ predicateResultsAndItems
where
results [] = ([], [])
results ((predicateResult, item) : tail) = (a ++ tailA, b ++ tailB)
where
(a, b) = splitter (const predicateResult) [item]
(tailA, tailB) = results tail
You can use this function to lift all functions of type
(a -> Bool) -> [a] -> ([a], [a])
(i.e., partition
, break
and span
) to
(a -> m Bool) -> [a] -> m ([a], [a])
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With