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
Related
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
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 <-.
Consider the code below taken from a working example I've built to help me learn Haskell. This code parses a CSV file containing stock quotes downloaded from Yahoo into a nice simple list of bars with which I can then work.
My question: how can I write a function that will take a file name as its parameter and return an OHLCBarList so that the first four lines inside main can be properly encapsulated?
In other words, how can I implement (without getting all sorts of errors about IO stuff) the function whose type would be
getBarsFromFile :: Filename -> OHLCBarList
so that the grunt work that was being done in the first four lines of main can be properly encapsulated?
I've tried to do this myself but with my limited Haskell knowledge, I'm failing miserably.
import qualified Data.ByteString as BS
type Filename = String
getContentsOfFile :: Filename -> IO BS.ByteString
barParser :: Parser Bar
barParser = do
time <- timeParser
char ','
open <- double
char ','
high <- double
char ','
low <- double
char ','
close <- double
char ','
volume <- decimal
char ','
return $ Bar Bar1Day time open high low close volume
type OHLCBar = (UTCTime, Double, Double, Double, Double)
type OHLCBarList = [OHLCBar]
barsToBarList :: [Either String Bar] -> OHLCBarList
main :: IO ()
main = do
contents :: C.ByteString <- getContentsOfFile "PriceData/Daily/yhoo1.csv" --PriceData/Daily/Yhoo.csv"
let lineList :: [C.ByteString] = C.lines contents -- Break the contents into a list of lines
let bars :: [Either String Bar] = map (parseOnly barParser) lineList -- Using the attoparsec
let ohlcBarList :: OHLCBarList = barsToBarList bars -- Now I have a nice simple list of tuples with which to work
--- Now I can do simple operations like
print $ ohlcBarList !! 0
If you really want your function to have type Filename -> OHLCBarList, it can't be done.* Reading the contents of a file is an IO operation, and Haskell's IO monad is specifically designed so that values in the IO monad can never leave. If this restriction were broken, it would (in general) mess with a lot of things. Instead of doing this, you have two options: make the type of getBarsFromFile be Filename -> IO OHLCBarList — thus essentially copying the first four lines of main — or write a function with type C.ByteString -> OHLCBarList that the output of getContentsOfFile can be piped through to encapsulate lines 2 through 4 of main.
* Technically, it can be done, but you really, really, really shouldn't even try, especially if you're new to Haskell.
Others have explained that the correct type of your function has to be Filename -> IO OHLCBarList, I'd like to try and give you some insight as to why the compiler imposes this draconian measure on you.
Imperative programming is all about managing state: "do certain operations to certain bits of memory in sequence". When they grow large, procedural programs become brittle; we need a way of limiting the scope of state changes. OO programs encapsulate state in classes but the paradigm is not fundamentally different: you can call the same method twice and get different results. The output of the method depends on the (hidden) state of the object.
Functional programming goes all the way and bans mutable state entirely. A Haskell function, when called with certain inputs, will always produce the same output. Simple examples of
pure functions are mathematical operators like + and *, or most of the list-processing functions like map. Pure functions are all about the inputs and outputs, not managing internal state.
This allows the compiler to be very smart in optimising your program (for example, it can safely collapse duplicated code for you), and helps the programmer not to make mistakes: you can't put the system in an invalid state if there is none! We like pure functions.
The exception to the rule is IO. Code that performs IO is impure by definition: you could call getLine a hundred times and never get the same result, because it depends on what the user typed. Haskell handles this using the type system: all impure functions are marred with the IO type. IO can be thought of as a dependency on the state of the real world, sort of like World -> (NewWorld, a)
To summarise: pure functions are good because they are easy to reason about; this is why Haskell makes functions pure by default. Any impure code has to be labelled as such with an IO type signature; this tells the compiler and the reader to be careful with this function. So your function which reads from a file (a fundamentally impure action) but returns a pure value can't exist.
Addendum in response to your comment
You can still write pure functions to operate on data that was obtained impurely. Consider the following straw-man:
main :: IO ()
main = do
putStrLn "Enter the numbers you want me to process, separated by spaces"
line <- getLine
let numberStrings = words line
let numbers = map read numberStrings
putStrLn $ "The result of the calculation is " ++ (show $ foldr1 (*) numbers + 10)
Lots of code inside IO here. Let's extract some functions:
main :: IO ()
main = do
putStrLn "Enter the numbers you want me to process, separated by spaces"
result <- fmap processLine getLine -- fmap :: (a -> b) -> IO a -> IO b
-- runs an impure result through a pure function
-- without leaving IO
putStrLn $ "The result of the calculation is " ++ result
processLine :: String -> String -- look ma, no IO!
processLine = show . calculate . readNumbers
readNumbers :: String -> [Int]
readNumbers = map read . words
calculate :: [Int] -> Int
calculate numbers = product numbers + 10
product :: [Int] -> Int
product = foldr1 (*)
I've pulled logic out of main into pure functions which are easier to read, easier for the compiler to optimise, and more reusable (and so more testable). The program as a whole still lives inside IO because the data is obtained impurely (see the last part of this answer for a more thorough treatment of this argument). Impure data can be piped through pure functions using fmap and other combinators; you should try to put as little logic in main as possible.
Your code does seem to be most of the way there; as others have suggested you could extract lines 2-4 of your main into another function.
In other words, how can I implement (without getting all sorts of errors about IO stuff) the function whose type would be
getBarsFromFile :: Filename -> OHLCBarList
so that the grunt work that was being done in the first four lines of main can be properly encapsulated?
You cannot do this without getting all sorts of errors about IO stuff because this type for getBarsFromFile misses an IO. Probably that's what the errors about IO stuff are trying to tell you. Did you try understanding and fixing the errors?
In your situation, I would start by abstracting over the second to fourth line of your main in a function:
parseBars :: ByteString -> OHLCBarList
And then I would combine this function with getContentsOfFile to get:
getBarsFromFile :: FilePath -> IO OHLCBarList
This I would call in main.
I'm pretty new to Haskell, and am trying to simply read a file into a list of strings. I'd like one line of the file per element of the list. But I'm running into a type issue that I don't understand. Here's what I've written for my function:
readAllTheLines hdl = (hGetLine hdl):(readAllTheLines hdl)
That compiles fine. I had thought that the file handle needed to be the same one returned from openFile. I attempted to simply show the list from the above function by doing the following:
displayFile path = show (readAllTheLines (openFile path ReadMode))
But when I try to compile it, I get the following error:
filefun.hs:5:43:
Couldn't match expected type 'Handle' with actual type 'IO Handle'
In the return type of a call of 'openFile'
In the first argument of 'readAllTheLines', namely
'(openFile path ReadMode)'
In the first argument of 'show', namely
'(readAllTheLines (openFile path ReadMode))'
So it seems like openFile returns an IO Handle, but hGetLine needs a plain old Handle. Am I misunderstanding the use of these 2 functions? Are they not intended to be used together? Or is there just a piece I'm missing?
Use readFile and lines for a better alternative.
readLines :: FilePath -> IO [String]
readLines = fmap lines . readFile
Coming back to your solution openFile returns IO Handle so you have to run the action to get the Handle. You also have to check if the Handle is at eof before reading something from that. It is much simpler to just use the above solution.
import System.IO
readAllTheLines :: Handle -> IO [String]
readAllTheLines hndl = do
eof <- hIsEOF hndl
notEnded eof
where notEnded False = do
line <- hGetLine hndl
rest <- readAllTheLines hndl
return (line:rest)
notEnded True = return []
displayFile :: FilePath -> IO [String]
displayFile path = do
hndl <- openFile path ReadMode
readAllTheLines hndl
To add on to Satvik's answer, the example below shows how you can utilize a function to populate an instance of Haskell's STArray typeclass in case you need to perform computations on a truly random access data type.
Code Example
Let's say we have the following problem. We have lines in a text file "test.txt", and we need to load it into an array and then display the line found in the center of that file. This kind of computation is exactly the sort situation where one would want to use a random access array over a sequentially structured list. Granted, in this example, there may not be a huge difference between using a list and an array, but, generally speaking, list accesses will cost O(n) in time whereas array accesses will give you constant time performance.
First, let's create our sample text file:
test.txt
This
is
definitely
a
test.
Given the file above, we can use the following Haskell program (located in the same directory as test.txt) to print out the middle line of text, i.e. the word "definitely."
Main.hs
{-# LANGUAGE BlockArguments #-} -- See footnote 1
import Control.Monad.ST (runST, ST)
import Data.Array.MArray (newArray, readArray, writeArray)
import Data.Array.ST (STArray)
import Data.Foldable (for_)
import Data.Ix (Ix) -- See footnote 2
populateArray :: (Integral i, Ix i) => STArray s i e -> [e] -> ST s () -- See footnote 3
populateArray stArray es = for_ (zip [0..] es) (uncurry (writeArray stArray))
middleWord' :: (Integral i, Ix i) => i -> STArray s i String -> ST s String
middleWord' arrayLength = flip readArray (arrayLength `div` 2)
middleWord :: [String] -> String
middleWord ws = runST do
let len = length ws
array <- newArray (0, len - 1) "" :: ST s (STArray s Int String)
populateArray array ws
middleWord' len array
main :: IO ()
main = do
ws <- words <$> readFile "test.txt"
putStrLn $ middleWord ws
Explanation
Starting with the top of Main.hs, the ST s monad and its associated function runST allow us to extract pure values from imperative-style computations with in-place updates in a referentially transparent manner. The module Data.Array.MArray exports the MArray typeclass as an interface for instantiating mutable array data types and provides helper functions for creating, reading, and writing MArrays. These functions can be used in conjunction with STArrays since there is an instance of MArray defined for STArray.
The populateArray function is the crux of our example. It uses for_ to "applicatively" loop over a list of tuples of indices and list elements to fill the given STArray with those list elements, producing a value of type () in the ST s monad.
The middleWord' helper function uses readArray to produce a String (wrapped in the ST s monad) that corresponds to the middle element of a given STArray of Strings.
The middleWord function instantiates a new STArray, uses populateArray to fill the array with values from a provided list of strings, and calls middleWord' to obtain the middle string in the array. runST is applied to this whole ST s monadic computation to extract the pure String result.
We finally use our middleWord function in main to find the middle word in the text file "test.txt".
Further Reading
Haskell's STArray is not the only way to work with arrays in Haskell. There are in fact Arrays, IOArrays, DiffArrays and even "unboxed" versions of all of these array types that avoid using the indirection of pointers to simply store "raw" values. There is a page on the Haskell Wikibook on this topic that may be worth some study. Before that, however, looking at the Wikibook page on mutable objects may give you some insight as to why the ST s monad allows us to safely compute pure values from functions that use imperative/destructive operations.
Footnotes
1 The BlockArguments language extension is what allows us to pass a do block directly to a function without any parentheses or use of the function application operator $.
2 As suggested by the Hackage documentation, Ix is a typeclass mainly meant to be used to specify types for indexing arrays.
3 The use of the Integral and Ix type constraints may be a bit of overkill, but it's used to make our type signatures as general as possible.
I'm trying to do some programming in Haskell. I'm trying to read a file and then put every line in the file in a list by using the line function. Here's the partial code:
file = "muh.rtr"
readTrack :: String -> Track
readTrack file =
do let defFile = readFile file
let fileLines = lines defFile
However, I keep getting this error:
Parser.hs:22:39:
Couldn't match expected type `String' with actual type `IO String'
In the first argument of `lines', namely `defFile'
In the expression: lines defFile
In an equation for `fileLines': fileLines = lines defFile
I have been searching the Internet for hours now hoping to find some answers somewhere but I've not been so lucky so far.
You probably wanted either something like this:
readTrack :: String -> IO Track
readTrack file = do defFile <- readFile file
let fileLines = lines defFile
-- etc....
...or something like this:
readTrack :: String -> IO Track
readTrack file = do fileLines <- liftM lines (readFile file)
-- etc....
But what you really should do is stop, go find an introduction to the language such as Learn You a Haskell, and spend some time reading it.
Feeding code consisting entirely of very simple errors into GHC and then posting the error message on Stack Overflow is not a good way to learn.
The type of readFile is
readFile :: FilePath -> IO String
so you need to use <- to bind the result, and your function has to return IO Track.
readTrack :: String -> IO Track
readTrack file =
do defFile <- readFile file
let fileLines = lines defFile
...
I suggest reading a good tutorial on IO in Haskell, for example the Input and Output chapter of Learn You a Haskell for Great Good!.
readFile return an IO string. That is, it is an IO computation that returns a string. This means that you need to use <- instead of let to "get" the string its returning.
readTrack file =
do
defFile <- readFile file
...
You can use let to bind things that are not IO computations, such as the return value of lines, that is a regular string.
readTrack file =
do
defFile <- readFile file
let fileLines = lines defFile
...
Finally, you need to return the value you might want to try something like
readTrack file =
do
defFile <- readFile file
let fileLines = lines defFile
fileLines --Doesn't actually work!
but unfortunately, since we are inside a "do" block and are trying to return a monadic computation, we need to send the fileLines back into the io monad (remember, out function returns IO [String], not String!
readTrack file =
do
defFile <- readFile file
let fileLines = lines defFile
return fileLines
Note that the "return" here is not a return statement as would normaly be found in most languages and it should not be used in your pure functions.
All this might seem like a lot at first. I would suggest you stick to pure functions (without input and output / monads) until until you get a better hang on the language.
You can't do it like that -- you've run into the IO monad. What you need to do is something like:
readTrack :: String -> IO Track
readTrack file = do
defFile <- readFile file
let fileLines = lines deffile
...
return whatever
Think of IO T values as statements (as opposed to expressions) with return type T. Because statements have side effects, but expressions don't, you can never turn a statement into an expression; the type system enforces this, which is why your type signature won't work.
Note the different assignment-like syntax in the do block: in this example, the foo <- bar is used for IO operations, while the let baz = quux syntax is used for purely functional evaluation. This is more fallout from using monadic I/O -- it makes more sense in the full generality of Haskell's polymorphic type system, but it's not necessarily bad to have a syntactic indicator of pure vs. side-effecting operations, either.
In general, it is good practice to try keeping most of your implementation in the purely functional realm: implement your pure computation with regular functional methods, then describe your I/O operations in the IO monad. It is a common novice mistake to write loops in the IO monad which would be more appropriate as list comprehensions or recursive functions.
If your function is supposed to have type readTrack :: String -> Track, are you sure the String is a filename? Perhaps it's data - if so, don't use readFile. Write some sample data and test using that, eg
sampleData = "2 3\n1 30 234 45\n1 2 32 4\n5 3 4 23"
(The other question on SO about this homework didn't use file IO. I'll not link to it because you're in a crisis and might be tempted to copy, and in any case if you refuse to learn haskell at least I'll force you to improve your StackOverflow search skills! :) )
In any case I think you'll get more marks by solving the String problem than by solving the IO problem.
Delay the readFile issue until you've got the pure version working, otherwise you might end up writing most of your code in the IO monad which would be much more complex than necessary.
One you have a pure function readTrack :: String -> Track, you can do
readTrackFrom :: FilePath -> IO Track
readTrackFrom filename = fmap readTrack (readFile filename)
Now, fmap :: Functor f => (a -> b) -> f a -> f b, so takes pure functions and lifts them to work in a different computational context like IO.
Since IO is a Functor (look it up tomorrow, not tonight), we're using it as the type (String -> Track) -> IO String -> IO Track. That's good because readTrack :: String -> Track and (readFile filename) :: IO String.
If you want to, you can then >>= print or >>= writeFile newfilename as you see fit.
Don't forget to add deriving Show after use data Track =..., but you don't need to if you're using type Track = .....