可以将分区应用于`a - > IO Bool`?

时间:2013-03-05 05:40:49

标签: haskell monads

我有以下代码:

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)

我想写的是return $ partitionM doesFileExist filtered。有没有办法重用或提升partition或是filterM最佳方式的双重用途?

1 个答案:

答案 0 :(得分:4)

Hayoo上搜索partitionM将返回至少2个实现该功能的库。这意味着您可以依赖它们或研究它们的来源。

这是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)

关于如何将partition功能解除为partitionM的问题,我提出了以下提升功能的实现:

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

您可以使用此功能解除所有类型

的功能
(a -> Bool) -> [a] -> ([a], [a])

(即,partitionbreakspan

(a -> m Bool) -> [a] -> m ([a], [a])