I get no runtime errors when I run the following code:
printReverse :: IO ()
printReverse = do
line <- getLine
when (not $ null line) $
do putStrLn $ reverse line
printReverse
return ()
But when I run the same code except that I replaced getLine with readLn :: IO String, I get a parse error.
Code:
printReverse :: IO ()
printReverse = do
line <- readLn :: IO String
when (not $ null line) $
do putStrLn $ reverse line
printReverse
return ()
Error:
*** Exception: user error (Prelude.readIO: no parse)
What's the difference here between getLine and readLn?
Look at the types.
readLn :: Read a => IO a
and
getLine :: IO String
readLn parses the input according to the 'Read' format of the result type. This is the same format as show.
So you're attempting to read a Haskell String value, in show format from input, which is confusing, unless the string is already in double-qyoted haskell format.
Related
I have this program which just prints out the command line arguments.
echoArgs :: IO ()
echoArgs = do
line <- getArgs
print line
What I wanted to know is that why does this fail when I type:
echoArgs :: IO ()
echoArgs = do
line <- getArgs
putStrLn line
and also why doesn't it work when I change it to:
echoArgs :: IO String
echoArgs = do
line <- getArgs
let line' = read line :: String
putStrLn line'
Because
getArgs :: IO [String]
so line in do { line <- getArgs ; ... } is
line :: [String]
but putStrLn :: String -> IO () expects a String argument, not a list of Strings.
Similarly, read :: Read a => String -> a also expect a String argument, not a list of Strings argument.
See also: The Guide to Types in do-notation, In Vivid Colors.
print produces a String from whatever argument you give it.
putStrLn, on the other hand, expects a String as an argument. (Indeed, print = putStrLn . show.) Similarly, read expects a String as an argument; in effect, it deserializes when what you are trying to do is serialize the list.
getArgs has type IO [String], which means that line is not a String, but both String and Show a => [a] have a Show instance which print can use to make a String out of 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.
As we know main function has type IO ().
However, it is problem for me, because my program may return error. It means that I am executing from main function something like that:
ErrorT String IO ()
Of course, at this moment I have problem with type errors.
What should I do ?
args <- getArgs
s <- readFile $ head args
myFoo s
Where myFoo :: String -> ErrorT String IO ()
You need to run it with runErrorT:
runErrorT :: ErrorT e m a -> m (Either e a)
Since myFoo returns a ErrorT String IO () this will evaluate to an IO (Either String ()) which you execute in main and match on the result:
args <- getArgs
s <- readFile $ head args
result <- runErrorT (myFoo s)
case result of
Right _ -> putStrLn "ok!"
Left(err) -> putStrLn $ "Error: " ++ err
To expand on #Lee's answer, you can then use exitFailure and exitSuccess from System.Exit to return an appropriate error code to the calling process:
module Main (main) where
import Control.Monad.Error
import System.Environment
import System.Exit
myFoo :: String -> ErrorT String IO ()
myFoo = undefined
main :: IO ()
main = do
args <- getArgs
s <- readFile $ head args
result <- runErrorT (myFoo s)
case result of
Right _ -> do
putStrLn "OK"
exitSuccess
Left (e) -> do
putStrLn $ "Error: " ++ e
exitFailure
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"
Here is my new main with the error: parse error on input '->'
I commented where the error is. Could it be an indentation error somewhere?
main :: IO()
main = do
expression <- evaluate_input
putStrLn $ show $ compute expression
evaluate_input :: IO ()
evaluate_input = do
args <- getArgs
case args of
a:s -> return a
-> do putStrLn "Enter Expression or 'end' to exit calculator"
hFlush stdout
getLine
unless (expression -> "end") $ showExpr expression --error here
where
showExpr expression = do putStrLn $ evaluateExpr expression
evaluate_input
evaluateExpr :: String -> String
evaluateExpr = show
Few problems with your code
until is not used correctly. I find it better to recurse when I have to repeat same action again and again. You can write the monadic version of until and use that.
It is better to use getArgs inside main once. You don't need to repeat it every time.
A corrected version is here. I haven't implemented all the functions so you still need to do the hard work of parsing and evaluating expressions.
import Control.Monad (unless)
main :: IO ()
main = evaluate
evaluate :: IO ()
evaluate = do
putStrLn "Enter Expression"
expr <- getLine
unless (expr == "end") $ showExpr expr
where
showExpr expr = do putStrLn $ evaluateExpr expr
evaluate
evaluateExpr :: String -> String
evaluateExpr = show