我有以下实现目录漫游的模块:
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]))
我认为:
失败了吗?我不能确定我该如何解决?
答案 0 :(得分:5)
我认为
:
失败了吗?
的确如此。如果使用(:)
来构建结构,则该结构将是一个列表,并且您无法更改walk
的类型来声明它返回任意可遍历的结构。也没有一个很好的以Traversable
为中心的解决方法,或者:Traversable
意味着您通过其Foldable
超类拥有一个toList
,但没有一个{{1} }。
答案 1 :(得分:2)
列表的多态生成以及通常为多态容器设计类已被证明比最初看起来要困难得多。 GHC目前用于生产完全多态容器的解决方案是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