如何将haskell List转换为使用列表值进行操作的一元函数



我很难将列表转换为使用列表值的一元函数。

例如,我有一个列表[("dir1/content1", "1"), ("dir1/content11", "11"), ("dir2/content2", "2"), ("dir2/content21", "21")],我想将其转换为映射到以下do语句的一元函数:

do
mkBlob ("dir1/content1", "1")
mkBlob ("dir1/content11", "11")
mkBlob ("dir2/content2", "2")
mkBlob ("dir2/content21", "21")

我想这是一个类似的功能:

contentToTree [] = return 
contentToTree (x:xs) = (mkBlob x) =<< (contentToTree xs)

但这不起作用,失败时出现错误:

• Couldn't match expected type ‘() -> TreeT LgRepo m ()’
with actual type ‘TreeT LgRepo m ()’
• Possible cause: ‘(>>=)’ is applied to too many arguments
In the expression: (mkBlob x) >>= (contentToTree xs)
In an equation for ‘contentToTree’:
contentToTree (x : xs) = (mkBlob x) >>= (contentToTree xs)
• Relevant bindings include
contentToTree :: [(TreeFilePath, String)] -> () -> TreeT LgRepo m ()

我不太明白如何使它发挥作用。

这是我的相关代码:

import Data.Either
import Git
import Data.Map
import Conduit
import qualified Data.List as L
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy as BL
import Control.Monad (join)
type FileName = String 

data Content = Content {
content :: Either (Map FileName Content) String
} deriving (Eq, Show) 
contentToPaths :: String -> Content -> [(TreeFilePath, String)]
contentToPaths path (Content content) = case content of
Left m -> join $ L.map ((k, v) -> (contentToPaths (if L.null path then k else path ++ "/" ++ k) v)) $ Data.Map.toList m
Right c -> [(BS.pack path, c)]
mkBlob :: MonadGit r m => (TreeFilePath, String) -> TreeT r m ()
mkBlob (path, content) = putBlob path
=<< lift (createBlob $ BlobStream $
sourceLazy $ BL.fromChunks [BS.pack content])
sampleContent = Content $ Left $ fromList [
("dir1", Content $ Left $ fromList [
("content1", Content $ Right "1"),
("content11", Content $ Right "11")
]),
("dir2", Content $ Left $ fromList [
("content2", Content $ Right "2"),
("content21", Content $ Right "21")
])
]

如果有任何提示或帮助,我们将不胜感激。

您有:

  • 某种类型的值的列表a(在本例中为a ~ (String, String)(。因此,xs :: [a]
  • 在一元上下文中从a到某种类型b的函数fm b。由于您忽略了返回值,我们可以想象b ~ ()。因此,f :: Monad m => a -> m ()

您想要执行操作,生成一些一元上下文和一个不重要的值m ()。所以总的来说,我们想要一些函数doStuffWithList :: Monad m => [a] -> (a -> m ()) -> m ()。我们可以在Hoogle中搜索这种类型,它会产生一些结果。不幸的是,由于我们选择了对参数进行排序,前几个结果是其他包中很少使用的函数。如果你进一步滚动,你会发现base中的内容非常有前景。事实证明,您要查找的函数是traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()。有了它,我们可以用替换您的do块

traverse_ mkBlob [ ("dir1/content1", "1")
, ("dir1/content11", "11")
, ("dir2/content2", "2")
, ("dir2/content21", "21")
]

碰巧,这个功能有很多名字,有些是历史原因,有些是风格原因。mapM_forM_for_都是相同的,并且都在base中,所以您可以使用其中的任何一个。但是M_版本现在已经不受欢迎了,因为实际上你只需要Applicative,而不需要Monad;并且CCD_ 23版本按照对lambda方便但对命名函数不方便的顺序接受它们的参数。所以,我建议traverse_

假设mkBlob是一个看起来像的函数

mkBlob :: (String, String) -> M ()

如果M是某个特定的monad,那么您就有了列表

xs = [("dir1/content1", "1"), ("dir1/content11", "11"), ("dir2/content2", "2"), ("dir2/content21", "21")]

其类型为CCD_ 27。我们需要的第一件事是在每个元素上运行mkBlob函数,即通过map

map mkBlob xs :: [M ()]

现在,我们有一个一元操作的列表,所以我们可以使用sequence按顺序运行它们。

sequence (map mkBlob xs) :: M [()]

得到的[()]值几乎是无用的,所以我们可以使用void来消除它

void . sequence . map mkBlob $ xs :: M ()

现在,在Haskell中,void . sequence被称为sequence_(因为这种模式相当常见(,sequence . map被称为mapM。将两者放在一起,您想要的函数称为mapM_

mapM_ mkBlob xs :: M ()

最新更新