I'm a beginner with Haskell and am having trouble figuring out some code. What do I need to do to get the types right on this IO section of my code?
Thanks in advance.
loadPeople :: FilePath -> IO [Person]
loadPeople file = do
lines <- getLines file
map parsePerson lines
getLines :: FilePath -> IO [String]
getLines = liftM lines . readFile
parsePerson :: String -> Person
parsePerson line = ...........
map is underlined in red in Leksah, and the compile error I am receiving is:
src\Main.hs:13:3:
Couldn't match expected type `IO [Person]'
against inferred type `[Person]'
In the expression: map parsePerson lines
In the expression:
do { lines <- getLines file;
map parsePerson lines }
In the definition of `loadPeople':
loadPeople file
= do { lines <- getLines file;
map parsePerson lines }
map parsePerson lines has type [Person], but since you need the result type of loadPeople is IO [Person], you need to wrap it in IO using return:
return $ map parsePerson lines
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.
I have getLinesIn = liftM lines . getContents than
readAndWriteIn = do
list <- getLinesIn
and it doesnt work.
It says: Couldn't match expected type a0 -> m0 String with actual type IO String.
I dont understand why is that?
When I use getLinesFile = liftM lines . readFile
It works fine.
I need to do the same with getContents. Is there a way?
Thanks for any ideas.
EDIT:
Full output:
Couldn't match expected type `a0 -> m0 String'
with actual type `IO String'
In the second argument of `(.)', namely `getContents'
In the expression: liftM lines . getContents
In an equation for `getLinesIn':
getLinesIn = liftM lines . getContents
readFile is a function FilePath -> IO String while getContents is just IO String so you cannot use the (.) operator to compose it with liftM lines. You should just use
getLinesIn = liftM lines getContents
or
getLinesIn = fmap lines getContents
Given this little project I'm using to learn Haskell, I would like to move my request handler's code generation to a Hamlet template, but am unsure how to pass things around.
My current code generates the following error when lines are uncommented, which is the first blocker:
Couldn't match expected type `String -> String'
with actual type `String'
In the return type of a call of `renderHtml'
Probable cause: `renderHtml' is applied to too many arguments
In the expression: renderHtml ($ (shamletFile "fileList.hamlet"))
In an equation for `myTemplate':
myTemplate = renderHtml ($ (shamletFile "fileList.hamlet"))
Code:
site :: Snap ()
site =
ifTop (writeBS "hello world") <|>
route [ ("foo", writeBS "ba"),
("view_root_json_files", listRootFilesHandler)
] <|>
dir "static" (serveDirectory ".")
--myTemplate :: String -> String
--myTemplate = renderHtml ( $(shamletFile "fileList.hamlet") )
toText :: [FilePath] -> Text
toText = foldMap (flip snoc '\n' . pack)
listRootFilesHandler :: Snap ()
listRootFilesHandler = do
filenames <- liftIO $ getDirectoryContents "data"
let filtered_filenames = filter (not . isPrefixOf ".") filenames
writeText $ toText filtered_filenames
Ghc is telling you the correct type signature to put there. Just replace String -> String with String.
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"
I need some help with types in Haskell...
Here's the code I'm working on:
loadManyPeople :: [FilePath] → IO [Person]
loadManyPeople fs = do
return $ concat $ map loadPeople fs
loadPeople :: FilePath → IO [Person]
loadPeople file = do
lines ← getLines file
return $ map parsePerson lines
loadPeople is fine. I want loadManyPeople to load all the Persons from each file, then concat them into one list of Persons.
I'm new to Haskell and need help with getting the types to work out.
Thanks for the help.
Alex
loadPeople gives you an IO [Person], so map loadPeople gives you a [IO [Person]], however to use concat, you'd need a [[Person]].
To fix this, you can use sequence, which is a function of type [IO a] -> IO [a], so you can do the following:
loadManyPeople fs = do
manyPeople <- sequence $ map loadPeople fs
return $ concat manyPeople
However there's a shortcut for using map and then sequence: mapM which has type (a -> IO b) -> [a] -> IO [b]. With mapM your code looks like this:
loadManyPeople fs = do
manyPeople <- mapM loadPeople fs
return $ concat manyPeople
This can be written more succinctly using Applicative:
import Control.Applicative
loadManyPeople fs = concat <$> mapM loadPeople fs