我在將串列轉換為使用串列值的 monadic 函式時遇到了麻煩。
例如,我有一個串列[("dir1/content1", "1"), ("dir1/content11", "11"), ("dir2/content2", "2"), ("dir2/content21", "21")],我想將其轉換為映射到以下do陳述句的 monadic 函式:
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")
])
]
將不勝感激任何提示或幫助。
uj5u.com熱心網友回復:
你有:
- 某種型別的值串列
a(在本例中為a ~ (String, String))。所以,xs :: [a] - 一元背景關系中
f從a到某種型別的函式b,m 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-block:
traverse_ mkBlob [ ("dir1/content1", "1")
, ("dir1/content11", "11")
, ("dir2/content2", "2")
, ("dir2/content21", "21")
]
碰巧這個函式有很多名字,有些是出于歷史原因,有些是出于風格原因。mapM_、forM_、 和for_都相同并且都在 中base,因此您可以使用其中任何一個。但是M_這些天這些版本不受歡迎,因為實際上您只需要Applicative,而不是Monad;并且這些for版本按照對 lambda 很方便但對命名函式不方便的順序來接受它們的引數。所以,traverse_是我建議的。
uj5u.com熱心網友回復:
假設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 ()
現在,在 Haskell 中void . sequence被呼叫sequence_(因為這種模式相當普遍),并被sequence . map稱為mapM. 將兩者放在一起,您想要的函式稱為mapM_。
mapM_ mkBlob xs :: M ()
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/393115.html
標籤:哈斯克尔
下一篇:產品型別轉換為元組
