r.sendecky
r.sendecky

Reputation: 10353

Haskell: how to deal with IO monad inside another IO monad?

just started learning haskell - love it after a week. At the moment going through the monads pain, not there yet but hopefully it will click.

I am trying to put together a function similar to pythons walk() but simpler. Given a path I want to generate list of tuples. A tuple for each sub directory (lets just assume that there will be only directories). The tuple would contain path of the directory as its first element and list of files the directory contains as the second element.

I don't know if I explained it correctly but here is the code:

walkDir :: String -> IO [IO (FilePath, [FilePath])]
walkDir path = do
  dir <- getDirectoryContents path  
  let nd = [x | x <- dir, notElem x [".",".."]]
  return (map getcont nd)
    where
      getcont path = do
      cont <- getDirectoryContents path
      return (path,cont)

My concern is IO inside IO and how to deal with it?? Is it possible to cancel them out? Is it possible to unwrap at least the internal IO? Is this kind of return normal?

I can not even print this kind of return. Do I have to create an instance of show for this to be printed correctly?

There most likely exist a similar function in some haskell library but this is for educational purpose. I want to learn. So any suggestions welcome.

Thank you very much.

Upvotes: 4

Views: 929

Answers (2)

Dan Burton
Dan Burton

Reputation: 53665

Let's look at the types.

map :: (a -> b) -> [a] -> [b]
getCont :: FilePath -> IO (FilePath, [FilePath])
nd :: [FilePath]

map getCont nd :: [IO (FilePath, FilePath)]

Now, at this point, the structure looks inside-out. We have [IO a] but we want IO [a]. Stop! Hoogle time. Generalizing for any ol' monad, we hoogle [m a] -> m [a]. Lo and behold, sequence has that precise type signature. So instead of return :: a -> m a you should use sequence :: [m a] -> m [a], like this:

sequence (map getCont nd)

Then you'll be all set. Notice that this is essentially identical to Kurt S's solution, because

mapM f xs = sequence (map f xs)

Upvotes: 4

Kurt Stutsman
Kurt Stutsman

Reputation: 4034

Check out Control.Monad for mapM.

Then this

return (map getcont nd)

becomes

mapM getcont nd

Upvotes: 10

Related Questions