Writing an IO String to stdout in Haskell - haskell

How do we print the output of a function that returns an IO String to the stdout? I am not able to use show or print.

If you want to print the result of the function foo :: Int -> IO String (for example), you can do
main = do
str <- foo 12
putStrLn str
or, without the do-notation,
main = foo 12 >>= putStrLn.
The do-notation is syntactic sugar for the second, which uses the fundamental (>>=) combinator, which has the type
(>>=) :: Monad m => m a -> (a -> m b) -> m b
IO is an instance of the Monad class, so you can use it here.
foo :: Int -> IO String
foo 12 :: IO String
putStrLn :: String -> IO ()
(foo 12) >>= putStrLn :: IO ()

How do we print the output of a function that returns an IO String to the stdout?
Well let's see. Here's a function that returns an IO String:
dumbFunction :: a -> IO String
dumbFunction x = getLine
dumbFunction is a dumb function (but a function nonetheless!). It ignores its input, and then returns getLine, which has the type IO String.
So you tell me, how do you print getLine :: IO String? The answer is, you don't! This is what we call an "IO action". Note that an IO action is not a function, because it does not take input. (However, IO actions might acquire input from, well, IO operations such as reading stdin, as getLine does. But it is not considered a "function" because it does not accept any traditional input)
So instead of printing out the action itself, you probably want to run the action, and then print the result. This can be done as Daniel Fischer described (with <-, which can be thought of as the "run" operator).

Related

What is the purpose of liftIO?

I have the following code snippet from internet:
calculateLength :: LengthMonad Int
calculateLength = do
-- all the IO operations have to be lifted to the IO monad in the monad stack
liftIO $ putStrLn "Please enter a non-empty string: "
s <- liftIO getLine
if null s
then throwError "The string was empty!"
else return $ length s
and could not understand, why the author use liftIO?
What is the purpose of liftIO?
It is defined as follows:
class (Monad m) => MonadIO m where
-- | Lift a computation from the 'IO' monad.
liftIO :: IO a -> m a
Is it possible to lift IO a -> [a]? It looks like natural transformation.
IO operations like getLine, putStrLn "..." only work inside the IO monad. Using them inside any other monad will trigger a type error.
Still, there are many monads M which are defined in terms of IO (e.g. StateT Int IO, and apparently your LengthMonad as well) and because of that they allow IO actions to be converted into M-actions, and executed as such.
However, we need a conversion for each M:
convertIOintoM1 :: IO a -> M1 a
convertIOintoM2 :: IO a -> M2 a
convertIOintoM3 :: IO a -> M3 a
...
Since this is cumbersome, the library defines a typeclass MonadIO having such conversion function, so that all the functions above can be named liftIO instead.
In practice, liftIO is used each time one wants to run IO actions in another monad, provided such monad allows for it.

Monad transformers with IO and Maybe

I am trying to stack up IO and Maybe monads but either I don't understand monad transformers well enough or this is not possible using transformers. Can some one help me understand this?
f :: String -> Maybe String
main :: IO ()
main = do
input <- getLine -- IO String
output <- f input -- Maybe String (Can't extract because it is IO do block)
writeFile "out.txt" output -- gives error because writeFile expects output :: String
In the above simplified example, I have a function f that returns a Maybe String and I would like to have a neat way of extracting this in the IO do block. I tried
f :: String -> MaybeT IO String
main :: IO ()
main = do
input <- getLine -- IO String
output <- runMaybeT (f input) -- Extracts output :: Maybe String instead of String
writeFile "out.txt" output -- gives error because writeFile expects output :: String
which lets me extract the Maybe String out in the second line of do block but I need to extract the string out of that. Is there a way to do this without using case?
Let's stick for a moment with your first snippet. If f input is a Maybe String, and you want to pass its result to writeFile "out.txt", which takes a String, you need to deal with the possibility of f input being Nothing. You don't have to literally use a case-statement. For instance:
maybe from the Prelude is case analysis packaged as a function;
fromMaybe from Data.Maybe lets you easily supply a default value, if that makes sense for your use case;
traverse_ and for_ from Data.Foldable could be used to silently ignore Nothing-ness:
for_ (f input) (writeFile "out.txt") -- Does nothing if `f input` is `Nothing`.
Still, no matter what you choose to do, it will involve handling Nothing somehow.
As for MaybeT, you don't really want monad transformers here. MaybeT IO is for when you want something like a Maybe computation but in which you can also include IO computations. If f :: String -> Maybe String already does what you want, you don't need to add an underlying IO layer to it.

Haskell: read a text file of doubles and assign a list containing them to a list variable

Okay, I'm new to the Haskell community having come from Python and this is driving me crazy.
I have a text file looking something like:
"1.2
1.423
2.43".
I want to read this text file and store it as a list of doubles in list_var. So list_var = [1.2,1.423,2.43]. This list_var will be used further in the program.
I just don't seem to find an answer on how to do this, most answers can print out list_var, e.g. Haskell - Read a file containing numbers into a list but I need list_var further down the line!
I have tried:
get_coefficients :: String -> [Double]
get_coefficients file_1 = do
coefficients_fromfile <- readLines "test2.txt"
let coefficients = map readDouble coefficients_fromfile
return coefficients
which doesn't work, readLines is
readLines :: FilePath -> IO [String]
readLines = fmap lines . readFile
and readDouble is
readDouble :: String -> Double
readDouble = read
Thanks in advance!
Since you use return, your output is in a monad, in this case the IO monad. As the error message tells you, you should change this line:
get_coefficients :: String -> [Double]
To this:
get_coefficients :: String -> IO [Double]
This is because of a core principle of Haskell: referential transparency.
If you want to use the [Double] produced, you still have to keep it in an IO monad, like so:
main :: IO ()
main = do
-- This can be thought of as taking out values from the monad,
-- but requires the promise that it'll be put back into a monad later.
doubles <- get_coefficients "This argument does nothing, why?"
-- This prints the list of doubles. Note: it returns an IO (),
-- thus fufills the promise!
-- print :: Show a => a -> IO ()
print doubles

How to use readFile

I am having trouble reading in a level file in Haskell. The goal is to read in a simple txt file with two numbers seperated by a space and then commas. The problem I keep getting is this: Couldn't match type `IO' with `[]'
If I understand correctly the do statement is supposed to pull the String out of the Monad.
readLevelFile :: FilePath -> [FallingRegion]
readLevelFile f = do
fileContent <- readFile f
(map lineToFallingRegion (lines fileContent))
lineToFallingRegion :: String -> FallingRegion
lineToFallingRegion s = map textShapeToFallingShape (splitOn' (==',') s)
textShapeToFallingShape :: String -> FallingShape
textShapeToFallingShape s = FallingShape (read $ head numbers) (read $ head
$ tail numbers)
where numbers = splitOn' (==' ') s
You can't pull things out of IO. You can think of IO as a container (in fact, some interpretations of IO liken it to the box containing Schrödinger's cat). You can't see what's in the container, but if you step into the container, values become visible.
So this should work:
readLevelFile f = do
fileContent <- readFile f
return (map lineToFallingRegion (lines fileContent))
It does not, however, have the type given in the OP. Inside the do block, fileContent is a String value, but the entire block is still inside the IO container.
This means that the return type of the function isn't [FallingRegion], but IO [FallingRegion]. So if you change the type annotation for readLevelFile to
readLevelFile :: FilePath -> IO [FallingRegion]
you should be able to get past the first hurdle.
Let's look at your first function with explicit types:
readLevelFile f = do
(fileContent :: String) <-
(readFile :: String -> IO String) (f :: String) :: IO String
fileContent is indeed of type String but is only available within the execution of the IO Monad under which we are evaluating. Now what?
(map lineToFallingRegion (lines fileContent)) :: [String]
Now you are suddenly using an expression that is not an IO monad but instead is a list value - since lists are also a type of monad the type check tries to unify IO with []. What you actually wanted is to return this value:
return (map lineToFallingRegion (lines fileContent)) :: IO [String]
Now recalling that we can't ever "exit" the IO monad your readLevelFile type must be IO - an honest admission that it interacts with the outside world:
readLevelFile :: FilePath -> IO [FallingRegion]

After reading a file I have IO [Char], but I need [IO Char]

I have a file number.txt which contains a large number and I read it into an IO String like this:
readNumber = readFile "number.txt" >>= return
In another function I want to create a list of Ints, one Int for each digit…
Lets assume the content of number.txt is:
1234567890
Then I want my function to return [1,2,3,4,5,6,7,8,9,0].
I tried severall versions with map, mapM(_), liftM, and, and, and, but I got several error messages everytime, which I was able to reduce to
Couldn't match expected type `[m0 Char]'
with actual type `IO String'
The last version I have on disk is the following:
module Main where
import Control.Monad
import Data.Char (digitToInt)
main = intify >>= putStrLn . show
readNumber = readFile "number.txt" >>= return
intify = mapM (liftM digitToInt) readNumber
So, as far as I understand the error, I need some function that takes IO [a] and returns [IO a], but I was not able to find such thing with hoogle… Only the other way round seemes to exist
In addition to the other great answers here, it's nice to talk about how to read [IO Char] versus IO [Char]. In particular, you'd call [IO Char] "an (immediate) list of (deferred) IO actions which produce Chars" and IO [Char] "a (deferred) IO action producing a list of Chars".
The important part is the location of "deferred" above---the major difference between a type IO a and a type a is that the former is best thought of as a set of instructions to be executed at runtime which eventually produce an a... while the latter is just that very a.
This phase distinction is key to understanding how IO values work. It's also worth noting that it can be very fluid within a program---functions like fmap or (>>=) allow us to peek behind the phase distinction. As an example, consider the following function
foo :: IO Int -- <-- our final result is an `IO` action
foo = fmap f getChar where -- <-- up here getChar is an `IO Char`, not a real one
f :: Char -> Int
f = Data.Char.ord -- <-- inside here we have a "real" `Char`
Here we build a deferred action (foo) by modifying a deferred action (getChar) by using a function which views a world that only comes into existence after our deferred IO action has run.
So let's tie this knot and get back to the question at hand. Why can't you turn an IO [Char] into an [IO Char] (in any meaningful way)? Well, if you're looking at a piece of code which has access to IO [Char] then the first thing you're going to want to do is sneak inside of that IO action
floob = do chars <- (getChars :: IO [Char])
...
where in the part left as ... we have access to chars :: [Char] because we've "stepped into" the IO action getChars. This means that by this point we've must have already run whatever runtime actions are required to generate that list of characters. We've let the cat out of the monad and we can't get it back in (in any meaningful way) since we can't go back and "unread" each individual character.
(Note: I keep saying "in any meaningful way" because we absolutely can put cats back into monads using return, but this won't let us go back in time and have never let them out in the first place. That ship has sailed.)
So how do we get a type [IO Char]? Well, we have to know (without running any IO) what kind of IO operations we'd like to do. For instance, we could write the following
replicate 10 getChar :: [IO Char]
and immediately do something like
take 5 (replicate 10 getChar)
without ever running an IO action---our list structure is immediately available and not deferred until the runtime has a chance to get to it. But note that we must know exactly the structure of the IO actions we'd like to perform in order to create a type [IO Char]. That said, we could use yet another level of IO to peek at the real world in order to determine the parameters of our action
do len <- (figureOutLengthOfReadWithoutActuallyReading :: IO Int)
return $ replicate len getChar
and this fragment has type IO [IO Char]. To run it we have to step through IO twice, we have to let the runtime perform two IO actions, first to determine the length and then second to actually act on our list of IO Char actions.
sequence :: [IO a] -> IO [a]
The above function, sequence, is a common way to execute some structure containing a, well, sequence of IO actions. We can use that to do our two-phase read
twoPhase :: IO [Char]
twoPhase = do len <- (figureOutLengthOfReadWithoutActuallyReading :: IO Int)
putStrLn ("About to read " ++ show len ++ " characters")
sequence (replicate len getChar)
>>> twoPhase
Determining length of read
About to read 22 characters
let me write 22 charac"let me write 22 charac"
You got some things mixed up:
readNumber = readFile "number.txt" >>= return
the return is unecessary, just leave it out.
Here is a working version:
module Main where
import Data.Char (digitToInt)
main :: IO ()
main = intify >>= print
readNumber :: IO String
readNumber = readFile "number.txt"
intify :: IO [Int]
intify = fmap (map digitToInt) readNumber
Such a function can't exists, because you would be able to evaluate the length of the list without ever invoking any IO.
What is possible is this:
imbue' :: IO [a] -> IO [IO a]
imbue' = fmap $ map return
Which of course generalises to
imbue :: (Functor f, Monad m) => m (f a) -> m (f (m a))
imbue = liftM $ fmap return
You can then do, say,
quun :: IO [Char]
bar :: [IO Char] -> IO Y
main = do
actsList <- imbue quun
y <- bar actsLists
...
Only, the whole thing about using [IO Char] is pointless: it's completely equivalent to the much more straightforward way of working only with lists of "pure values", only using the IO monad "outside"; how to do that is shown in Markus's answer.
Do you really need many different helper functions? Because you may write just
main = do
file <- readFile "number.txt"
let digits = map digitToInt file
print digits
or, if you really need to separate them, try to minimize the amount of IO signatures:
readNumber = readFile "number.txt" --Will be IO String
intify = map digitToInt --Will be String -> [Int], not IO
main = readNumber >>= print . intify

Resources