How does getArgs work? - haskell

I am trying to understand getArgs in Haskell. Here is what I have:
import System.Environment
myFunFunction = do
args <- getArgs
return $ head args
What I am getting when I run the function is
*Main> myFunFunction
*** Exception: Prelude.head: empty list
Does this not work the same way as getLine? Why does it not ask for a command line argument?

The type of getArgs is IO [String]. When you bind it with <-, as in the OP, the bound symbol (args) gets the type [String], i.e. a list of strings.
The head function returns the first element in a list; it has the type [a] -> a. It's (in)famous for being unsafe, in the sense that if you apply it to an empty list, it'll crash:
Prelude System.Environment> head []
*** Exception: Prelude.head: empty list
That's what's happening here. getArgs gives you the arguments you supplied at the command line when you ran the program. If you don't supply any arguments at the command line, then the returned list will be empty.
The getArgs function isn't interactive. It'll just return the arguments supplied from the command line, if any were supplied.

Related

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

Input arguments using getArgs returns nothing

I am trying to understand getArgs but I am getting a weird behavior that I am not understanding. Here is my program:
getMyArgs :: IO [String]
getMyArgs =do
x <- getArgs
return x
I run this and get:
*Main> hello <- getMyArgs
*Main>
Why doesn't it return my argument passed? I tried to put in a " show() " but that turns it into a String instead of a [String]
getMyArgs :: IO [String]
getMyArgs =do
x <- getArgs
return x
The do notation desugars to:
getMyArgs :: IO [String]
getMyArgs = getArgs >>= \x -> return x
Using the right identity we can rewrite this to:
getMyArgs :: IO [String]
getMyArgs = getArgs
So you've just defined a new name for getArgs. Now why does getArgs not show your program arguments? Well it appears you didn't provide any program arguments. In the interpreter it can be tricky to provide arguments - one way is to :set them:
Prelude> :set args hello world
Prelude> import System.Environment
Prelude System.Environment> getArgs
["hello","world"]
EDIT: Oh you might be looking to print the value you bound. Consider:
Prelude System.Environment> hello <- getArgs
Prelude System.Environment> print hello
["hello","world"]
Thanks to #4castle for this observation.
Assume your Haskell program is compiled to an executable foo. When you call your program, you want to pass some runtime arguments to your program eg foo param1 param2 . Depending on the values of param1 and param2 you will take different actions in your program.
Now with the getArgs function you get access to these parameters in your Haskell program.
In GHCi this argument passing can be simulated. Either with the :set args paarm1 param2 command as shown in the answer of Thomas M. DuBuisson
or you call your main program in GHCI with :main param1 param2 .
In both scenarios getEnv will return IO ["param1", "param2"]

Running my program with command line arguments

I am attempting to run my program using the command line. I am trying to return my command line arguments:
import System.Environment
import Data.List
main :: IO()
main = do
args <- getArgs
progName <- getProgName
putStrLn "The arguments are:"
mapM putStrLn args
putStrLn "The program name is:"
putStrLn progName
I am executing the code by calling the main function with my arguments:
main argument arguments "more arguements"
However, I am getting a complier error:
<interactive>:33:6: Not in scope: ‘argument’
<interactive>:33:15: Not in scope: ‘arguments’
Is there an issue with how I am calling my function with my arguments?
You have to use :main if you want to simulate command line arguments. main alone only executes your IO () action, but doesn't actually build the arguments. For all what GHCi knows, main doesn't necessarily need to be IO (), it could be Int -> Int -> IO ().
However, if you use :main, GHC will use main in the same way it would get invoked during an runhaskell call, e.g. with interpreting the following parameters as command line arguments.
Alternatively, you can use withArgs from System.Environment:
ghci> withArgs ["argument", "arguments", "more arguments"] main

"User error" pattern matching failure when using getArgs

import Control.Concurrent (forkIO)
import System.Environment (getArgs)
main= do
[a,b]<- getArgs
putStrLn $ "command line arguments: " ++ show [a,b]
When I compiled it, it was all right, but when I ran it,
it said "user error (Pattern match failure in do expression)", what is wrong here?
The problem is that you're pattern matching [a, b] on the return value of getArgs. If you run your program with anything other than 2 arguments, then the return value will not match the pattern [a, b]. So unless you run this program as
$ ./xie 1 2
command line arguments: ["1","2"]
It will throw an error. Instead, if you wrote your code
main = do
args <- getArgs
case args of
[a, b] -> putStrLn $ "command line arguments: " ++ show [a, b]
_ -> putStrLn "Invalid number of arguments"
then you would never fail on a pattern match.
The pattern [a,b] only matches a 2-element list, so if getArgs returns a list with a different number of elements, the match will fail.
When using do notation, when a match fails, the fail function is called, which in the case of IO causes a userError to be thrown.

Call main function with arguments

I have problem with reading from file. Whenever I need to read from a file, I do something like this:
main = do x <- readFile "/tmp/foo.txt"
putStr x
But now I would like the path to be an argument, so I tried the following
main s = do x <- readFile s
putStr x
It doesn't work. I see the following error:
Couldn't match expected type `IO t0'
with actual type `FilePath -> IO ()'
In the expression: main
When checking the type of the function `main'
My homework is to write a program and the program has to contain a main function (because it will be compiled) and argument of call must contain the name of the file. I'm not sure I understand this and I don't know how to proceed. I will grateful for some help.
The Haskell report specifies that the main function always has type IO t, (for some type t which will be ignored) and hence never takes normal function arguments, so this is not the right approach.
You are looking for the function getArgs (for which you have to import the module System.Environment. It returns the arguments passed to your program as a list of Strings.
So your code would look like:
import System.Environment
main = do
args <- getArgs
case args of
[file] -> do
x <- readFile file
putStr x
_ -> putStrLn "Wrong number of arguments"
In Haskell, the arguments are NOT given to the main function because of the way Haskell binds its start up and to remain consistent. You need to use System.Environment.getArgs.
In particular, because Haskell is a pure functional language, main is a monadic action that organizes the side-effect-ful computations performed by the software – the result computed by main is discarded, because in functional languages you are detached from the environment w.r.t. computation and only interact with it as a side-effect.
Example
import System.Environment
main = do x <- getArgs; print x
This will print out whatever you type on the command line.
The Haskell wiki has an excellent tutorial on the topic.

Resources