patternMinor
Can I use mapWriterT instead of mapM/mapM_ (Haskell)?
Viewed 0 times
canmapminsteadmapm_haskellusemapwritert
Problem
First off, I'm just learning Haskell and I'm not proficient in that language yet.
I wrote this Haskell module to look for files in a Linux filesystem. It can use regular expressions as search patterns. I wrote two variants: one of them sends the search results to stdout, the other (shown here) gives a list with the search results.
For example (in GHCi):
I made the function searchWriterT recursive using mapM_, but I know there is mapWriterT in the module Control.Monad.Writer.
Could anyone give me a hint about what I shall do to replace mapM_ with mapWriterT (if it's possible)?
Thanks in advance.
``
searchWriterT :: String -> FilePath -> W
I wrote this Haskell module to look for files in a Linux filesystem. It can use regular expressions as search patterns. I wrote two variants: one of them sends the search results to stdout, the other (shown here) gives a list with the search results.
For example (in GHCi):
searchToList "conf" "." >>= return . take 1 (being "." my home directory) would give [".config"]I made the function searchWriterT recursive using mapM_, but I know there is mapWriterT in the module Control.Monad.Writer.
Could anyone give me a hint about what I shall do to replace mapM_ with mapWriterT (if it's possible)?
Thanks in advance.
``
module FileSearch where
import System.Directory (getDirectoryContents, doesDirectoryExist)
import Control.Monad (filterM, unless)
import Data.List ((\\))
import Text.Regex.Posix ((=~))
import Control.Monad.Writer
data Dir = Dir { path :: FilePath -- path of the current directory
, files :: [FilePath] -- files in the directory
, subdirs :: [FilePath] } -- subdirectories
deriving (Show)
dontSearch :: [FilePath]
dontSearch = ["/proc", "/sys"]
-- listDir takes the name of a directory and gives a Dir with
-- that name, a list of the files in the directory and a list of
-- its subdirectories, all prefixed with the path of the current
-- directory.
-- listDir will list neither /proc nor /sys
listDir :: FilePath -> IO Dir
listDir f
| f elem dontSearch = return Dir { path = f
, files = []
, subdirs = [] }
| otherwise = do
c' FilePath -> FilePath
-- if the path is "/", do not add another "/"
appendPath "/" y = concat ["/", y]
appendPath x y = concat [x, "/", y]
-- remove "." and ".." entries in directories
deleteDot :: [FilePath] -> [FilePath]
deleteDot = filter (notElem` [".", ".."])searchWriterT :: String -> FilePath -> W
Solution
mapWriterT is for transforming one WriterT into another. Its type is painfully generic, but allows you to transform pretty much everything about a WriterT.mapWriterT :: (m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n bSo if you start with a
WriterT w m a, and you want to end up with WriterT w' n b (in other words, you can transform all 3 type parameters), then you have to provide a function of the formm (a, w) -> n (b, w').Contrast this with
mapM_ :: (a -> m b) -> [a] -> m (). The purpose of this mapping is to take a list and perform monadic operations parameterized by the list's contents in sequential order.While there may be a way to write your function in terms of
mapWriterT, it is a completely different kind of mapping than mapM_, so your request to " replace mapM_ with mapWriterT" doesn't really make much sense to me. Nevertheless, let's give it a spin.newtype MatchedFiles = MatchedFiles { unwrapFiles :: [FilePath] } deriving Monoid
newtype SubDirs = SubDirs { unwrapDirs :: [FilePath] }
filesInDir :: String -> FilePath -> WriterT MatchedFiles IO SubDirs
filesInDir s f = do
d <- liftIO $ listDir f
let found = MatchedFiles $ filter (=~ s) (files d)
subdirs = SubDirs $ subdirs d
tell found
return subdirsOK, so here we've got a non-recursive
WriterT that will simply tell the files that match the string at the given directory, and returns the subdirectories in that directory. I've used newtypes to help me not get the "found files" and "subdirectories" mixed up.Now, what is the transformation we want to perform on this?
searchWriterT :: String -> FilePath -> WriterT MatchedFiles IO ()
searchWriterT s f = mapWriterT mapper (filesInDir s f)
where
mapper :: IO (SubDirs, MatchedFiles) -> IO ((), MatchedFiles)
mapper action = undefinedAssuming we want to write
searchWriterT in terms of mapWriterT and filesInDir, I just followed the types and this is what we've got. Now, the question is, how to write mapper?mapper action = do
(SubDirs subdirs, files) <- action
if null subdirs
then return ((), files)
else undefinedClearly, if there are no "subdirs", then we are done, and we simply regurgitate the
files. But what if there are subdirs? Well, you've already figured out what to do: use mapM_ on the subdirs!else runWriterT $ tell files >> mapM_ (searchWriterT s) subdirsIn fact,
mapM_ will take care of the null case for us: it won't do anything in the event of an empty list.mapper action = do
(SubDirs subdirs, files) > mapM_ (searchWriterT s) subdirsConclusion:
mapWriterT felt kind of awkward for this problem, especially since inside the mapper we turned right around and used runWriterT. We didn't even get rid of mapM_, because mapM_ embodies exactly what needs to be done for this problem.searchWriterT s f = filesInDir s f >>= mapM_ (searchWriterT s) . unwrapDirsCode Snippets
mapWriterT :: (m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n bnewtype MatchedFiles = MatchedFiles { unwrapFiles :: [FilePath] } deriving Monoid
newtype SubDirs = SubDirs { unwrapDirs :: [FilePath] }
filesInDir :: String -> FilePath -> WriterT MatchedFiles IO SubDirs
filesInDir s f = do
d <- liftIO $ listDir f
let found = MatchedFiles $ filter (=~ s) (files d)
subdirs = SubDirs $ subdirs d
tell found
return subdirssearchWriterT :: String -> FilePath -> WriterT MatchedFiles IO ()
searchWriterT s f = mapWriterT mapper (filesInDir s f)
where
mapper :: IO (SubDirs, MatchedFiles) -> IO ((), MatchedFiles)
mapper action = undefinedmapper action = do
(SubDirs subdirs, files) <- action
if null subdirs
then return ((), files)
else undefinedelse runWriterT $ tell files >> mapM_ (searchWriterT s) subdirsContext
StackExchange Code Review Q#12584, answer score: 3
Revisions (0)
No revisions yet.