HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonModerate

Script for copying files to the appropriate directory

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
scriptdirectorytheappropriatefilescopyingfor

Problem

In the book Real World Haskell, the author writes in the first line of every file that uses, for example the following: file: ch05/PrettyJSON. The chapter slash and the name of a module.

I wanted to create a script which reads the first line and copies the files to the appropriate directory. In the above example we should copy the file to the directory ch05. I wanted to write this in Haskell in order to use it for "real world" applications.

But before starting writing to Haskell I wrote it in Python:

import os
import shutil

current_dir = os.getcwd()
current_files=os.listdir(current_dir)
haskell_files=filter(lambda x:x.find(".hs")!=-1,current_files)

for x in haskell_files:
 with open(x,"r") as f:
     header=f.readline()
    try:
        chIndex=header.index("ch")
    except ValueError as e:
        continue

    number=header[chIndex:chIndex+4]
 try:
    os.mkdir(number)
    shutil.copy2(x,number)
except OSError as e:
    shutil.copy2(x,number)


And then I tried to write the same code in Haskell:

f x = dropWhile (\x-> x/='c') x
g x = takeWhile (\x-> x=='c' || x=='h' || isDigit x) x
z= do 
   y<-getCurrentDirectory
   e<-getDirectoryContents y
   let b=findhs e
   mapM w b
findhs filepaths = filter (isSuffixOf ".hs") filepaths
w filename= do
           y<-readFile filename
           let a=(g.f) y
           if "ch" `isPrefixOf` a
           then do
                 createDirectoryIfMissing False a
                 g<-getCurrentDirectory
                 writeFile (g ++"/"++ a ++ "/" ++ filename) y
            else
                return ()
main=z


Haskell code should be more elegant and more compact but it is not. Can you give me suggestions on making the above program more Haskellish?

Solution

Notes:

  • don't use single letter variable names for top level bindings



  • leave spaces around syntax like = and



  • use meaningful variable names for system values (such as file contents)



  • inline single uses of tiny functions, such as your findhs e



  • give your core algorithm, w, a meaningful name



  • don't use long names for local parameters like filepaths. fs is fine.



  • use pointfree style for arguments in the final position



  • use when instead of if .. then ... else return ()



  • use the filepath library for nice file path construction



  • your only use of f and g is to compose them. so do that and name the result.



  • use section syntax for lambdas as function arguments to HOFs



  • indent consistently



  • use mapM_` when the result you don't care about.



Resulting in:

import Data.Char
import Data.List
import Control.Monad
import System.Directory
import System.FilePath

clean = takeWhile (\x -> x == 'c' || x == 'h' || isDigit x)
      . dropWhile (/='c')

process path = do
   y  a  path) y

main = do
    y <- getCurrentDirectory
    e <- getDirectoryContents y
    mapM_ process $ filter (isSuffixOf ".hs") e

Code Snippets

import Data.Char
import Data.List
import Control.Monad
import System.Directory
import System.FilePath

clean = takeWhile (\x -> x == 'c' || x == 'h' || isDigit x)
      . dropWhile (/='c')

process path = do
   y <- readFile path

   let a = clean y

   when ("ch" `isPrefixOf` a) $ do
         createDirectoryIfMissing False a
         g <- getCurrentDirectory
         writeFile (g </> a </> path) y

main = do
    y <- getCurrentDirectory
    e <- getDirectoryContents y
    mapM_ process $ filter (isSuffixOf ".hs") e

Context

StackExchange Code Review Q#16038, answer score: 17

Revisions (0)

No revisions yet.