我正试图通过目录递归,处理文件并将结果存储在数据库中,但我遇到了问题.
我正在尝试做的简化示例如下:
{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies, OverloadedStrings #-}
{-# LANGUAGE GADTs, FlexibleContexts #-}
import Database.Persist
import Database.Persist.Sqlite
import Database.Persist.TH
import System.Environment (getArgs)
import System.Directory (canonicalizePath, getDirectoryContents, doesDirectoryExist, doesFileExist)
import System.FilePath (combine, takeExtension)
import Control.Monad (filterM, mapM_)
import Control.Monad.IO.Class (liftIO)
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistUpperCase|
File
path String
deriving (Show)
|]
main :: IO ()
main = do
args <- getArgs
path <- canonicalizePath $ head args
runSqlite "files.sqlite" $ do
runMigration migrateAll
liftIO $ processDirectory path
return ()
processDirectory path = files >>= mapM_ processFile >>
directories >>= mapM_ processDirectory
where contents = getDirectoryContents path >>=
return . map (combine path) . filter (`notElem` [".", ".."])
directories = contents >>= filterM doesDirectoryExist
files = contents >>= filterM doesFileExist
processFile path = insert $ File path
Run Code Online (Sandbox Code Playgroud)
但是上面没有编译,而是导致:
No instance for (PersistStore IO)
arising from a use of `processFile'
Possible fix: add an instance declaration for (PersistStore IO)
In the first argument of `mapM_', namely `processFile'
In the second argument of `(>>=)', namely `mapM_ processFile'
In the first argument of `(>>)', namely
`files >>= mapM_ processFile'
Failed, modules loaded: none.
Run Code Online (Sandbox Code Playgroud)
这对我来说很有意义,因为processFile只是对insert的调用,PersistStore monad的哪一部分(对吧?)而不是IO.我认为我需要的是monad变换器,但此时我遇到了一堵砖墙,这或许意味着我正在咆哮错误的树.
您不希望由liftIO包含的一件事是数据库操作,因此您需要重新排列代码以将processFile保留在其外部.
关于代码的最简单更改将使其编译如下.我会留给你整理它,以便更清楚发生了什么!
{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies, OverloadedStrings #-}
{-# LANGUAGE GADTs, FlexibleContexts #-}
import Database.Persist
import Database.Persist.Sqlite
import Database.Persist.TH
import System.Environment (getArgs)
import System.Directory (canonicalizePath, getDirectoryContents, doesDirectoryExist, doesFileExist)
import System.FilePath (combine, takeExtension)
import Control.Monad (filterM, mapM_)
import Control.Monad.IO.Class (liftIO)
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistUpperCase|
File
path String
deriving (Show)
|]
main :: IO ()
main = do
args <- getArgs
path <- canonicalizePath $ head args
runSqlite "files.sqlite" $ do
runMigration migrateAll
processDirectory path
return ()
processDirectory path = liftIO files >>= mapM_ processFile >>
liftIO directories >>= mapM_ processDirectory
where contents = getDirectoryContents path >>=
return . map (combine path) . filter (`notElem` [".", ".."])
directories = contents >>= filterM doesDirectoryExist
files = contents >>= filterM doesFileExist
processFile path = insert $ File path
Run Code Online (Sandbox Code Playgroud)