我想制作entries :: Map(String -> Entry)
,以便我可以轻松按名称访问每个条目。为此,我有代码
Just xml ← xmlNew "blah.glade"
...
entries ← fromList $ [(name,entry) | name <- entryList
, entry <- xmlGetWidget xml castToEntry name]
(其中entryList
是条目名称列表,例如["name1","name2",...]
)。
但是,列表理解会出现以下错误:
Couldn't match expected type `[t]' against inferred type `IO Entry'
In the expression: xmlGetWidget xml castToEntry name
In a stmt of a list comprehension:
entry <- xmlGetWidget xml castToEntry name
In the second argument of `($)', namely
`[(name, entry) |
name <- entryList, entry <- xmlGetWidget xml castToEntry name]'
我不明白为什么它会期待任何列表。任何人都可以帮忙吗?
答案 0 :(得分:2)
这是因为列表理解中的<-
期望右侧是列表。您尝试使用它来绑定IO
操作的结果,但这仅在do
表示法中有效(至少没有扩展名)。
问题是xmlGetWidget
会返回IO Entry
,但您想要一张Entry
的地图。这意味着您必须将这些IO
动作组合成更大的动作。
最后,你会想要这样的东西:
let getEntry name = do entry <- xmlGetWidget xml castToEntry name
return (name, entry)
entries <- fromList <$> mapM getEntry entryList
在这里,我创建了一个辅助函数getEntry :: String -> IO (String, Entry)
来获取条目并将其与其名称配对。
接下来,我使用mapM
将getEntry
映射到名称列表上。请注意map
和mapM
之间的差异。如果我使用了map
,我会得到一个操作列表,即[IO (String, Entry)]
,当我想要的是一个返回列表的操作,即IO [(String, Entry)]
。
现在,构建了IO
操作后,我使用Map
将操作符fromList
转换为<$>
。也称为fmap
,<$>
将纯函数应用于IO
内的事物,因此结果为IO (Map String Entry)
类型。
最后,此IO
操作的结果可以绑定到entries
。