IO trouble finding sizing directory contents?

I am studying Haskell, and today my task is to write the function sizeOf :: FilePath -> IO Integer (calculate the size of a file or folder), with logic

  • If path is a file, System.Directory.getFileSize path
  • If path is a directory, get a list of its contents, recursively run this function on them, and sum results
  • If it is something other than a file or directory, return 0

Here is how I could implement it in Ruby to illustrate (Ruby notes: map arguments are equivalent to \d -> size_of d , reduce :+ is foldl (+) 0 , any terminating function ? Returns bool, returns implicit):

 def size_of path if File.file? path File.size path elsif File.directory? path Dir.glob(path + '/*').map { |d| size_of d }.reduce :+ end end 

Here is my crack on it in Haskell:

  sizeOf :: FilePath -> IO Integer sizeOf path = do isFile <- doesFileExist path if isFile then getFileSize path else do isDir <- doesDirectoryExist path if isDir then sum $ map sizeOf $ listDirectory path else return 0 

I know where my problem is. sum $ map sizeOf $ listDirectory path , where listDirectory path returns IO [FilePath] , not a FilePath . But ... I can not imagine any solution to resolve this. <$> instead of $ was the first thing that came to mind, since <$> I realized that the function a -> b becomes Context a -> Context b . But ... I think this is not so?

I spent about two hours puzzling the logic. I tried this with other examples. Here is the corresponding discovery that threw me: if double = (*) 2 , then map double [1,2,3] == [2,4,6] , but map double <$> [return 1, return 2, return 3] == [[2],[4],[6]] ... he transfers them to the list. I think that what happens to me, but I came out of the depths.

+5
source share
2 answers

You will need

 sum <$> (listDirectory path >>= mapM sizeOf) 

Explanation:

  • The idea of ​​using sum over IO [Integer] is fine, so we need to get such a thing.
  • listDirectory path gives us IO [FilePath] , so we need to pass each path to the sizeOf file. This is what >>= does with mapM .
  • Note that map alone will give us [IO Integer] , so we need mapM
+5
source

how about (using Control.Monad.Extra):

 du :: FilePath -> IO Integer du path = ifM (doesFileExist path) (getFileSize path) $ ifM (doesDirectoryExist path) (sum <$> (listDirectory path >>= mapM (du . (addTrailingPathSeparator path ++)))) (return 0) 

I believe that you need to add a listDirectory exit listDirectory for a successful recursive descent, since listDirectory returns file names without the path that is required for subsequent du calls.

The ifM type is probably obvious, but ifM :: Monad m => m Bool -> ma -> ma -> ma

+1
source

Source: https://habr.com/ru/post/1261833/


All Articles