如何将 haskell 列表转换为使用列表值进行操作的单子函数?
How to convert a haskell List into a monadic function that uses list values for operations?
我无法集中精力将列表转换为使用列表值的单子函数。
例如,我有一个列表 [("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]
- 一个函数
f
从 a
到 monadic 上下文中的某种类型 b
,m b
。由于您忽略了 return 值,我们可以想象 b ~ ()
。所以,f :: Monad m => a -> m ()
.
您想执行操作,产生一些单子上下文和一个不重要的值,m ()
。所以总的来说,我们需要一些函数 doStuffWithList :: Monad m => [a] -> (a -> m ()) -> m ()
。我们可以search Hoogle for this type, and it yields some results. Unfortunately, as we've chosen to order the arguments, the first several results are little-used functions from other packages. If you scroll further, you start to find stuff in base
- very promising. As it turns out, the function you are looking for is traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()
。这样,我们就可以用以下内容替换您的 do-block:
traverse_ mkBlob [ ("dir1/content1", "1")
, ("dir1/content11", "11")
, ("dir2/content2", "2")
, ("dir2/content21", "21")
]
碰巧这个函数有很多名称,有些是出于历史原因,有些是出于风格原因。 mapM_
、forM_
和 for_
都是相同的,并且都在 base
中,因此您可以使用其中任何一个。但是现在 M_
版本不受欢迎,因为实际上您只需要 Applicative
,而不是 Monad
; for
版本采用对 lambdas 方便但对命名函数不方便的顺序获取参数。所以,traverse_
是我建议的那个。
假设 mkBlob
是一个看起来像
的函数
mkBlob :: (String, String) -> M ()
其中 M
是一些特定的 monad,那么你就有了列表
xs = [("dir1/content1", "1"), ("dir1/content11", "11"), ("dir2/content2", "2"), ("dir2/content21", "21")]
类型为xs :: [(String, String)]
。我们需要做的第一件事是 运行 每个 元素上的 mkBlob
函数,即通过 map
.
map mkBlob xs :: [M ()]
现在,我们有一个 monadic 动作列表,所以我们可以使用 sequence
到 运行 它们的顺序。
sequence (map mkBlob xs) :: M [()]
生成的 [()]
值几乎没有用,所以我们可以使用 void
来摆脱它
void . sequence . map mkBlob $ xs :: M ()
现在,void . sequence
在 Haskell 中被称为 sequence_
(因为这种模式相当普遍),而 sequence . map
被称为 mapM
。把两者放在一起,你要的函数就叫mapM_
.
mapM_ mkBlob xs :: M ()
我无法集中精力将列表转换为使用列表值的单子函数。
例如,我有一个列表 [("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]
- 一个函数
f
从a
到 monadic 上下文中的某种类型b
,m b
。由于您忽略了 return 值,我们可以想象b ~ ()
。所以,f :: Monad m => a -> m ()
.
您想执行操作,产生一些单子上下文和一个不重要的值,m ()
。所以总的来说,我们需要一些函数 doStuffWithList :: Monad m => [a] -> (a -> m ()) -> m ()
。我们可以search Hoogle for this type, and it yields some results. Unfortunately, as we've chosen to order the arguments, the first several results are little-used functions from other packages. If you scroll further, you start to find stuff in base
- very promising. As it turns out, the function you are looking for is traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()
。这样,我们就可以用以下内容替换您的 do-block:
traverse_ mkBlob [ ("dir1/content1", "1")
, ("dir1/content11", "11")
, ("dir2/content2", "2")
, ("dir2/content21", "21")
]
碰巧这个函数有很多名称,有些是出于历史原因,有些是出于风格原因。 mapM_
、forM_
和 for_
都是相同的,并且都在 base
中,因此您可以使用其中任何一个。但是现在 M_
版本不受欢迎,因为实际上您只需要 Applicative
,而不是 Monad
; for
版本采用对 lambdas 方便但对命名函数不方便的顺序获取参数。所以,traverse_
是我建议的那个。
假设 mkBlob
是一个看起来像
mkBlob :: (String, String) -> M ()
其中 M
是一些特定的 monad,那么你就有了列表
xs = [("dir1/content1", "1"), ("dir1/content11", "11"), ("dir2/content2", "2"), ("dir2/content21", "21")]
类型为xs :: [(String, String)]
。我们需要做的第一件事是 运行 每个 元素上的 mkBlob
函数,即通过 map
.
map mkBlob xs :: [M ()]
现在,我们有一个 monadic 动作列表,所以我们可以使用 sequence
到 运行 它们的顺序。
sequence (map mkBlob xs) :: M [()]
生成的 [()]
值几乎没有用,所以我们可以使用 void
来摆脱它
void . sequence . map mkBlob $ xs :: M ()
现在,void . sequence
在 Haskell 中被称为 sequence_
(因为这种模式相当普遍),而 sequence . map
被称为 mapM
。把两者放在一起,你要的函数就叫mapM_
.
mapM_ mkBlob xs :: M ()