Jeff M.
2011-03-24 22:36:06 UTC
So, I'm diving trying to learn me some Haskell (coming from Common
Lisp), and while I conceptually understand the problems that monads
solve and - to a lesser degree - how they work, I'm just bashing my
head up against the wall trying to work with them. I find myself doing
"genetic programming" (read: try this... nope... try this... nope)
until I get something that works, and I'm hoping someone here can give
me a little help?
I'll read a tutorial, and it will give an example of parsing a text
file with something like this:
input = lines $ readFile "foo.txt"
Except when I try that, it doesn't work; GHC complains that readFile
returns a IO String and lines expects a String. More research reveals
both the fmap and liftM functions. I'll be frank in that I have yet to
comprehend what exactly these do, but I can get the above to work like
so:
input = liftM lines $ readFile "foo.txt"
Can someone explain what exactly they do (and how)?
Of course, when I go to use input anywhere I end up with the same
problem again. Is there a "trick" to not having to use liftM or fmap
with every subsequent function call? Better still, is there a way I
can expect the IO monad in the rest of my functions so I don't have to
do that extra work?
How about a more concrete, simple example... I have a file of names,
one per line:
John Smith
Alex Smith
...
I'd like to generate a (Data.Map [Char] [[Char]]) where the k/v pairs
for the above would be ("Smith", ["John","Alex"]). Just to bang out a
quick function...
type NameMap = (Map [Char] [[Char]])
names :: NameMap -> [[Char]] -> NameMap
names tree [] = tree
names tree (x:xs) = names (insertWith (++) last [first] tree) xs
where [first,last] = words x
Now, if I test the above function... names empty ["John Smith", "Alex
Smith"]... everything works as expected. So, let's try doing this from
an input file:
main = names empty (lines $ readFile "names.txt")
Okay, on to type errors. So, I know I can "lift" lines...
main = names empty (liftM lines $ readFile "names.txt")
But this just defers to error onto names, and at this point I feel
pretty solidly stuck. I don't know how to rectify this problem without
implementing an intermediate function thusly:
hack text = names empty text
main = liftM hack (liftM lines $ readFile "names.txt")
Sorry if this is long-winded. I just wanted to clearly show where I'm
having problems. I'm quite sure that I'm just missing a little tidbit
of knowledge that will make this all come together nicely (I hope). If
someone can take a few minutes to explain how I should code this to
work properly with the IO Monad - and any other monad - or at least
point me to a really good tutorial that I haven't been able to find
yet, it'd be much appreciated!
Thanks!
Jeff M.
Lisp), and while I conceptually understand the problems that monads
solve and - to a lesser degree - how they work, I'm just bashing my
head up against the wall trying to work with them. I find myself doing
"genetic programming" (read: try this... nope... try this... nope)
until I get something that works, and I'm hoping someone here can give
me a little help?
I'll read a tutorial, and it will give an example of parsing a text
file with something like this:
input = lines $ readFile "foo.txt"
Except when I try that, it doesn't work; GHC complains that readFile
returns a IO String and lines expects a String. More research reveals
both the fmap and liftM functions. I'll be frank in that I have yet to
comprehend what exactly these do, but I can get the above to work like
so:
input = liftM lines $ readFile "foo.txt"
Can someone explain what exactly they do (and how)?
Of course, when I go to use input anywhere I end up with the same
problem again. Is there a "trick" to not having to use liftM or fmap
with every subsequent function call? Better still, is there a way I
can expect the IO monad in the rest of my functions so I don't have to
do that extra work?
How about a more concrete, simple example... I have a file of names,
one per line:
John Smith
Alex Smith
...
I'd like to generate a (Data.Map [Char] [[Char]]) where the k/v pairs
for the above would be ("Smith", ["John","Alex"]). Just to bang out a
quick function...
type NameMap = (Map [Char] [[Char]])
names :: NameMap -> [[Char]] -> NameMap
names tree [] = tree
names tree (x:xs) = names (insertWith (++) last [first] tree) xs
where [first,last] = words x
Now, if I test the above function... names empty ["John Smith", "Alex
Smith"]... everything works as expected. So, let's try doing this from
an input file:
main = names empty (lines $ readFile "names.txt")
Okay, on to type errors. So, I know I can "lift" lines...
main = names empty (liftM lines $ readFile "names.txt")
But this just defers to error onto names, and at this point I feel
pretty solidly stuck. I don't know how to rectify this problem without
implementing an intermediate function thusly:
hack text = names empty text
main = liftM hack (liftM lines $ readFile "names.txt")
Sorry if this is long-winded. I just wanted to clearly show where I'm
having problems. I'm quite sure that I'm just missing a little tidbit
of knowledge that will make this all come together nicely (I hope). If
someone can take a few minutes to explain how I should code this to
work properly with the IO Monad - and any other monad - or at least
point me to a really good tutorial that I haven't been able to find
yet, it'd be much appreciated!
Thanks!
Jeff M.