Haskell Read Integers from a file to a list - haskell

I have a simple text file with one line:
6 195 265 750 265 750 196
I have a function:
executeList :: Integer -> [Integer] -> [String]
executeList n x = [reverseAndAdd n i | i <- x]
That takes an integer, list of integer and returns an array of Strings.
What I want to do, is to read that text file to and [Integer] list and pass that to executeList function.
Here is my code:
main = do
let list = []
handle <- openFile "data.txt" ReadMode
contents <- hGetContents handle
let singlewords = words contents
list = f singlewords
print list
hClose handle
f :: [String] -> [Integer]
f = map read
I found it here:
Haskell file reading
When I run 'main' I get this output:
[6,195,265,750,265,750,196]
but when I try to pass it like this to executeList:
let list = main
executeList 0 list
I get this error:
<interactive>:103:15: error:
* Couldn't match expected type `[Integer]' with actual type `IO ()'
* In the second argument of `executeList', namely `list'
In the expression: executeList 0 list
In an equation for `it': it = executeList 0 list
If I check the type of that list, i get this:
list :: IO()
I looked up on the internet for how to transform IO() to [Integer] but found nothing useful. Maybe someone can show me the way to do that conversion?

The short answer is that you can't transform IO() into [Integer].
It seems as though you are misunderstanding the IO monad. Most functions return a value. Functions with a return type of IO a instead return an I/O action that performs some sort I/O before returning a value of type a. In your case IO () is an I/O action that will return () which is just an empty tuple. When you are writing console programs like this that read in data and then print out some results you'll typically follow this pattern:
Read input from file or command line
Pass data to function for computation
Print results
Your whole program will end up living inside of the IO monad. do is a notation that is a syntactic sugar for the bind operator >>=. This operator allows us to chain monadic computations together. The <- in your code extracts a value from a monad (in your case an IO action) and stores it in a variable. Lets take a look at the type signature of hGetContents. From GHCI we can learn that this function has a type of hGetContents :: Handle -> IO String It takes a Handle and returns an I/O action that returns a string. When you call contents <- hGetContents handle the program calls hGetContents with the file handle you specified and then extracts a string from the IO action that is returned and stores that string in the variable contents. So now you've read the input. After you've converted the numbers to actual integer types the next step is to call your function which is the simple call let data = executeList 0 list From there you can output you data with print data. It's important to keep in mind that the whole time you are in the IO monad. In the end your entire main function should look something like this:
main = do
handle <- openFile "data.txt" ReadMode
contents <- hGetContents handle
let singlewords = words contents
list = f singlewords
data = executeList 0 list
print data
hClose handle
f :: [String] -> [Integer]
f = map read

Related

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]

how to return an integer value from function without passing any argument in haskell

I need one function which read a file and return the number of lines in that file but it will not take anything as an argument.I have written this code but i am getting the error that couldnt match type IO Int with Int.
I am calling this function from another function as
let r=row'
Help me to figure it out
Thanks`
row'::()->Int
row' ()=do
content<-readFile "sample.txt"
let line_file=lines content
return (length line_file)
The problem here is that readFile and return are IO functions, and any function that performs IO in Haskell has to denote this in its type. You could instead write your function as
row' :: IO Int
row' = do
content <- readFile "sample.txt"
let line_file = lines content
return (length line_file)
And this would compile just fine. This restriction in the type signature is there to ensure that any function that has side effects or can return different values for the same input is sectioned off by the type system. This can greatly help in reasoning about your code, since for a pure function, such as
countRows :: String -> Int
countRows content = length (lines content)
Can always be guaranteed to return the same result for the same input. Then you can use these "pure" functions in "impure" functions to perform the actual calculations, keeping all the "impure" side effects contained to a single location:
row'' :: IO Int
row'' = do
content <- readFile "sample.txt"
return (countRows content)
Now row'' doesn't do anything other than read the file and pass the contents to a function that actually performs the work of counting the lines in that file. You could even state this more simply using fmap as
row'' :: IO Int
row'' = fmap countRows $ readFile "sample.txt"
To use this function, you can do something like
main :: IO ()
main = do
putStrLn "Counting the number of rows in sample.txt"
r <- row''
putStrLn $ "There are " ++ show r ++ " row(s) in sample.txt"
But note that row'' must be called from another IO function, you can not use it as
-- THIS IS WRONG, WON'T COMPILE
doubleRows :: Int
doubleRows = rows'' * 2
-- THIS IS WRONG TOO
doubleRows' :: Int
doubleRows' = do
r <- rows''
return (r * 2)
because both of these examples would have to have the type IO Int instead, and the first one wouldn't compile even with the right type signature because IO Int * Int doesn't make any sense, you have to first "extract" the Int from IO Int using the bind syntax of r <-.

another haskell IO issue

Ok, so let's say I have a function which takes a String and returns a certain value of that string specified by a text file. A text file will look like this.
hello - 2
bye - 3
so the function would return 2 given "hello" and 3 given "bye". Now, the function is of type Weight (I can't change it as this is the part of the framework):
type Weight = String -> Int
How do I implement such function given that it HAS TO be of the type Weight. The problem is that I don't know how to make weight aware of what value to return given a certain string. I can't hardcode the values because they will be different in different textfiles. And I can't have readFile inside the function or anything like that, right? Are there any other options?
You have two choices,
assignWeight :: String -> IO Int
assignWeight = do
file <- readFile whatever
-- parse file and extract a function assigning strings to their weights
return $ parseFile file
Or,
assignWeight :: String -> Weight
assignWeight file = parseFileAndExtractWeights file
and then read the file in main and use currying. So there's not toplevel function, but we still get our function later by partially applying assignWeight to the contents of the file
main = do
weights <- assignWeight `fmap` readFile whatever
-- use weights
But you can't perform IO in a computation of type Weight, so you'll either have to perform it elsewhere and pass the type to it or just change the type. No other way about it.
You cannot have assignWeight globally, but you can have it locally with the genuine non-IO type you wanted. I think the approach below is a common pattern used to separate monadic code from non-monadic.
import Data.Maybe
import Control.Applicative
parseFile :: IO [(String, Int)]
parseFile = read <$> readFile "Parse.txt"
main = do
content <- parseFile
let assignWeight x = fromJust $ lookup x content
print $ process assignWeight
type Weight = String -> Int
process :: Weight -> Int
process x = 0
Here assignWeight is of correct type. You can pass it around: look how I passed it to non-monadic process function. You cannot have assignWeight defined at top level without violating purity, as other commenters pointed out, but to have it locally and pass around is a commonly used approach.
Here is a more modular approach:
getAssignWeight :: IO Weight
getAssignWeight = do
content <- parseFile
let assignWeight x = fromJust $ lookup x content
return assignWeight
main = do
assignWeight <- getAssignWeight
print $ process assignWeight

How withFile is implemented in haskell

Following a haskell tutorial, the author provides the following implementation of the withFile method:
withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
withFile' path mode f = do
handle <- openFile path mode
result <- f handle
hClose handle
return result
But why do we need to wrap the result in a return? Doesn't the supplied function f already return an IO as can be seen by it's type Handle -> IO a?
You're right: f already returns an IO, so if the function were written like this:
withFile' path mode f = do
handle <- openFile path mode
f handle
there would be no need for a return. The problem is hClose handle comes in between, so we have to store the result first:
result <- f handle
and doing <- gets rid of the IO. So return puts it back.
This is one of the tricky little things that confused me when I first tried Haskell. You're misunderstanding the meaning of the <- construct in do-notation. result <- f handle doesn't mean "assign the value of f handle to result"; it means "bind result to a value 'extracted' from the monadic value of f handle" (where the 'extraction' happens in some way that's defined by the particular Monad instance that you're using, in this case the IO monad).
I.e., for some Monad typeclass m, the <- statement takes an expression of type m a in the right hand side and a variable of type a on the left hand side, and binds the variable to a value. Thus in your particular example, with result <- f handle, we have the types f result :: IO a, result :: a and return result :: IO a.
PS do-notation has also a special form of let (without the in keyword in this case!) that works as a pure counterpart to <-. So you could rewrite your example as:
withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
withFile' path mode f = do
handle <- openFile path mode
let result = f handle
hClose handle
result
In this case, because the let is a straightforward assignment, the type of result is IO a.

Resources