Filename argument in Haskell - 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)

Related

Can not print to file using IO Monad

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

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"

How do I putStrLn a Data.ByteString.Internal.ByteString?

I'm learning haskell and decided to try writing some small test programs to get use to Haskell code and using modules. Currently I'm trying to use the first argument to create a password hash using the Cypto.PasswordStore. To test out my program I'm trying to create a hash from the first argument and then print the hash to screen.
import Crypto.PasswordStore
import System.Environment
main = do
args <- getArgs
putStrLn (makePassword (head args) 12)
I'm getting the following error:
testmakePassword.hs:8:19:
Couldn't match expected type `String'
with actual type `IO Data.ByteString.Internal.ByteString'
In the return type of a call of `makePassword'
In the first argument of `putStrLn', namely
`(makePassword (head args) 12)'
In a stmt of a 'do' block: putStrLn (makePassword (head args) 12)
I've been using the following links as references but I am now just trial-erroring to no avail.
http://hackage.haskell.org/packages/archive/bytestring/0.9.0.4/doc/html/Data-ByteString-Internal.html
http://hackage.haskell.org/packages/archive/pwstore-purehaskell/2.1/doc/html/Crypto-PasswordStore.html
You haven't imported ByteString, so it's trying to use the String version of putStrLn.
I've provided toBS for the String->ByteString conversion.
Try
import Crypto.PasswordStore
import System.Environment
import qualified Data.ByteString.Char8 as B
toBS = B.pack
main = do
args <- getArgs
makePassword (toBS (head args)) 12 >>= B.putStrLn
You have to do two things differently. First, makePassword is in IO, so you need to bind the result to a name and then pass the name to the IO function. Secondly, you need to import IO functions from Data.ByteString
import Crypto.PasswordStore
import System.Environment
import qualified Data.ByteString as B
main = do
args <- getArgs
pwd <- makePassword (B.pack $ head args) 12
B.putStrLn pwd
Or, if you won't be using the password result anywhere else, you can use bind to connect the two functions directly:
main = do
args <- getArgs
B.putStrLn =<< makePassword (B.pack $ head args) 12

Idiomatic way to conditionally process IO in Haskell

I'm writing a little shell script in Haskell which can take an optional argument. However, if the argument is not present, I'd like to get a line from stdin in which to ask for a value.
What would be the idiomatic way to do this in Haskell?
#!/usr/bin/env runhaskell
import Control.Applicative ((<$>))
import Data.Char (toLower)
import IO (hFlush, stdout)
import System.Environment (getArgs)
main :: IO ()
main = do args <- getArgs
-- here should be some sort of branching logic that reads
-- the prompt unless `length args == 1`
name <- lowerCase <$> readPrompt "Gimme arg: "
putStrLn name
lowerCase = map toLower
flushString :: String -> IO ()
flushString s = putStr s >> hFlush stdout
readPrompt :: String -> IO String
readPrompt prompt = flushString prompt >> getLine
Oh, and if there's a way to do it with something from Control.Applicative or Control.Arrow I'd like to know. I've become quite keen on these two modules.
Thanks!
main :: IO ()
main = do args <- getArgs
name <- lowerCase <$> case args of
[arg] -> return arg
_ -> readPrompt "Gimme arg: "
putStrLn name
This doesn't fit your specific use case, but the question title made me think immediately of when from Control.Monad. Straight from the docs:
when :: Monad m => Bool -> m () -> m ()
Conditional execution of monadic expressions.
Example:
main = do args <- getArgs
-- arg <- something like what FUZxxl did..
when (length args == 1) (putStrLn $ "Using command line arg: " ++ arg)
-- continue using arg...
You can also use when's cousin unless in similar fashion.

Resources