3
\$\begingroup\$

This is what I've done;

import Control.Applicative
import Control.Monad
import qualified System.Directory as SD
main = do
    files <- SD.getDirectoryContents =<< SD.getCurrentDirectory
    exes <- filterM (liftA SD.executable <$> SD.getPermissions) files
    print exes

This works fine, but I can't help feeling that this code is somewhat redundant --- with filterM, liftA, <$> and =<< all present at once, things seem unnecessarily complicated.

How can I improve this piece of code?

For a quick reference:

SD.getCurrentDirectory :: IO FilePath
SD.getDirectoryContents :: FilePath -> IO [FilePath]
SD.executable :: Permissions -> Bool
SD.getPermissions :: FilePath -> IO Permissions

and I want to filter the contents of the current directory based on its being executable or not.

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

I wouldn't import System.Directory qualified, it doesn't export anything that could clash with standard names.

import Control.Applicative
import Control.Monad
import System.Directory

main :: IO ()
main = do
    files <- getDirectoryContents =<< getCurrentDirectory
    exes <- filterM (liftA executable <$> getPermissions) files
    print exes

You can toss out the Functor and Applicative stuff and just compose Monads.

import Control.Monad
import System.Directory

main :: IO ()
main = do
    files <- getDirectoryContents =<< getCurrentDirectory
    exes <- filterM (liftM executable . getPermissions) files
    print exes

You could also get rid of reverse bind and use the forward version, it's maybe more familiar?

import Control.Monad
import System.Directory

main :: IO ()
main = do
    files <- getCurrentDirectory >>= getDirectoryContents
    exes <- filterM (liftM executable . getPermissions) files
    print exes

I'd eliminate the explicit use of bind entirely though, then it becomes easier to turn this into a function that can work on directories other than the current one.

import Control.Monad
import System.Directory

main :: IO ()
main = do
    dir <- getCurrentDirectory
    printExecutables dir

printExecutables :: FilePath -> IO ()
printExecutables dir = do
    files <- getDirectoryContents dir
    exes <- filterM (liftM executable . getPermissions) files
    print exes
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.