Read from file or from command line arguments haskell - haskell

I am designing the IO for a program I have written in Haskell. I would like to be able to read some arguments from file using -f or from the command line by default. The command line arguments get split up into convenient chunks meaning I do not need to parse them. However this does not happen when reading from file. So I wrote a simple little parser to do what I wanted.
parseFile :: String -> [String]
parseFile [] = [""]
parseFile ('"':xs) = parseString xs '"'
parseFile ('\'':xs) = parseString xs '\''
parseFile (' ':xs) = "":parseFile xs
parseFile ('\t':xs) = "":parseFile xs
parseFile ('\n':xs) = "":parseFile xs
parseFile (s:xs) = (\(a:xa)->(a++[s]):xa)$ parseFile xs
parseString :: String -> Char -> [String]
parseString (s:xs) a
| s == a = "":parseFile xs
| otherwise = (\(a:xa)->(a++[s]):xa)$ parseString xs a
I thought this would be pretty simple I would do something like
let myInput = if (flagDetected) then (parseFile $ readFile $ last args) else (args)
However the result of readFile is an IO action and not a string thus I cannot parse it. I've tried a number of configurations all which have fails, mostly for typing reasons. I've tried assigning the results before parsing, which resulted in a type mismatch between args which is a [[Char]] ( it is the result of using getArgs from the System.Environment module) and the result of readFile which is still an IO String.
I tried wrapping args with a return which of course doesn't fix this problem because of a type mismatch. I'm really at a loss of ideas now. I feel like this is probably a problem with the way I am thinking about the issue.
How can I get the behavior I desire?
Here's a related question I asked earlier. The root of the problem is the same but the answers on the last one were far to specific to help me here. It seems this is a frequent problem for me.

You need to do an inversion of control in the flow of your data.. Haskell's IO datatypes are there to manage the effects of i/o operations. Read string from a file is not a fact, the file couldn't exist, can be empty or so on. Many possibilities could happend. So for that reason main function use to be based on IO. The way you can use to "extract" wrapped value from a IO is using do-return block
inputIO = if flagDetected then
do
cnt <- (readFile path)
return (parseFile cnt)
else return args
main = do
input <- inputIO
return yourProgram input
Look here

Related

How to correctly parse arguments with Haskell?

I'm trying to learn how to work with IO in Haskell by writing a function that, if there is a flag, will take a list of points from a file, and if there is no flag, it asks the user to enter them.
dispatch :: [String] -> IO ()
dispatch argList = do
if "file" `elem` argList
then do
let (path : otherArgs) = argList
points <- getPointsFile path
else
print "Enter a point in the format: x;y"
input <- getLine
if (input == "exit")
then do
print "The user inputted list:"
print $ reverse xs
else (inputStrings (input:xs))
if "help" `elem` argList
then help
else return ()
dispatch [] = return ()
dispatch _ = error "Error: invalid args"
getPointsFile :: String -> IO ([(Double, Double)])
getPointsFile path = do
handle <- openFile path ReadMode
contents <- hGetContents handle
let points_str = lines contents
let points = foldl (\l d -> l ++ [tuplify2 $ splitOn ";" d]) [] points_str
hClose handle
return points
I get this: do-notation in pattern Possibly caused by a missing 'do'?` after `if "file" `elem` argList.
I'm also worried about the binding issue, assuming that I have another flag that says which method will be used to process the points. Obviously it waits for points, but I don't know how to make points visible not only in if then else, constructs. In imperative languages I would write something like:
init points
if ... { points = a}
else points = b
some actions with points
How I can do something similar in Haskell?
Here's a fairly minimal example that I've done half a dozen times when I'm writing something quick and dirty, don't have a complicated argument structure, and so can't be bothered to do a proper job of setting up one of the usual command-line parsing libraries. It doesn't explain what went wrong with your approach -- there's an existing good answer there -- it's just an attempt to show what this kind of thing looks like when done idiomatically.
import System.Environment
import System.Exit
import System.IO
main :: IO ()
main = do
args <- getArgs
pts <- case args of
["--help"] -> usage stdout ExitSuccess
["--file", f] -> getPointsFile f
[] -> getPointsNoFile
_ -> usage stderr (ExitFailure 1)
print (frobnicate pts)
usage :: Handle -> ExitCode -> IO a
usage h c = do
nm <- getProgName
hPutStrLn h $ "Usage: " ++ nm ++ " [--file FILE]"
hPutStrLn h $ "Frobnicate the points in FILE, or from stdin if no file is supplied."
exitWith c
getPointsFile :: FilePath -> IO [(Double, Double)]
getPointsFile = {- ... -}
getPointsNoFile :: IO [(Double, Double)]
getPointsNoFile = {- ... -}
frobnicate :: [(Double, Double)] -> Double
frobnicate = {- ... -}
if in Haskell doesn't inherently have anything to do with control flow, it just switches between expressions. Which, in Haskell, happen to include do blocks of statements (if we want to call them that), but you still always need to make that explicit, i.e. you need to say both then do and else do if there are multiple statements in each branch.
Also, all the statements in a do block need to be indented to the same level. So in your case
if "file" `elem` argList
...
if "help" `elem` argList
Or alternatively, if the help check should only happen in the else branch, it needs to be indented to the statements in that do block.
Independent of all that, I would recommend to avoid parsing anything in an IO context. It is usually much less hassle and easier testable to first parse the strings into a pure data structure, which can then easily be processed by the part of the code that does IO. There are libraries like cmdargs and optparse-applicative that help with the parsing part.

Get parameters with value

I would like to get the value of parameters from the input.
I have a program that read input, and I want to be able to get value like: ./test --params value and in my program get the value as an INT.
main = do
args <- getArgs
print args
Without using getOpt()
Thanks
Without using getOpt() Thanks
Huh. You're bound to reimplement at least parts of it, probably badly, but sure, let's assume your code is actually so special that it needs a hand-written arg parsing code.
Since getArgs :: IO [String], we already have the input tokenized by spaces, which is neat. However, in your case you want specifically --params value, and obtain value by Int.
There are numerous problems to solve here:
there might not be --params in the list at all
or there might be multiple instances of it
it might have no following token
or the following token might be another --otherparam
or the following token might not parse as Int
All of the above (and more) are possible to happen, because the input is completely unsanitized.
Solving all of the cases brings us back to using getOpt, so let's assume that there's exactly one --params in the list, and that it's followed by something that parses as an Int.
import System.Environment (getArgs)
main = do
args <- getArgs
let intArg = (read :: String -> Int) . head . tail . dropWhile (/= "--params") $ args
print intArg
If any of those assumptions is broken, this code will fail in numerous ways. Each of the problems requires a careful decision about a failure path. You might want to abort execution, provide a default value, you might want to use exceptions or a Maybe access API. Ultimately, you'll figure out that this is a solved problem and simply use getOpt:
import System.Console.GetOpt
import Data.Maybe (fromMaybe)
import System.Environment (getArgs)
data Arg = Params Int deriving Show
params :: String -> Arg
params = Params . read
options :: [OptDescr Arg]
options = [ Option ['p'] ["params"] (ReqArg params "VALUE") "Pass your params"]
main = do
argv <- getArgs
case getOpt Permute options argv of
(o,_no,[]) -> print o
(_,_,errs) -> ioError (userError (concat errs ++ usageInfo header options))
where header = "Usage:"
You can do this using the following function:
getParam :: [String] -> Maybe String
getParam [] = Nothing
getParam ("--param":next:_) = Just next
getParam (_:xs) = getParam xs
And you can use it as follows:
main = do
args <- getArgs
let param = getParam args
print param
If you’re interested in the details, getParam works by recursion:
The first line is a type signature stating that getParam takes a list of strings as its only argument, and it returns either a string or nothing (that’s what Maybe String means).
The second line states that if there are no arguments, it returns nothing.
The third line states that if the first argument is --param, match the next argument (by assigning it to the identifier next) and return it (albeit wrapped in Just; look up the ‘Maybe data type’ if you want to know more).
The fourth line states that if neither of the previous cases have matched, discard the first item in the list and try again.
There is one slight problem with this implementation of getParam: it returns a String, but you want an Int. You can fix this by using the read function, which can be used to convert a String to many other types, including Int. You could insert read in two places in the program: you could either replace Just next by Just (read next) (to get getParam to return an Int), or you could replace getParam args by read (getParam args) (to get getParam to return an String, and then convert that to an Int outside getParam).

Read in multiple lines from standard input with arguments in Haskell

I'm trying to read in multiple lines from standard input in Haskell, plus one argument, then do something with the current line and write something to the standard output.
In my case I am trying to normalize lambda expressions. The program may receive 1 or more lambda expressions to normalize and then it has to write the result (normalized form or error) to the standard output. And the program may receive an argument (the max number of reductions). Here is the main function:
main :: IO ()
main = do
params <- getArgs
fullLambda <- getLine
let lambda = convertInput fullLambda
let redNum | (length params) == 1 = read (head params)
| otherwise = 100
case (parsing lambda) of
Left errorExp -> putStrLn ("ERROR: " ++ lambda)
Right lambdaExp -> do
let normalizedLambdaExp = reduction lambdaExp redNum
if (isNormalForm normalizedLambdaExp) && (isClosed lambdaExp)
then putStrLn ("OK: " ++ show normalizedLambdaExp)
else putStrLn ("ERROR: " ++ lambda)
where
convertInput :: String -> String
convertInput ('\"':xs) = take ((length xs) - 2) xs
convertInput input = input
So this code handles one line and completes the reductions and then writes something to the standard output. How can I change this to handle multiple lines? I've read about replicateM but I can't seem to grasp it. My mind is very OO so I was thinking maybe some looping somehow, but that is surely not the preferred way.
Also, this program has to be able to run like this:
echo "(\x.x) (\x.x)" | Main 25
And will produce:
OK: (\x.x)
And if there are multiple lines, it has to produce the same kind of output for each line, in new lines.
But also has to work without the argument, and has to handle multiple lines. I spent time on google and here, but I'm not sure how the argument reading will happen. I need to read in the argument once and the line(s) once or many times. Does someone know a not too lengthy solution to this problem?
I've tried it like this, too (imperatively):
main :: IO ()
main = do
params <- getArgs
mainHelper params
main
mainHelper :: [String] -> IO ()
mainHelper params = do
fullLambda <- getLine
And so on, but then it puts this to the standard output as well:
Main: <stdin>: hGetLine: end of file
Thank you in advance!
It appears you want to:
Parse a command line option which may or may not exist.
For each line of input process it with some function.
Here is an approach using lazy IO:
import System.Environment
import Control.Monad
main = do args <- getArgs
let option = case args of
[] -> ... the default value...
(a:_) -> read a
contents <- getContents
forM_ (lines contents) $ \aline -> do
process option aline
I am assuming your processing function has type process :: Int -> String -> IO (). For instance, it could look like:
process :: Int -> String -> IO ()
process option str = do
if length str < option
then putStrLn $ "OK: " ++ str
else putStrLn $ "NOT OK: line too long"
Here's how it works:
contents <- getContents reads all of standard input into the variable contents
lines contents breaks up the input into lines
forM_ ... iterates over each line, passing the line to the process function
The trick is that getContents reads standard input lazily so that you'll get some output after each line is read.
You should be aware that there are issues with lazy IO which you may run into when your program becomes more complex. However, for this simple use case lazy IO is perfectly fine and works well.

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.

Using lines after readFile

I'm trying to do some programming in Haskell. I'm trying to read a file and then put every line in the file in a list by using the line function. Here's the partial code:
file = "muh.rtr"
readTrack :: String -> Track
readTrack file =
do let defFile = readFile file
let fileLines = lines defFile
However, I keep getting this error:
Parser.hs:22:39:
Couldn't match expected type `String' with actual type `IO String'
In the first argument of `lines', namely `defFile'
In the expression: lines defFile
In an equation for `fileLines': fileLines = lines defFile
I have been searching the Internet for hours now hoping to find some answers somewhere but I've not been so lucky so far.
You probably wanted either something like this:
readTrack :: String -> IO Track
readTrack file = do defFile <- readFile file
let fileLines = lines defFile
-- etc....
...or something like this:
readTrack :: String -> IO Track
readTrack file = do fileLines <- liftM lines (readFile file)
-- etc....
But what you really should do is stop, go find an introduction to the language such as Learn You a Haskell, and spend some time reading it.
Feeding code consisting entirely of very simple errors into GHC and then posting the error message on Stack Overflow is not a good way to learn.
The type of readFile is
readFile :: FilePath -> IO String
so you need to use <- to bind the result, and your function has to return IO Track.
readTrack :: String -> IO Track
readTrack file =
do defFile <- readFile file
let fileLines = lines defFile
...
I suggest reading a good tutorial on IO in Haskell, for example the Input and Output chapter of Learn You a Haskell for Great Good!.
readFile return an IO string. That is, it is an IO computation that returns a string. This means that you need to use <- instead of let to "get" the string its returning.
readTrack file =
do
defFile <- readFile file
...
You can use let to bind things that are not IO computations, such as the return value of lines, that is a regular string.
readTrack file =
do
defFile <- readFile file
let fileLines = lines defFile
...
Finally, you need to return the value you might want to try something like
readTrack file =
do
defFile <- readFile file
let fileLines = lines defFile
fileLines --Doesn't actually work!
but unfortunately, since we are inside a "do" block and are trying to return a monadic computation, we need to send the fileLines back into the io monad (remember, out function returns IO [String], not String!
readTrack file =
do
defFile <- readFile file
let fileLines = lines defFile
return fileLines
Note that the "return" here is not a return statement as would normaly be found in most languages and it should not be used in your pure functions.
All this might seem like a lot at first. I would suggest you stick to pure functions (without input and output / monads) until until you get a better hang on the language.
You can't do it like that -- you've run into the IO monad. What you need to do is something like:
readTrack :: String -> IO Track
readTrack file = do
defFile <- readFile file
let fileLines = lines deffile
...
return whatever
Think of IO T values as statements (as opposed to expressions) with return type T. Because statements have side effects, but expressions don't, you can never turn a statement into an expression; the type system enforces this, which is why your type signature won't work.
Note the different assignment-like syntax in the do block: in this example, the foo <- bar is used for IO operations, while the let baz = quux syntax is used for purely functional evaluation. This is more fallout from using monadic I/O -- it makes more sense in the full generality of Haskell's polymorphic type system, but it's not necessarily bad to have a syntactic indicator of pure vs. side-effecting operations, either.
In general, it is good practice to try keeping most of your implementation in the purely functional realm: implement your pure computation with regular functional methods, then describe your I/O operations in the IO monad. It is a common novice mistake to write loops in the IO monad which would be more appropriate as list comprehensions or recursive functions.
If your function is supposed to have type readTrack :: String -> Track, are you sure the String is a filename? Perhaps it's data - if so, don't use readFile. Write some sample data and test using that, eg
sampleData = "2 3\n1 30 234 45\n1 2 32 4\n5 3 4 23"
(The other question on SO about this homework didn't use file IO. I'll not link to it because you're in a crisis and might be tempted to copy, and in any case if you refuse to learn haskell at least I'll force you to improve your StackOverflow search skills! :) )
In any case I think you'll get more marks by solving the String problem than by solving the IO problem.
Delay the readFile issue until you've got the pure version working, otherwise you might end up writing most of your code in the IO monad which would be much more complex than necessary.
One you have a pure function readTrack :: String -> Track, you can do
readTrackFrom :: FilePath -> IO Track
readTrackFrom filename = fmap readTrack (readFile filename)
Now, fmap :: Functor f => (a -> b) -> f a -> f b, so takes pure functions and lifts them to work in a different computational context like IO.
Since IO is a Functor (look it up tomorrow, not tonight), we're using it as the type (String -> Track) -> IO String -> IO Track. That's good because readTrack :: String -> Track and (readFile filename) :: IO String.
If you want to, you can then >>= print or >>= writeFile newfilename as you see fit.
Don't forget to add deriving Show after use data Track =..., but you don't need to if you're using type Track = .....

Resources