I want to make an entries :: Map(String -> Entry)
so I can easily access each entry by name. To this end, I have the code
Just xml ← xmlNew "blah.glade"
...
entries ← fromList $ [(name,entry) | name <- entryList
, entry <- xmlGetWidget xml castToEntry name]
(where entryList
is a list of entry names, e.g. ["name1","name2",...]
).
However, the list comprehension comes up with the following error:
Couldn't match expected type `[t]' against inferred type `IO Entry'
In the expression: xmlGetWidget xml castToEntry name
开发者_Python百科 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]'
I can't see why it's expecting a list of anything. Can anyone help with this?
It's because <-
in a list comprehension expects the right hand side to be a list. You're trying to use it to bind the result of an IO
action, but that's only valid in do
notation (without extensions, at least).
The problem is that xmlGetWidget
returns IO Entry
, but you want a map of Entry
. That means you'll have to compose those IO
actions into a larger one.
In the end, you'll want something like this:
let getEntry name = do entry <- xmlGetWidget xml castToEntry name
return (name, entry)
entries <- fromList <$> mapM getEntry entryList
Here, I've created a helper function getEntry :: String -> IO (String, Entry)
to get the entry and pair it up with its name.
Next, I use mapM
to map getEntry
over the list of names. Note the difference between map
and mapM
. If I had used map
, I would have gotten a list of actions, i.e. [IO (String, Entry)]
, when what I want is an action returning a list, i.e. IO [(String, Entry)]
.
Now, having constructed that IO
action, I convert it to a Map
using fromList
with the operator <$>
. Also known as fmap
, <$>
applies a pure function to the thing inside the IO
, so the result is of type IO (Map String Entry)
.
Finally, the result of this IO
action can be bound to entries
.
精彩评论