Haskell IO : Printing Command Line Arguments - haskell

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.

Related

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 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.

Difference between getLine and readLn

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.

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"

Haskell String to List of Strings using Words

I'm rather new to Haskell, and I'm currently using LearnYouAHaskell.
I am trying to take a string separated by white space, and break it into a list of smaller word strings.
My current program:
main = do
putStrLn "Insert a string to convert: "
-- Input string
line <- getLine
words line;
But in this case, it tells me I'm having an IO error.
TO my understanding, getLine is an action, and so since this is impure, I have to bind it to "line". Line is an accurate representation of getLine, which is an IO String.
However, shouldn't line be a string?
When I try to use words on line, it tells me
"Couldn't match expected type "IO a0" with actual type [String]
As if line isn't a string.
Furthermore, can I use :t line in the program itself when I make it to see if it's actual of the right type or not?
I apologize for the novice question, but I'm a bit stuck.
EDIT:
I did something similar in GHCI, and it tells me that my type is in fact a normal string.. I don't get it.
Prelude> line <- getLine
"Hello fellows"
Prelude> :t line
line :: String
Prelude> words line
["Hello","fellows"]
Why doesn't that work?
In haskell if you want to return a value, you have to say so:
main = do
putStrLn "Insert a string to convert: "
-- Input string
line <- getLine
return (words line)
words line isn't an IO action, it's a list of strings, so it can't be a statement in a do block.
return :: Monad m => a -> m a and in this case we can specialise it to the type a -> IO a and then to [String] -> IO [String]. Each of the statements in your do block must be IO statements.
Taking it further:
If you want to compile your program, you should have main :: IO(), which means you shouldn't return your list.
If, for example, you wanted to process those strings into a single string then output that, you could do
process :: [String] -> String
process xss = "I don't know, some answer"
main = do
putStrLn "Insert a string to convert: "
-- Input string
line <- getLine
putStrLn (process (words line))
although I'd personally write that last line as putStrLn $ process.words $ line.
Your interaction in GHCi
Prelude> line <- getLine
"Hello fellows"
Prelude> :t line
line :: String
Prelude> words line
["Hello","fellows"]
is using the fact that GHCi isn't actually just running in the IO monad. In GHCi, if your input is a valid line in a do block, it'll get run, but if it's pure code it'll get evaluated and printed. (An interactive interpreter like this is often called a REPL for Read-Eval-Print-Loop.)
Well, the question is what do you want to do with words line?
Having words line as a line inside a do block is doing nothing, but to get it to work you have to use return to wrap it up in the IO monad:
main = do
putStrLn "Insert a string to convert: "
-- Input string
line <- getLine
return (words line);
Anyway, perhaps you want to print it instead?
main = do
putStrLn "Insert a string to convert: "
-- Input string
line <- getLine
print (words line);

Resources