Get parameters with value - haskell

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).

Related

Read from file or from command line arguments 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

Option.Applicative: How to parse a combined parser with a flag?

I have complicated command line options, as
data Arguments = Arguments Bool (Maybe SubArguments)
data SubArguments = SubArguments String String
I want to parse these subarguments with a flag:
programName --someflag --subarguments "a" "b"
programName --someflag
I already have
subArgParser = SubArguments <$> argument str <*> argument str
mainParser = MainArgs <$> switch
(long "someflag"
<> help "Some argument flag")
<*> ???
(long "subarguments"
<> help "Sub arguments"
What do I have to write at the ???
Your question turned out to be more complicated than you think. Current optparse-applicative API is not supposed to be used with such cases. So you probably may want to change the way you handle CLI arguments or switch to another CLI parsing library. But I will describe most closest way of achieving your goal.
First, you need to read other two SO questions:
1. How to parse Maybe with optparse-applicative
2. Is it possible to have a optparse-applicative option with several parameters?
From first question you know how to parse optional arguments using optional function. From second you learn some problems with parsing multiple arguments. So I will write here several approaches how you can workaround this problem.
1. Naive and ugly
You can represent pair of strings as pair of String type and use just naive show of this pair. Here is code:
mainParser :: Parser Arguments
mainParser = Arguments
<$> switch (long "someflag" <> help "Some argument flag")
<*> optional (uncurry SubArguments <$>
(option auto $ long "subarguments" <> help "some desc"))
getArguments :: IO Arguments
getArguments = do
(res, ()) <- simpleOptions "main example" "" "desc" mainParser empty
return res
main :: IO ()
main = getArguments >>= print
Here is result in ghci:
ghci> :run main --someflag --subarguments "(\"a\",\"b\")"
Arguments True (Just (SubArguments "a" "b"))
2. Less naive
From answer to second question you should learn how pass multiple arguments inside one string. Here is code for parsing:
subArgParser :: ReadM SubArguments
subArgParser = do
input <- str
-- no error checking, don't actually do this
let [a,b] = words input
pure $ SubArguments a b
mainParser :: Parser Arguments
mainParser = Arguments
<$> switch (long "someflag" <> help "Some argument flag")
<*> optional (option subArgParser $ long "subarguments" <> help "some desc")
And here is ghci output:
ghci> :run main --someflag --subarguments "x yyy"
Arguments True (Just (SubArguments "x" "yyy"))
The only bad thing in second solution is that error checking is absent. Thus you can use another general purpose parsing library, for example megaparsec, instead of just let [a,b] = words input.
It's not possibile, at least not directly. You might find some indirect encoding that works for you, but I'm not sure. Options take arguments, not subparsers. You can have subparsers, but they are introduced by a "command", not an option (i.e. without the leading --).

Assigning command line arguments to variables

I need to convert my command line argument chars to Ints. I would like to assign each one to a variable and use them in my function. I have the following:
import System.Environment
getIntArg :: IO Int
getIntArg = fmap (read . head) getArgs
main = do
n <- getIntArg
print n
However, it doesn't loop through all the arguments only printing one. Also, would I assign each one to a variable to use?
I'm new to FP.
if you want to map all arguments to Ints you should use
getIntArgs :: IO [Int]
getIntArgs = fmap (map read) $ getArgs
or a bit shorter:
getIntArgs = map read <$> getArgs
instead.
This way you can do
main = do
ns <- getIntArgs
print ns
and then get the ones you are interested in with (!!) or if you know/expect at least a fixed number of Int - arguments with
(n1:n2:n3:_) <- getIntArgs
but of course you should maybe check the length first - also this will fail if the user decides to input things that cannot get parsed into Ints
So if you don't want to reinvent the wheel (which might be ok if you want to learn or only need a quick solution) you maybe want to look around and use a existing package - for example parseargs to do this for you

A simple way of parsing a program arguments

I've seen some approaches about parsing program arguments. They seem to be too complicated. I need a simple solution. But I also want to be able (preferably, not necessarily) to refer to the arguments by name. So if a command like looks like this:
./MyApp --arg1Name arg1Value --arg2Name arg2Value
then I'd like to treat them as args["arg1Name"] and args["arg2Name"] to get their values. I know that's not a valid Haskell code, though. What I have now is not much:
main = do
[args] <- getArgs
I repeat, I'd like a simple solution, preferably without involving any third-party haskell libraries.
optparse-applicative is great for argument parsing, and very easy to use! Writing your own argument parser will be much more difficult to get right, change, extend, or otherwise manage than if you take 10 minutes to write a parser with optparse-applicative.
Start by importing the Options.Applicative module.
import Options.Applicative
Next create a data type for your command-line configuration.
data Configuration = Configuration
{ foo :: String
, bar :: Int
}
Now for the workhorse, we create a parser by using the combinators exported from optparse-applicative. Read the documentation on Options.Applicative.Builder for the full experience.
configuration :: Parser Configuration
configuration = Configuration
<$> strOption
( long "foo"
<> metavar "ARG1"
)
<*> option
( long "bar"
<> metavar "ARG2"
)
Now we can execute our Parser Configuration in an IO action to get at our command-line data.
main :: IO ()
main = do
config <- execParser (info configuration fullDesc)
putStrLn (show (bar config) ++ foo config)
And we're done! You can easily extend this parser to support a --help argument to print out usage documentation (make a new parser with helper <*> configuration and pass it to info), you can add default values for certain arguments (include a <> value "default" clause in the arguments to strOption or option), you can support flags, or sub-parsers or generate tab-completion data.
Libraries are a force multiplier! The investment you make in learning the basics of a good library will pay dividends in what you're able to accomplish, and tasks will often be easier (and quicker!) with the proper tool than with a "fast" solution thrown together out of duct tape.
How about just parsing them in pairs and adding them to a map:
simpleArgsMap :: [String] -> Map String String
simpleArgsMap [] = Map.empty
simpleArgsMap (('-':'-':name):value:rest)
= Map.insert name value (simpleArgsMap rest)
simpleArgsMap xs = error $ "Couldn't parse arguments: " ++ show xs
A simple wrapper program to show this working:
module Args where
import System.Environment ( getArgs )
import Control.Applicative ( (<$>) )
import qualified Data.Map as Map
import Data.Map ( Map )
main = do
argsMap <- simpleArgsMap <$> getArgs
print $ Map.lookup "foo" argsMap

Understanding when to use let and <-

I have been playing around with Haskell for a bit now but I have not fully grasped how to use third party functions that run inside a Monad. Every time I go back to reading articles about Monads, etc. I get a good understanding but when it comes to applying them to real-world code, I cannot figure why a piece of code does not work. I resort to trial and error and usually get it to compile but I feel I should be able to use them properly the first time without trying to go through my heuristic of changes (try let, <-, liftM, etc.)
So I would like to ask a few questions based on this simple function, which admittedly does a lot of interesting things.
import Text.XML.HXT.Core
import Text.HandsomeSoup
import Data.String.Utils
function h = do
let url = myUrlBuilder h
doc = fromUrl url
res = runX $ doc >>> css "strong" /> getText
--nres = liftM rmSpaceAndBang (res)
res
rmSpaceAndBang ps = map (\x-> replace "!" "" (strip x)) ps
The above code compiles. I have purposefully left out the type declarations as what I thought it should be doesn't compile. So here are my questions.
Why can I not do res <- runX ... and return res that way?
Why should res be inside a let statement and not be bound the result of action? As I understand it, do x <- a1; a2 is equivalent to a1 >>= \x -> a2. How is that different when you let x = a1?
When I used <- I got the following error and if not for my trial and error approach I would not have been able to figure out that I need to use let here.
Couldn't match type `[]' with `IO'
Expected type: IO String
Actual type: [String]
While I focused on res above, my lack of understanding applies to other let statements in the function as well.
How do I find the return type of res?
I couldn't figure out a way to search hackage for getText (hxt seems too big to look through module by module. Probably will try Google site search next time). In the end, I ended up typing up some parts of the code in GHCi and did :t res. It told me it is [String]. Is there a better way to do this?
Since res is of type [String] I thought I will put [String] as the return type for my function. But GHC says it should be IO [String] (compiles). Why did :t give me the wrong information first?
When functions return IO String, what's the best way to use pure functions on them?
Now that I am stuck inside IO [String] I need to use to lift everywhere I do string operations. Is there a better way to do this?
Hopefully I will learn enough from this that I will be able to use right syntax without resorting to blindly trying a few combinations.
Update:
The key piece I was missing was the fact res is not a value but rather an action. So I have 2 choices: one is is my above code with let res = but call it at the end and the other is to do res <- but then do return (res).
The advantage of using res <- is that I can get rid of the liftM as res is now [String] (see #duplode's answer below).
Thanks!
In your code, res is an IO [String]. I do not doubt that you got [String] through GHCi at first, but I believe you tested it with
>>> res <- runX $ doc >>> css "strong" /> getText
>>> :t res
res :: [String]
Which is not equivalent to your code. The difference is that let just binds your IO [String] action without running it, while <- in a do block runs the action and binds the result, in this case a [String].
Now that I am stuck inside IO [String] I need to use to lift
everywhere I do string operations. Is there a better way to do this?
Within a do block, sometimes it is more convenient to write:
res <- runX $ doc >>> css "strong" /> getText
return $ rmSpaceAndBang res
Which is strictly equivalent to using liftM (or fmap):
liftM rmSpaceAndBang $ doc >>> css "strong" /> getText
For a fast answer, let doesn't run anything, it's just makes the lhs as a synonym for rhs.
You actually need a monadic function inside the do for computation be executed.
main = do
let func = print "I need to be called"
print "I don't need to be called"
func
outputs:
"I don't need to be called"
"I need to be called"
So res in your code is not a value, it's a monadic action/function.
Remember that <- is tied to >>=, and requires a a -> m b on the rhs.
let has no requirements.

Resources