Hey, sorry to dump the error message here but I've tried everything I can find and nothing seems relevant. This code is generating the error:
import System.Environment
import System.Directory
import System.IO
import Data.List
data Node = PathNode String Float Float [String] | NoNode deriving (Show)
main = do
(filename:args) <- getArgs
load filename
load :: String -> IO ()
load fileName = do
contents <- readFile fileName
let pathStrings = lines contents
first = head pathStrings
args = lines first
path = createNode args
putStr path
createNode [String] -> Node
createNode (name:x:y:paths) = PathNode name x y paths
createNode [] = NoNode
I know its something to do with alignment, but I have aligned all the calls in the 'load' function correctly. What am I doing wrong?
Thanks
-A
The last line in the do expression is indented too far.
Also, you can just write
load :: String -> IO ()
load fileName =
putStr . createNode . lines . head . lines =<< readFile filename
Only errors I can find besides identation are:
in load: putStr expects String, whereas path is of type Node.
in createNode: Pathnode needs x and y to be of type Float, whereas you give Strings.
load :: String -> IO ()
load fileName = do
contents <- readFile fileName
let pathStrings = lines contents
first = head pathStrings
args = lines first
path = createNode args
putStr $ show path
createNode :: [String] -> Node
createNode (name:x:y:paths) = PathNode name (read x) (read y) paths
createNode [] = NoNode
This solves both type errors described by using show and read.
Related
This function takes an filepath and returns the content of the file.
this file includes a cuple of lines of the same length.
-- It's used to be an primitive picture.
parsePicture :: FilePath -> IO()
parsePicture fileName = do
content <- lines <$> readFile fileName
print content
Now I've tried to implement a function to flip the "picture" vertically:
type Picture = [[Char]]
flipVertical :: IO() -> Picture
flipVertical xs = map reverse xs
But I only get the following error code:
zuP12.hs:24:31: error:
• Couldn't match expected type ‘[[Char]]’ with actual type ‘IO ()’
• In the second argument of ‘map’, namely ‘xs’
In the expression: map reverse xs
In an equation for ‘flipVertical’: flipVertical xs = map reverse xs
|
24 | flipVertical xs = map reverse xs
| ^^
Failed, no modules loaded.
How can I use my function flipVertical on the result of parsePicture?
This function... returns the content of the file.
No it doesn't. It prints the content of the file, to STDOUT. For most purposes you should consider information you put to STDOUT to be gone – the information has left your program and is now on the terminal screen, or whereever else the user chooses to put it, but not accessible to the program anymore.
(Strictly speaking, it is possible to redirect STDOUT back into your own program, but it's a big hack, don't do this.)
Instead, you should change the function so it actually does return the content:
parsePicture :: FilePath -> IO Picture
parsePicture fileName = do
content <- lines <$> readFile fileName
return content
...or simply
parsePicture fileName = lines <$> readFile fileName
which behaves exactly the same (by the monad laws).
Also, I would rather call this loadPicture: parsing shouldn't involve file reading.
Of course, you can still print the contents later on, with something like
main = do
...
fooPicture <- parsePicture "foofilename"
print fooPicture
...
As for flipVertical, this shouldn't have anything to do with IO at all. It's simply a pure function
flipVertical :: Picture -> Picture
flipVertical xs = map reverse xs
which can be used like, for example
main = do
...
fooPicture <- parsePicture "foofilename"
print $ flipVertical fooPicture
...
or
main = do
...
fooVFPicture <- flipVertical <$> parsePicture "foofilename"
...
I'm new to Haskell and IO is still a bit confusing. I have a txt file that I want to read, add the numbers in the text file, and then write it to a text file. the file looks like the following:
2
3
the numbers are separated by a new line character I know how to read a file contents then write it to another file but I don't know how I can manipulate it or if I have to cast the information to an Int?
module Main where
import System.Environment
-- | this fuction read first line in a file and write out to src file
-- src "src.txt", des "des.txt"
copyFirstLine :: FilePath -- ^ path to input file
-> FilePath -- ^ path to output file
-> IO ()
copyFirstLine src dst = do
contect <- readFile src
let (fst :rest) = (lines contect)
writeFile dst fst
main = do
[src,dst] <- getArgs
copyFirstLine src dst
Thanks in advance.
I can't sure your 'manipulate' means what, but I will assume you need integer calculation. It won't be difficult to manipulate as string.
If you hoogle the signature String -> Int you can find the read.
-- | this fuction read first line in a file and write out +1 result
-- to src file src "src.txt", des "des.txt"
eachPlusOne :: FilePath -- ^ path to input file
-> FilePath -- ^ path to output file
-> IO ()
eachPlusOne src dst = do
contect <- readFile src
let lns = lines contect :: [String]
ints = map ((1+) . read) lns :: [Int]
outs = unlines . map show $ ints :: String
writeFile dst outs
If you are using sufficiently recent version of ghc, you can use readMaybe which is desirable.
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 have a main like the following:
main :: IO ()
main = do
args <- getArgs
putStrLn $ functionName args
where
functionName args = "problem" ++ (filter (/= '"') $ show (args!!0))
Instead of putting the name to stdout like I do it right now, I want to call the function.
I am aware of the fact, that I could use hint (as mentioned in Haskell: how to evaluate a String like "1+2") but I think that would be pretty overkill for just getting that simple function name.
At the current stage it does not matter if the program crashes if the function does not exist!
Without taking special measures to preserve them, the names of functions will likely be gone completely in a compiled Haskell program.
I would suggest just making a big top-level map:
import Data.Map ( Map )
import qualified Data.Map as Map
functions :: Map String (IO ())
functions = Map.fromList [("problem1", problem1), ...]
call :: String -> IO ()
call name =
case Map.lookup name of
Nothing -> fail $ name + " not found"
Just m -> m
main :: IO ()
main = do
args <- getArgs
call $ functionName args
where
functionName args = "problem" ++ (filter (/= '"') $ show (args!!0))
If you're going to do this, you have a few approaches, but the easiest by far is to just pattern match on it
This method requires that all of your functions you want to call have the same type signature:
problem1 :: Int
problem1 = 1
problem2 :: Int
problem2 = 2
runFunc :: String -> Maybe Int
runFunc "problem1" = Just problem1
runFunc "problem2" = Just problem2
runFunc _ = Nothing
main = do
args <- getArgs
putStrLn $ runFunc $ functionName args
This requires you to add a line to runFunc each time you add a new problemN, but that's pretty manageable.
You can't get a string representation of an identifier, not without fancy non-standard features, because that information isn't retained after compilation. As such, you're going to have to write down those function names as string constants somewhere.
If the function definitions are all in one file anyway, what I would suggest is to use data types and lambdas to avoid having to duplicate those function names altogether:
Data Problem = {
problemName :: String,
evalProblem :: IO () # Or whatever your problem function signatures are
}
problems = [Problem]
problems = [
Problem {
problemName = "problem1",
evalProblem = do ... # Insert code here
},
Problem
problemName = "problem2",
evalProblem = do ... # Insert code here
}
]
main :: IO ()
main = do
args <- getArgs
case find (\x -> problemName x == (args!!0)) problems of
Just x -> evalProblem x
Nothing -> # Handle error
Edit: Just to clarify, I'd say the important takeaway here is that you have an XY Problem.
I have text file containing data like that:
13.
13.
[(1,2),(2,3),(4,5)].
And I want to read this into 3 variables in Haskell. But standard functions read this as strings, but considering I get rid of dot at the end myself is there any built-in parser function that will make Integer of "13" and [(Integer,Integer)] list out of [(1,2),(2,3),(4,5)] ?
Yes, it's called read:
let i = read "13" :: Integer
let ts = read "[(1,2),(2,3),(4,5)]" :: [(Integer, Integer)]
The example text file you gave has trailing spaces as well as the full stop, so merely cutting the last character doesn't work. Let's take just the digits, using:
import Data.Char (isDigit)
Why not have a data type to store the stuff from the file:
data MyStuff = MyStuff {firstNum :: Int,
secondNum:: Int,
intPairList :: [(Integer, Integer)]}
deriving (Show,Read)
Now we need to read the file, and then turn it into individual lines:
getMyStuff :: FilePath -> IO MyStuff
getMyStuff filename = do
rawdata <- readFile filename
let [i1,i2,list] = lines rawdata
return $ MyStuff (read $ takeWhile isDigit i1) (read $ takeWhile isDigit i2) (read $ init list)
The read function works with any data type that has a Read instance, and automatically produces data of the right type.
> getMyStuff "data.txt" >>= print
MyStuff {firstNum = 13, secondNum = 13, intPairList = [(1,2),(2,3),(4,5)]}
A better way
I'd be inclined to save myself a fair bit of work, and just write that data directly, so
writeMyStuff :: FilePath -> MyStuff -> IO ()
writeMyStuff filename somedata = writeFile filename (show somedata)
readMyStuff :: FilePath -> IO MyStuff
readMyStuff filename = fmap read (readFile filename)
(The fmap just applies the pure function read to the output of the readFile.)
> writeMyStuff "test.txt" MyStuff {firstNum=12,secondNum=42, intPairList=[(1,2),(3,4)]}
> readMyStuff "test.txt" >>= print
MyStuff {firstNum = 12, secondNum = 42, intPairList = [(1,2),(3,4)]}
You're far less likely to make little parsing or printing errors if you let the compiler sort it all out for you, it's less code, and simpler.
Haskell's strong types require you to know what you're getting. So let's forgo all error checking and optimization and assume that the file is always in the right format, you can do something like this:
data Entry = Number Integer
| List [(Integer, Integer)]
parseYourFile :: FilePath -> IO [Entry]
parseYourFile p = do
content <- readFile p
return $ parseYourFormat content
parseYourFormat :: String -> [Entry]
parseYourFormat data = map parseEntry $ lines data
parseEntry :: String -> Entry
parseEntry line = if head line == '['
then List $ read core
else Number $ read core
where core = init line
Or you could write a proper parser for it using one of the many combinator frameworks.