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
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 anIOaction, but that’s only valid indonotation (without extensions, at least).The problem is that
xmlGetWidgetreturnsIO Entry, but you want a map ofEntry. That means you’ll have to compose thoseIOactions into a larger one.In the end, you’ll want something like this:
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
mapMto mapgetEntryover the list of names. Note the difference betweenmapandmapM. If I had usedmap, 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
IOaction, I convert it to aMapusingfromListwith the operator<$>. Also known asfmap,<$>applies a pure function to the thing inside theIO, so the result is of typeIO (Map String Entry).Finally, the result of this
IOaction can be bound toentries.