该程序中的多种类型不匹配

rns*_*nso 1 haskell types

我有一个Linux文件夹,其中包含许多文件,其中一些是二进制文件,而其他文件是脚本文件,其中第一行包含#! /bin/bash或类似内容。我正在尝试读取此类文件的第一行,拆分该行并打印出最后一个成员的列表(指示使用的程序):

import Control.Exception(catch, IOException)
import System.IO(IOMode(ReadMode), withFile, hGetLine)
import Data.Maybe(catMaybes)
import Data.List
import System.Directory (getDirectoryContents, doesFileExist, getFileSize)
import System.FilePath ((</>))
import Control.Monad(filterM)

getAbsoluteDirContents :: String -> IO [FilePath]
getAbsoluteDirContents dir = do
    contents <- getDirectoryContents dir
    return $ map (dir </>) contents

readFirstLine :: FilePath -> IO (Maybe String)
readFirstLine fp = withFile fp ReadMode $
    \h -> (catch (fmap Just (hGetLine h))
        ((const :: a -> IOException -> a) (return Nothing)))

hasSlash :: String -> Bool
hasSlash firline = do
    elem '/' firline

-- main :: IO ()
main = do
    print
    $ map last              -- get only last string
    $ map splitOn "/"       -- split first line
    $ filter hasSlash       -- filter out those that do not have '/'
    $ catMaybes             -- get Just ones
    $ map readFirstLine     -- read first line of each file
    $ filter doesFileExist  -- filter out dirs
    $ getAbsoluteDirContents "." -- get a list of all files & dirs
Run Code Online (Sandbox Code Playgroud)

在主函数中,执行将从下往上进行!

但是,以上代码给出了多个类型不匹配的情况:

soq_firstline6.hs:28:11: error:
    • Couldn't match expected type ‘[String] -> [[b0]]’
                  with actual type ‘[b1]’
    • The first argument of ($) takes one argument,
      but its type ‘[b1]’ has none
      In the second argument of ‘($)’, namely
        ‘map splitOn "/"
         $ filter hasSlash
           $ catMaybes
             $ map readFirstLine
               $ filter doesFileExist $ getAbsoluteDirContents "."’
      In the second argument of ‘($)’, namely
        ‘map last
         $ map splitOn "/"
           $ filter hasSlash
             $ catMaybes
               $ map readFirstLine
                 $ filter doesFileExist $ getAbsoluteDirContents "."’

soq_firstline6.hs:28:15: error:
    • Variable not in scope: splitOn :: Char -> b1
    • Perhaps you meant ‘splitAt’ (imported from Data.List)

soq_firstline6.hs:31:7: error:
    • Couldn't match type ‘IO (Maybe String)’ with ‘Maybe String’
      Expected type: [Maybe String]
        Actual type: [IO (Maybe String)]
    • In the second argument of ‘($)’, namely
        ‘map readFirstLine
         $ filter doesFileExist $ getAbsoluteDirContents "."’
      In the second argument of ‘($)’, namely
        ‘catMaybes
         $ map readFirstLine
           $ filter doesFileExist $ getAbsoluteDirContents "."’
      In the second argument of ‘($)’, namely
        ‘filter hasSlash
         $ catMaybes
           $ map readFirstLine
             $ filter doesFileExist $ getAbsoluteDirContents "."’

soq_firstline6.hs:32:14: error:
    • Couldn't match type ‘IO Bool’ with ‘Bool’
      Expected type: FilePath -> Bool
        Actual type: FilePath -> IO Bool
    • In the first argument of ‘filter’, namely ‘doesFileExist’
      In the expression: filter doesFileExist
      In the second argument of ‘($)’, namely
        ‘filter doesFileExist $ getAbsoluteDirContents "."’

soq_firstline6.hs:33:7: error:
    • Couldn't match expected type ‘[FilePath]’
                  with actual type ‘IO [FilePath]’
    • In the second argument of ‘($)’, namely
        ‘getAbsoluteDirContents "."’
      In the second argument of ‘($)’, namely
        ‘filter doesFileExist $ getAbsoluteDirContents "."’
      In the second argument of ‘($)’, namely
        ‘map readFirstLine
         $ filter doesFileExist $ getAbsoluteDirContents "."’
Run Code Online (Sandbox Code Playgroud)

如何纠正这些错误。谢谢你的帮助。

Wil*_*sem 5

您不能只filter doesFileExists在这里使用,因为它doesFileExists是一个FilePath -> IO Bool函数,而不是一个FilePath -> Bool函数,因此map readFirstLine通过使用map,您正在构建一个[IO (Maybe String)],而不是一个,也会发生同样的情况IO [Maybe String]。您还忘记了使用括号map (splitOn "/")

您可以使用filterM和来解决此mapM问题:

main = do
    fps <- getAbsoluteDirContents "."
    fexists <- filterM doesFileExist fps
    flines <- mapM readFirstLine fexists
    print
      $ map last              -- get only last string
      $ map (splitOn "/")     -- split first line
      $ filter hasSlash       -- filter out those that do not have '/'
      $ catMaybes flines      -- get Just ones
Run Code Online (Sandbox Code Playgroud)

或经过贬义的变体:

main :: IO ()
main = getAbsoluteDirContents "." >>= filterM doesFileExist >>= mapM readFirstLine >>= print . map (last . splitOn "/") . filter hasSlash . catMaybes
Run Code Online (Sandbox Code Playgroud)

hasSlash使用了do,但是在这里根本不使用monad,因此最好将其写为简单的:

hasSlash :: String -> Bool
hasSlash = elem '/'
Run Code Online (Sandbox Code Playgroud)