Reputation: 23
writeStr []=putChar ' '
writeStr (x:xs) = (putChar x)(writeStr xs)
Hello, and thanks in advance, i get a type error, it should be a simple answer but i just dont get where the error is coming from.
Upvotes: 2
Views: 502
Reputation: 17786
FUZxxl has answered the immediate question, but I'd like to expand on it with some more ways of writing "writeStr" to illustrate more about monads.
As delnan said in the comments, you can also write
writeStr [] = return ()
writeStr (x:xs) = putChar x >> writeStr xs
This is actually the "desugared" version of the "do" notation. The ">>" operator is used to daisy-chain monadic actions together. Its actually a specialised version of the "bind" operator, written ">>=". See this question for more details.
But when you look at this, it seems that all we are doing is applying "putChar" to each element in the argument list. There is already a function in the Prelude called "map" for doing this, so maybe we could write:
writeStr xs = map putChar xs
But when you try that it won't work. The reason becomes evident if you go into GHCi and type this:
:type map putChar "Hello"
[IO ()]
You want a single "IO()" action, but this gives you a list of them. What you need is a function that turns this list of IO actions into a single IO action. Fortunately one exists. The Prelude contains these two functions
sequence :: [IO a] -> IO [a]
sequence_ :: [IO a] -> IO ()
The first one is for when you want the list of results, the second is for cases where you don't, like this one. (Throughout this answer I'm going to be giving the IO-specific type signatures for clarity, but its important to remember that all these functions actually work for any monad.)
So now you can write:
writeStr xs = sequence_ $ map putChar xs
But there is a way of shortening this. Recall the "." operator, which sticks two functions together, and the way Haskell has of "currying" function arguments? We can rewrite the function above as:
writeStr = sequence_ . map putChar
This "point-free" style looks and feels very odd at first; it makes "writeStr" look more like a constant than a function. But it avoids the need to track variable names around the code when you are reading it, and so is often preferred. Its also a lot shorter and more readable when you are putting something complicated as the argument to "map" or similar higher order functions.
But we can go even shorter. The "sequence . map f" pattern is very common, so the "Control.Monad" module defines a couple more functions to embody it:
mapM :: (a -> IO b) -> [a] -> IO [b]
mapM f = sequence . map f
mapM_ :: (a -> IO b) -> [a] -> IO ()
mapM_ f = sequence_ . map f
So you can finally write
writeStr = mapM_ putChar
Upvotes: 3
Reputation: 92986
Your code is a bit strange. If I got it right, you try to print a string. Your method is to put the first string, and than the second. But it's not possible in Haskell to combine two IO actions like this. Have a look in your tutorial again about this, here's hwo it could look like:
writeStr [] = return () -- you had putChar ' ',
writeStr (x:xs) = do putChar x -- but this would print a superfluous whtiespace
writeStr xs
If you want to do several things sequentially, either use the do-keyword or monadic combinators. Its very easy, just like this:
do action1
action2
action3
...
Upvotes: 4