4

我有以下实现目录遍历的模块:

module Walk
  ( walk
  ) where

import           Control.Monad
import           Control.Monad.IO.Class
import           Data.List
import           System.Directory
import           System.FilePath

walk :: (MonadIO m) => FilePath -> m [(FilePath, [FilePath])]
walk root = do
  entries <- liftIO $ listDirectory root
  (files, dirs) <- partition snd <$> liftM2 (<$>) zip (mapM (liftIO . doesFileExist . (root </>))) entries
  ((root, map fst files) :) . concat <$> mapM (walk . (root </>) . fst) dirs

它目前返回一个列表,但我希望它返回一个Traversable

walk :: (MonadIO m, Traversable t) => FilePath -> m (t (FilePath, [FilePath]))

如果我更改签名,我会收到以下错误:

    • Couldn't match type ‘t’ with ‘[]’
      ‘t’ is a rigid type variable bound by
        the type signature for:
          walk :: forall (m :: * -> *) (t :: * -> *).
                  (MonadIO m, Traversable t) =>
                  FilePath -> m (t (FilePath, [FilePath]))
      Expected type: m (t (FilePath, [FilePath]))
        Actual type: m [(FilePath, [FilePath])]
    • In a stmt of a 'do' block:
        ((root, map fst files) :) . concat
          <$> mapM (walk . (root </>) . fst) dirs
      In the expression:
        do entries <- liftIO $ listDirectory root
           (files, dirs) <- partition snd
                              <$>
                                liftM2
                                  (<$>) zip (mapM (liftIO . doesFileExist .
(root </>))) entries
           ((root, map fst files) :) . concat
             <$> mapM (walk . (root </>) . fst) dirs
      In an equation for ‘walk’:
          walk root
            = do entries <- liftIO $ listDirectory root
                 (files, dirs) <- partition snd
                                    <$>
                                      liftM2
                                        (<$>)
                                        zip
                                        (mapM (liftIO . doesFileExist .
(root </>)))
                                        entries
                 ((root, map fst files) :) . concat
                   <$> mapM (walk . (root </>) . fst) dirs
    • Relevant bindings include
        walk :: FilePath -> m (t (FilePath, [FilePath]))

我认为它在:? 我不能确定。我该如何解决?

4

2 回答 2

5

我认为它在:?

它的确是。如果您使用(:)来构建结构,该结构将是一个列表,并且您不能更改类型walk以声称它返回任意可遍历的结构。也没有真正好的以中心为中心的Traversable解决方法:Traversable意味着您通过其Foldable超类拥有 a toList,但没有 a fromList

于 2019-06-01T15:35:29.273 回答
3

列表的多态生成和一般多态容器的类设计已被证明比最初看起来更困难。GHC 当前用于生成完全多态容器的解决方案,而不是仅在诸如 with 之类的预先存在的容器上运行Traversable,就是IsList类。

定义GHC.Exts为:

class IsList l where
  type Item l
  fromList  :: [Item l] -> l
  ...

已经有列表、非空列表、映射和大多数其他类型的实例来自您认为的标准 Haskell 库。

请注意,类型参数 ,l是一种类型*,而不是您对 . 容器的期望* -> *Item l您提供完全应用的类型,并且可以根据需要使用类型相等来约束类型。例如:

{-# LANGUAGE TypeFamilies #-}
module Walk
  ( walk
  ) where

import           Control.Monad
import           Control.Monad.IO.Class
import           Data.List
import           System.Directory
import           System.FilePath
import           GHC.Exts

walk :: (IsList l, Item l ~ (FilePath,[FilePath]), MonadIO m) => FilePath -> m l
walk root =
  do entries <- liftIO $ listDirectory root
     (files, dirs) <- partition snd <$> liftM2 (<$>) zip (mapM (liftIO . doesFileExist . (root </>))) entries
     fromList . ((root, map fst files) :) . concat <$> mapM (walk . (root </>) . fst) dirs
于 2019-06-01T16:34:06.553 回答