Can not print to file using IO Monad - haskell

Hello i have done my JSon type and i am trying to it to a file.
I can do this from the prelude but i can't do it when using the IO Monad.I get the following error:
Main.hs:13:24: error:
* Couldn't match type `Char' with `[Char]'
Expected type: String
Actual type: Char
* In the second argument of `writeFile', namely `val'
In a stmt of a 'do' block: writeFile out val
In the expression:
do val <- renderJValue sample
writeFile out val
|
13 | writeFile out val
| ^^^
Main
module Main where
import Jlib
import Put
import Data.Typeable
import System.Environment
out="data.txt"
main::IO()
main=do
val<-renderJValue sample
writeFile out val
Why would this not work in the IO Monad since renderJValue sample in the prelude works ok .
Jlib.hs
data JValue=JString String
|JNumber Double
|JBool Bool
|JNull
|JObject [(String,JValue)]
|JArray [JValue]
deriving (Eq,Ord,Show)
Put.hs
sample=JArray[
JObject [("name",JString "adita"),("age",JNumber 13)],
JObject [("name",JString "dan"),("kids",JNumber 3)] ,
JNumber 3,
JBool False,
JString "Howdy"
]
P.S renderJValue returns a string
P.S: if i start the prelude i load the module and i render the value it works:
Prelude System.Environment Put> :load Put
Ok, two modules loaded.
Prelude System.Environment Put> renderJValue sample
"[{name:adita,age:13.0},{name:dan,kids:3.0},3.0,False,Howdy]"

You here use renderJValue sample as if it is an IO String:
main :: IO()
main=do
val <- renderJValue sample
writeFile out val
But it is in fact (given it is a function similar to this one) a function with signature renderJValue :: JValue -> String. So no IO is involved. In that case we do not use the arrow notation.
We can call the function "inline":
main :: IO()
main = do
writeFile out (renderJValue sample)
or even shorter:
main :: IO()
main = writeFile out (renderJValue sample)
But in case the expression is rather long, this can become quite ugly. We can decide to use a let statement in that case.
You can solve this by either removing the putStrLn:
main :: IO()
main = do
let val = renderJValue sample
writeFile out val

Related

Filename argument in Haskell

How comes that this is working:
module Main where
import System.Environment (getArgs)
import qualified Data.ByteString.Char8 as B
import Data.ByteString.Char8 (ByteString)
type Field = ByteString
type Row = [Field]
type CSV = [Row]
main :: IO ()
main = do
contents <- B.readFile "test.csv"
print (parseCSV contents)
-- used with "./myprogram" to read "test.csv"
But not if I replace "test.csv" with a command-line argument in main:
-- ... same as before ...
main :: IO ()
main = do
contents <- B.readFile $ head getArgs
print (parseCSV contents)
-- used with "./myprogram test.csv" to read "test.csv"
With the latter I get this error at compile time:
csv.hs:20:35: error:
• Couldn't match expected type ‘[FilePath]’
with actual type ‘IO [String]’
• In the first argument of ‘head’, namely ‘getArgs’
In the second argument of ‘($)’, namely ‘head getArgs’
In a stmt of a 'do' block: contents <- B.readFile $ head getArgs
EDIT --
I initially omitted a part of the code, which I thought was not necessary to understand the question. It's corrected now.
Remember that getArgs :: IO [String] is also monadic, so you need to extract it first in the do block before trying to get the head of it:
main :: IO ()
main = do
args <- getArgs -- now `args :: [String]`
contents <- B.readFile (head args)
print (parseCSV contents)

How to read an array of binary data from a file in haskell?

Trying to read a binary file of Word8, I have the following program:
import qualified Data.Binary as B
type Chars = [B.Word8]
printChars :: Chars -> IO()
printChars cs = mapM_ print cs
main :: IO()
main = do
chars <- B.decodeFile "chars"
printChars chars
When I run it, I get an error:
$ ./test
test: too few bytes. Failed reading at byte position 241
It seems decodeFile expects an infinite list.
How can I tell it to just read as many elements as possible?
Edit:
Here was the code I was looking for: (This works with any type, not just Word8.)
import Prelude hiding ( readFile )
import Data.ByteString.Lazy ( readFile )
import Data.Binary.Get ( isEmpty, runGet )
import qualified Data.Binary as B
type Chars = [B.Word8]
printChars :: Chars -> IO()
printChars cs = mapM_ print cs
-- see http://hackage.haskell.org/package/binary-0.7.1.0/docs/Data-Binary-Get.html
-- function getTrades
getChars = do
e <- isEmpty
if e then return []
else do
c <- B.get
cs <- getChars
return (c:cs)
main :: IO()
main = do
input <- readFile "chars"
printChars $ runGet getChars input
Data.Binary is used to serialize known types in a canonical way (defined by the Binary class instance). In general it isn't intended for unstructured data.
In the case you gave us, you are attempting to deserialize the bytes in a file to an object of type [B.Word8]. If you look in the Data.Binary source code, you can see the following
instance Binary a => Binary [a] where
get = do n <- get :: Get Int
getMany n
which basically means that arrays are stored as follows
[length of array, val1, val2, ....]
so, when you applied the value to the file, it read the first Int in the file (no doubt, a very large number), then attempted to read that number of values.
If you just want to load the file as bytes, you should use Data.ByteString.getContents

how to parse yahoo csv with parsec

how to parse into array such as open[i],high[i],low[i],close[i]
testhaskell.hs:22:5:
Couldn't match type `[]' with `IO'
Expected type: IO a0
Actual type: [a0]
In the return type of a call of `map'
In a stmt of a 'do' block: map (\ line -> sentence line) allLines
In the expression:
do { handle <- openFile
"C:\\Users\\ivan\\Downloads\\0388.HK.csv" ReadMode;
contents <- hGetContents handle;
let allLines = lines contents;
map (\ line -> sentence line) allLines;
.... }
testhaskell.hs:22:19:
Couldn't match expected type `String -> a0'
with actual type `Text.Parsec.Prim.ParsecT
String () Data.Functor.Identity.Identity [String]'
import System.IO
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString as Str
import Text.ParserCombinators.Parsec
word :: Parser String
word = many1 letter
sentence :: Parser [String]
sentence = do{ words <- sepBy1 word separator
; oneOf ".?!" <?> "end of sentence"
; return words
}
separator :: Parser ()
separator = skipMany1 (space <|> char ',' <?> "")
main = do
handle <- openFile "C:\\Users\\ivan\\Downloads\\0005.HK.csv" ReadMode
contents <- hGetContents handle
let allLines = lines contents
map (\line -> sentence line) allLines
--putStr contents
hClose handle
update:
module Main where
import qualified Data.ByteString.Char8 as B
import Data.Map ((!))
import Data.Text
import qualified Data.Vector as V
import System.Directory
import Test.Framework (Test, defaultMain, testGroup)
import Test.Framework.Providers.API
import Test.HUnit ((#=?))
import Data.CSV.Conduit
main :: IO ()
main = defaultMain tests
tests :: [Test]
tests = [testGroup "Basic Ops" baseTests]
baseTests :: [Test]
baseTests =
[
testCase "simple parsing works" test_simpleParse
]
test_simpleParse :: IO ()
test_simpleParse = do
(d :: V.Vector (MapRow B.ByteString)) <- readCSVFile csvSettings testFile1
V.mapM_ assertRow d
where
assertRow r = v3 #=? (v1 + v2)
where v1 = readBS $ r ! "Open"
v2 = readBS $ r ! "High"
v3 = readBS $ r ! "Low"
v4 = readBS $ r ! "Close"
csvSettings :: CSVSettings
csvSettings = defCSVSettings { csvQuoteChar = Just '`'}
testFile1 :: FilePath
testFile1 = "C:\\Users\\ivan\\Downloads\\0005.HK.csv"
readBS :: B.ByteString -> Int
readBS = read . B.unpack
testhaskell.hs:52:5: Not in scope: `testCase'
testhaskell.hs:58:9:
Illegal type signature: `V.Vector (MapRow B.ByteString)'
Perhaps you intended to use -XScopedTypeVariables
In a pattern type-signature
I'd strongly recommend you not do this. There are a number of high-quality CSV libraries on Hackage, and rolling your own is a recipe of problems. At FP Complete, we use csv-conduit, though cassava is also a great library. I'd recommend you try out one of them.

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"

Type error using Criterion

I read the documentation and some articles that talk about the package, but I'm new to Haskell and did not understand much but I tried ....
Below is what I did:
module Main where
{-# LANGUAGE BangPatterns #-}
import Control.Parallel(par,pseq)
import Control.Exception
import Data.List
import IO
import Data.Char
import Criterion.Main (defaultMain, bench)
learquivo :: FilePath -> IO ([[Int]])
learquivo "mkList1.txt" = do
conteudo <- readFile "mkList1.txt"
return (read conteudo)
main = defaultMain [
bench "map sort learquivo" $ \n -> map sort learquivo
]
As it did the following error occurred:
Couldn't match expected type [[a]]
against inferred type FilePath -> IO [[Int]]
Just so you have how I usually run it, using the nf or whnf functions, I'll give my code:
import Data.List
import Criterion.Main
main :: IO ()
main = do
-- content <- learquivo "mkList1.txt"
let content = [ [big, big - step.. 0] | big <- [1000..1010], step <- [1..5]] :: [[Int]]
defaultMain
[ bench "benchmark-name" (nf (map sort) content)]
EDIT: If you like this then also give plotting a try:
module Main where
import Data.List
import Criterion.Main
import Criterion.Config
import Criterion.MultiMap as M
main :: IO ()
main = do
let myConfig = defaultConfig {
-- Always display an 800x600 window with curves.
cfgPlot = M.singleton KernelDensity (Window 800 600)
}
let content = [ [big, big-step.. 0] | big <- [1000..1010], step <- [1..5]] :: [[Int]]
defaultMainWith myConfig (return ())
[ bench "benchmark-name" (nf (map sort) content)]
The problem is this: map sort learquivo
sort expects a list, and so map sort expects a list of lists ([[a]]), whereas the type of learquivo is of type FilePath -> IO [[Int]].
You probably want something like:
main = do
contents <- learquivo "mkList1.txt"
defaultMain [
bench "map sort learquivo" $ \n -> map sort contents
]
There are various things in your code that could be cleaned up, but that should get you going.

Resources