Haskell Let/In in main function - haskell

My code:
import System.IO
main :: IO()
main = do
inFile <- openFile "file.txt" ReadMode
content <- hGetContents inFile
let
someValue = someFunction(content)
in
print(anotherFunction(someValue))
print(anotherFunction2(someValue))
hClose inFile
My error:
- Type error in application
*** Expression : print (anotherFunction2(someValue))
*** Term : print
*** Type : e -> IO ()
*** Does not match : a -> b -> c -> d
I need to print two or more lines with functions that require "someValue".
How I can fix it?

The cause of that error message is that when you write
let
someValue = someFunction(content)
in
print(anotherFunction(someValue))
print(anotherFunction2(someValue))
the two print statements are actually parsed as one:
print (anotherFunction (someValue)) print (anotherFunction2 (someValue))
In other words, it thinks the second print as well as (anotherFunction2 (someValue)) are also arguments to the first print. This is why it complains that e -> IO () (the actual type of print) does not match a -> b -> c -> d (a function taking three arguments).
You can fix this by adding a do after the in to make it parse the two statements as separate:
let
someValue = someFunction(content)
in do
print(anotherFunction(someValue))
print(anotherFunction2(someValue))
Though, it's better to use the do-notation form of let here, without any in:
import System.IO
main :: IO()
main = do
inFile <- openFile "file.txt" ReadMode
content <- hGetContents inFile
let someValue = someFunction content
print (anotherFunction someValue)
print (anotherFunction2 someValue)
hClose inFile
I also got rid of some redundant parentheses in the above code. Remember, they are only used for grouping, not for function application in Haskell.

When you use let binding in a do block, don't use the in keyword.
main :: IO()
main = do
inFile <- openFile "file.txt" ReadMode
content <- hGetContents inFile
let someValue = someFunction(content)
print(anotherFunction(someValue))
print(anotherFunction2(someValue))
hClose inFile

Related

Couldn´t match expected type error in haskell

I´m trying to implement a function that saves the highest score of a player in a game through a txt file. This is the code:
import System.IO
import System.Directory
saveHighScore:: Int -> IO()
saveHighScore num = do
let stringNum = "" ++ show num
let file = "score.txt"
writeFile file stringNum
readHighScore:: IO() -> Int
readHighScore = do
content <- openFile "score.txt" ReadMode
score <- hGetContents content
let highScore = read score :: Int
return highScore
But the code is giving this error and I don´t know what to do:
localStorage.hs:17:9: error:
* Couldn't match expected type: IO () -> Int
with actual type: IO Int
* In a stmt of a 'do' block:
content <- openFile "score.txt" ReadMode
In the expression:
do content <- openFile "score.txt" ReadMode
score <- hGetContents content
let highScore = ...
return highScore
In an equation for `readHighScore':
readHighScore
= do content <- openFile "score.txt" ReadMode
score <- hGetContents content
let highScore = ...
....
|
17| content <- openFile "score.txt" ReadMode
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The error message is telling you exactly what's wrong. You claim your function has type IO () -> Int, but it's impossible for any interesting function to have this type. The only possible implementation is a constant function that ignores the IO () and produces the same result regardless of its input.
The right type is the one the compiler is suggesting, IO Int.
In the other answer, amalloy gives the explanation.
To fix the issue, the easiest step is to remove the type declaration:
readHighScore = do
content <- openFile "score.txt" ReadMode
score <- hGetContents content
let highScore = read score :: Int
return highScore
Then load the file in GHCi and query GHCi about the type:
> :t readHighScore
readHighScore :: IO Int
That's the type. You can add it back as a type declaration:
readHighScore :: IO Int
readHighScore = do
content <- openFile "score.txt" ReadMode
score <- hGetContents content
let highScore = read score :: Int
return highScore
There are lots of tools and add-ons that can do this in your IDE of choice, but GHCi works everywhere, I should think.

Read a single int from a file and print it

How do I create a program that reads a line from a file, parse it to an int and print it(ignoring exceptions of course). Is there anything like "read" but for IO String?
I've got this so far but I couldn't get around the IO types:
readFromFile = do
inputFile <- openFile "catalogue.txt" ReadMode
isbn <- read( hGetLine inputFile)
hClose inputFile
You can specify the type explicitly, change the read line to
isbn <- fmap read (hGetLine inputFile) :: IO Int
As hGetLine inputFile is of type IO String, you should use fmap to get "inside" to read as an Int.
You can use the readFile function to convert your file to a string.
main = do
contents <- readFile "theFile"
let value = read $ head $ lines contents::Int
print value
You should add better error detection, or this program will fail if there isn't a first line, or if the value is malformed, but this is the basic flow....
First, observe that reading stuff and then immediately printing it can result in mysterious errors:
GHCi, version 8.0.0.20160421: http://www.haskell.org/ghc/ :? for help
Prelude λ read "123"
*** Exception: Prelude.read: no parse
The reason is that you don't specify what type you want to read. You can counter this by using type annotations:
Prelude λ read "123" :: Integer
123
but it is sometimes easier to introduce a little helper function:
Prelude λ let readInteger = read :: String -> Integer
Prelude λ readInteger "123"
123
Now to the main problem. read( hGetLine inputFile) doesn't work because hGetLine inputFile returns and IO String and read needs a String. This can be solved in two steps:
line <- hGetLine inputFile
let isbn = readInteger line
Note two different constructs <- and let .. =, they do different things. Can you figure out exactly what?
As shown in another answer, you can do it in a less verbose manner:
isbn <- fmap readInteger (hGetLine inputFile)
which is great if you do a simple thing like read. But it is often desirable to explicitly name intermediate results. You can use <- and let .. = constructs in such cases.

how to parse a uniprot-file with parsec?

I am a newbie to Haskell, but it seems like a powerful language that I want to learn. I was adopting some code from the chapter in real world Haskell on parsec. I tried to make my own version of it parsing the content of a uniprot-file. This is a file that consists of records (that starts with ">"), and where each record consists of lines. My code seems very close to what is done in the example, but I am getting a lot of errors - mostly on types. My exception is among other that I am taking the output of readFile (IO string) instead of a string. I would appreciate it if someone could help me understand what is wrong in my approach...
import Text.ParserCombinators.Parsec
main:: IO()
parseSprot :: IO String -> Either ParseError [[String]]
parseSprot input = parse uniprotFile "(unknown)" input
where
uniprotFile = endBy record eol
record = sepBy lines (char '>')
lines = many (noneOf ",\n")
eol = char '\n'
main = do
parseSprot $ readFile "uniprot_sprot.fasta"
putStrLn "hey"
parseSprot doesn't need an IO in its signature.
parseSprot :: String -> Either ParseError [[String]]
...
The result of readFile is an IO String. You can do something with this String by binding the result of the readFile action into a new IO action. In do notation you can bind the result to a variable with <-
main = do
fileContents <- readFile "uniprot_sprot.fasta"
The parseSprot function doesn't return a result in IO, you can use it anywhere. In do notation we tell the difference between a result bound to a variable and a declaration by using different syntax. x <- ... binds a result to a variable. let x = ... declares x to be whatever is on the right hand side.
main = do
fileContents <- readFile "uniprot_sprot.fasta"
let parsedContents = parseSprot fileContents
To test what your parser is doing, you might want to print the value returned from parse.
main = do
fileContents <- readFile "uniprot_sprot.fasta"
let parsedContents = parseSprot fileContents
print parsedContents
Without do notation you can write this as
main = readFile "uniprot_sprot.fasta" >>= print . parseSprot
>>= takes the result of the first computation and feeds it into a function to decide what to do next.

how to parse yahoo historical csv with Attoparsec

i am a beginner of haskell, how to parse with attoparsec into open array, high array etc
module CsvParser (
Quote (..)
, csvFile
, quote
) where
import System.IO
import Data.Attoparsec.Text
import Data.Attoparsec.Combinator
import Data.Text (Text, unpack)
import Data.Time
import System.Locale
import Data.Maybe
data Quote = Quote {
qTime :: LocalTime,
qAsk :: Double,
qBid :: Double,
qAskVolume :: Double,
qBidVolume :: Double
} deriving (Show, Eq)
csvFile :: Parser [Quote]
csvFile = do
q <- many1 quote
endOfInput
return q
quote :: Parser Quote
quote = do
time <- qtime
qcomma
ask <- double
qcomma
bid <- double
qcomma
askVolume <- double
qcomma
bidVolume <- double
endOfLine
return $ Quote time ask bid askVolume bidVolume
qcomma :: Parser ()
qcomma = do
char ','
return ()
qtime :: Parser LocalTime
qtime = do
tstring <- takeTill (\x -> x == ',')
let time = parseTime defaultTimeLocale "%d.%m.%Y %H:%M:%S%Q" (unpack tstring)
return $ fromMaybe (LocalTime (fromGregorian 0001 01 01) (TimeOfDay 00 00 00 )) time
--testString :: Text
--testString = "01.10.2012 00:00:00.741,1.28082,1.28077,1500000.00,1500000.00\n"
quoteParser = parseOnly quote
main = do
handle <- openFile "C:\\Users\\ivan\\Downloads\\0005.HK.csv" ReadMode
contents <- hGetContents handle
let allLines = lines contents
map (\line -> quoteParser line) allLines
--putStr contents
hClose handle
Error message:
testhaskell.hs:89:5:
Couldn't match type `[]' with `IO'
Expected type: IO (Either String Quote)
Actual type: [Either String Quote]
In the return type of a call of `map'
In a stmt of a 'do' block:
map (\ line -> quoteParser line) allLines
In the expression:
do { handle <- openFile
"C:\\Users\\ivan\\Downloads\\0005.HK.csv" ReadMode;
contents <- hGetContents handle;
let allLines = lines contents;
map (\ line -> quoteParser line) allLines;
.... }
testhaskell.hs:89:37:
Couldn't match type `[Char]' with `Text'
Expected type: [Text]
Actual type: [String]
In the second argument of `map', namely `allLines'
In a stmt of a 'do' block:
map (\ line -> quoteParser line) allLines
In the expression:
do { handle <- openFile
"C:\\Users\\ivan\\Downloads\\0005.HK.csv" ReadMode;
contents <- hGetContents handle;
let allLines = lines contents;
map (\ line -> quoteParser line) allLines;
.... }
The error has nothing to do with parsec or attoparsec. The line the error message points to is not an IO action, so it causes the error when you try to use it as one:
main = do
handle <- openFile "C:\\Users\\ivan\\Downloads\\0005.HK.csv" ReadMode
contents <- hGetContents handle
let allLines = lines contents
map (\line -> quoteParser line) allLines -- <== This is not an IO action
--putStr contents
hClose handl
You ignore the result of the map call. You should store it in a variable with let, like you do with the result of lines.
The second error is because you are trying to use Text as String which are different types, even though they both represent ordered collections of characters (they also have different internal representations). You can convert between the two types with pack and unpack: http://hackage.haskell.org/package/text/docs/Data-Text.html#g:5
Also, you should always explicitly give main the type signature main :: IO (). It can sometimes lead to subtle problems if you don't.
As other people have said, though, you should probably use a csv parser package.
You can use attoparsec-csv package or you can take a look at its source code to have some idea on how to write it by yourself.
The code will be like
import qualified Data.Text.IO as T
import Text.ParseCSV
main = do
txt <- T.readFile "file.csv"
case parseCSV txt of
Left err -> error err
Right csv -> mapM_ (print . mkQuote) csv
mkQuote :: [T.Text] -> Quote
mkQuote = error "Not implemented yet"

cat emulator in Haskell

So I'm attempting to make a program that reads from a handle and writes to stdOut, like so:
import IO
import System
cat w = do
fromHandle <- getAndOpenFile w ReadMode
contents <- hGetContents fromHandle
putStr contents
putStr "Done."
getAndOpenFile :: String -> IOMode -> IO Handle
getAndOpenFile name mode =
do
catch (openFile name mode)
(\_ -> do
putStrLn ("Cannot open "++ name ++"\n")
return())
I'm fairly new to Hs and it seems like this should be far more simple than I'm making it for myself. Any suggestions to helping me move further?
the usage would be ./cat "foo.txt" and would print the text in the foo.txt file to stdOut.
There is the below function which does what you want.
readFile :: FilePath -> IO String
use this with putStr to print the IO String

Resources