1

I am trying to read and print the output from the "readProcess" command mapped onto a list of filenames:

files <- readProcess "ls" [] []
let mdList = map ( \file -> do
    md <- readProcess "mdls" [file] []
    return md ) $ splitOn "\n" files in
    map (\md -> putStrLn md) mdList
putStrLn "Complete"

Each time I try to map putStrLn onto the mdList, I get this error:

Couldn't match type ‘IO String’ with ‘[Char]’

I have read many StackOverflow answers that seem to just use putStrLn on an IO String but I am unable to do so. Also, I am new to Haskell so any other tips are also appreciated.

genghiskhan
  • 1,095
  • 12
  • 21
  • 4
    An `IO String` value is **not** a string. It doesn't make sense to want to put an `IO String`, for the same reason you wouldn't want to eat a cookbook. – leftaroundabout Oct 30 '17 at 16:17
  • I am aware than an IO String is not a string. I am asking how to write an IO String. – genghiskhan Oct 30 '17 at 16:20
  • what you want `forM_`. your problem raised because you don't understand monad. you'd better try to understand what monad is in the first place. – Jason Hu Oct 30 '17 at 16:22
  • 1
    @genghiskhan What would you expect to get when you "write" an `IO String`? Machine code? Assembly instructions? What? – Thomas M. DuBuisson Oct 30 '17 at 16:23
  • I don't really think the duplication is right judging from OP's intention. – Jason Hu Oct 30 '17 at 16:23
  • @HuStmpHrrr perhaps the question I've linked to doesn't quite match; by all means edit the duplicate link. This question definitely is a duplicate though; this is a common subject of confusion. The only solution is properly learning how monads work. – leftaroundabout Oct 30 '17 at 16:25
  • @ThomasM.DuBuisson Maybe I was unclear. I want to see what the value of mdList is for debugging purposes. Therefore, I through I would want to print each element in that list to stdout. – genghiskhan Oct 30 '17 at 16:26
  • I vote to reopen. The answer to the claimed duplicate is `(<$>)`, but the answer to this question is `(>>=)` or maybe even more complicated than that. That this question is common I believe; but that particular choice of alternate question isn't a good fit. – Daniel Wagner Oct 30 '17 at 16:26
  • I clearly don't have a great understanding of monads but isn't >>= equivalent to a do block? – genghiskhan Oct 30 '17 at 16:29
  • 1
    @genghiskhan yes, it is equivalent to a `do` block. But if you shove a `do` block in a function argument, it'll always be a monadic action, not a pure value such as the function might accept. – leftaroundabout Oct 30 '17 at 16:30
  • 2
    The type of `map putStrLn` is `[String] -> [IO ()]`, but every statement in your `do` block must have type `IO X` for some `X` (and clearly `[IO ()]` can't match `IO X` for any `X`). However, `mapM_ putStrLn :: [String] -> IO ()`. – user2407038 Oct 30 '17 at 17:01
  • cf. https://stackoverflow.com/questions/11323300/using-return-vs-not-using-return-in-the-list-monad/11326549#11326549. disregard the question, just read that answer. it's got a nice picture. *blue* is for computation descriptions (i.e. "monadic actions"), *red* (and an accidental magenta) is for values. – Will Ness Oct 30 '17 at 18:23
  • monad is *something* that lets you mix, under a certain discipline, the computation descriptions (with their own primitives like `putStrLn` etc.) and the pure calculations using values these computations *will* produce. that's all. – Will Ness Oct 30 '17 at 18:31
  • Good suggestion by Will Ness, though beware: that is discussing the _list monad_ rather than IO. – leftaroundabout Oct 30 '17 at 19:23
  • IO or [], the blue slots are treated as monadic action descriptions, and the red slots are treated as pure values, regardless. Of course IO and [] are different, but the schema for the mixing is the same. – Will Ness Oct 30 '17 at 20:50

1 Answers1

3

You are using

map :: (a -> b) -> [a] -> [b]

which specializes to

map :: (a -> IO b) -> [a] -> [IO b]

The final result, [IO b] is not what we need. That is a list of IO actions, i.e. the equivalent of a list of non-executed no-arguments imperative functions. Instead, we want a single IO action, whose result is a list of b: that would be IO [b] instead of [IO b].

The library provides that as well:

mapM :: (a -> IO b) -> [a] -> IO [b]

or, if we don't care about collecting the results

mapM_ :: (a -> IO b) -> [a] -> IO ()

The library also provides variants with flipped arguments:

for  :: [a] -> (a -> IO b) -> IO [b]
for_ :: [a] -> (a -> IO b) -> IO ()

So, the original code can be fixed as follows:

import Data.Foldable

files <- readProcess "ls" [] []
for_ files $ \file -> do
    md <- readProcess "mdls" [file] []
    putStrLn md
putStrLn "Complete"
chi
  • 111,837
  • 3
  • 133
  • 218