0

I am trying to make a function that takes a list of strings and executes the command putStrLn or print (I think they are basically equivalent, please correct me if I am wrong as I'm still new to Haskell) to every element and have it printed out on my terminal screen. I was experimenting with the map function and also with lambda/anonymous functions as I already know how to do this recursively but wanted to try a more complex non recursive version. map returned a list of the type IO() which was not what I was going for and my attempts at lambda functions did not go according to plan. The basic code was:

test :: [String] -> something
test x = map (\a->putStrLn a) x -- output for this function would have to be [IO()]

Not entirely sure what the output of the function was supposed to be either which also gave me issues.

I was thinking of making a temp :: String variable and have each String appended to temp and then putStrLn temp but was not sure how to do that entirely. I though using where would be viable but I still ran into issues. I know how to do this in languages like java and C but I am still quite new to Haskell. Any help would be appreciated.

  • And by non-recursive, I mean using the test function once. I know `map` is recursive but I only care about test not being recursive. – theGreatOne Mar 06 '19 at 04:21
  • Regarding your comment about `print` and `putStrLn`, they're similar but it's important to know the difference. I was about to explain it myself, but while googling to check I wasn't being stupid I found [this question](https://stackoverflow.com/questions/19288652/difference-between-print-and-putstrln-in-haskell) which is exactly this, with a good detailed answer. – Robin Zigmond Mar 06 '19 at 07:09

2 Answers2

5

There is a special version of map that works with monadic functions, it's called mapM:

test :: [String] -> IO [()]
test x = mapM putStrLn x

Note that this way the return type of test is a list of units - that's because each call to putStrLn returns a unit, so result of applying it to each element in a list would be a list of units. If you'd rather not deal with this silliness and have the return type be a plain unit, use the special version mapM_:

test :: [String] -> IO ()
test x = mapM_ putStrLn x
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • 2
    And `mapM` has many cousins, including `traverse` for applicative traversals over foldable structures (including lists). – Thomas M. DuBuisson Mar 06 '19 at 04:44
  • Thanks that was so easy with that function! Sort of takes the understanding out of the equation though. I may have a look at the source code for it then. – theGreatOne Mar 06 '19 at 05:01
  • 1
    The source code for it is very simple, but recursive, and you explicitly said you didn't want recursion. – Fyodor Soikin Mar 06 '19 at 05:02
  • I don't want my test function to be recursive but I am fine using recursive functions. It's more just me trying to learn more methods other than coding the whole thing myself, as I already know how to solve this question with a self-made recursive function. Mostly just expanding my knowledge of Haskell and the functions it comes with. – theGreatOne Mar 06 '19 at 05:17
2

I was thinking of making a temp :: String variable and have each String appended to temp and then putStrLn temp

Good idea. A pattern of "render the message" then a separate "emit the message" is often nice to have long term.

test xs = let temp = unlines (map show xs)
          in putStrLn temp

Or just

test xs = putStrLn (unlines (show <$> xs))

Or

test = putStrLn . unlines . map show

Not entirely sure what the output of the function was supposed to be either which also gave me issues.

Well you made a list of IO actions:

test :: [String] -> [IO ()]
test x = map (\a->putStrLn a) x

So with this list of IO actions when do you want to execute them? Now? Just once? The first one many times the rest never? In what order?

Presumably you want to execute them all now. Let's also eta reduce (\a -> putStrLn a) to just putStrLn since that means the same thing:

test :: [String] -> IO ()
test x = sequence_ (map (\a->putStrLn a) x)
Thomas M. DuBuisson
  • 64,245
  • 7
  • 109
  • 166