在Haskell中检查文件类型的更好方法

时间:2014-08-23 19:34:02

标签: haskell

对于使用目录漫游器的应用程序,如果文件可访问,我需要信息,真实文件,需要区分文件和目录条目。

我想:

  • 跳过所有软链接,管道和其他特殊文件。
  • 仅访问可以读取且可以写入的文件。
  • 仅列出可以输入和列出的目录。

因此,所有文件都可以被读取并且可以被操作并驻留在允许该文件的目录中。

这就是我提出的:

fileType :: FilePath -> IO Int
fileType f = do
    -- skip any symbolic link
    l <- getSymbolicLinkStatus f
    if isSymbolicLink l
        then return 0 -- link
        else do
            s <- getFileStatus f
            if isRegularFile s
                then do
                    -- files need read and write
                    facc <- fileAccess f True True False
                    if facc
                        then return 1
                        else return 0 -- file but not RW
                else if isDirectory s
                    then do
                        -- dirs need read and execute
                        dacc <- fileAccess f True False True
                        if dacc
                            then return 2
                            else return 0 -- dir but not RX
                    else return 0 -- not a file or dir

但是我对这个实现非常不确定,并且想问一下我能做些什么来使这个更简洁。

例如,我有一种感觉,我至少可以移动&#34;返回&#34;在顶部的某个地方。但是尝试这个我无法确定类型。

P.S。:对我来说,返回Int 0 1 2(而不是特殊的数据类型)是好的,但我不介意,如果改变了。

2 个答案:

答案 0 :(得分:4)

您可以使用sum类型来表示不同的文件类型,而不是使用Int来跟踪不同的文件类型:

data FileType = SymbolicLink  -- Symbolic link
              | FileRead      -- File with Read Permission
              | DirRead       -- Directory with Read Permission
              | DirNoRead     -- Directory with No Read Permission
              | FileNoRead    -- File with No Read Permssion
              | NotFileAndDir -- Neither File nor directory
              deriving (Show)

我可以在您的代码中看到的一种模式是,有各种嵌套的monadic if if检查条件,然后根据它返回适当的结果。您可以看到标准库是否提供了这样的抽象,或者它是否可以为您自己编写:

bdef :: (Monad m) => m Bool -> m a -> m a -> m a
bdef mb t f  = mb >>= \x -> if x then t else f

bdef函数中,如果mbIO True,则我会回溯第一个参数或第二个参数。请注意,它不需要IO,但它可以是任何monad。一旦定义了这个,剩下的就是定义剩下的函数:

filetype :: FilePath -> IO FileType
filetype f =  sym
  where sym = bdef (isSymbolicLink <$> getSymbolicLinkStatus f)
                (return SymbolicLink) reg
        reg = bdef (isRegularFile <$> fStatus)
              (bdef checkfRead (return FileRead) (return FileNoRead)) dir
        dir = bdef (isDirectory <$> fStatus)
              (bdef checkDRead (return DirRead) (return DirNoRead))
              (return NotFileAndDir)
        checkfRead = fileAccess f True True False
        checkDRead = fileAccess f True False True
        fStatus = getFileStatus f

示例ghci演示:

λ> filetype "/home/sibi/test.hs"
FileRead
λ> filetype "/home/sibi"
DirRead

答案 1 :(得分:2)

阅读完其他答案和评论后(非常感谢!)我想回答我自己的问题并告诉你我的想法:

data FileType = Skip | File | Dir

getFileType :: FilePath -> IO FileType
getFileType f = getSymbolicLinkStatus f >>= testIt
    where testIt s
            | isSymbolicLink s = return Skip
            | isRegularFile s = useWhen (fileAccess f True True False) File
            | isDirectory s = useWhen (fileAccess f True False True) Dir
            | otherwise = return Skip

          useWhen p t = p >>= \b -> if b then return t else return Skip

我做的是:

  • 首先创建我真正需要的类型(Skip,File,Dir)。
  • 然后我发现我真的只需要获取一次fileStatus(这不应该遵循符号链接)。
  • 在此之后,看到这最终导致多个案件并且我使用了防护装置,这是微不足道的。
  • 在尝试“ifM”(来自评论的bdef)时,我看到通用形式只是为了通用8now)但是有一个帮助器使函数更具可读性是很好的。
  • ifM在这种情况下实际上更像“何时”。
  • 在某些地方删除do,因为只剩下一个操作。