Haskell: How to read in command line args as int? - haskell

I am trying to get an int value from the command line and pass it to the disp function.
import System(getArgs)
main = do
args <- getArgs
disp $ read $ head args :: Int
disp n = take n $ repeat 'S'
The error given by ghc is
Couldn't match expected type `Int' with actual type `[Char]'
In the expression: disp $ read $ head args :: Int
In the expression:
do { args <- getArgs;
disp $ read $ head args :: Int }
In an equation for `main':
main
= do { args <- getArgs;
disp $ read $ head args :: Int }
Thanks.

The problem is with precendence: Type signatures always try to apply to the whole expression (only scoped using parenthesis). So your disp $ read $ head args :: Int parses as (disp $ read $ head args) :: Int, which is obviously not correct. You can either use parenthesis like so:
disp (read $ head args :: Int)
or omit the type signature, as GHC can infer it in this case:
disp $ read $ head args
This code still won't work as-is, because you're in the IO monad so you need to produce IO actions. You can do this by printing the result, for example:
putStrLn $ disp $ read $ head args

You can encapsulate pulling an integer command-line argument like so:
getIntArg :: IO Int
getIntArg = fmap (read . head) getArgs
Which works because Monads are Functors. Or you can do the same thing with liftM.
That way your main function just becomes:
main = do
n <- getIntArg
disp n
Provided you add some type of print function to disp as discussed in the other answers.

Just remove that explicit type you added there, and it'll work. Have faith in type inference. :) Add print $ ... there, or something similar, to correct the new error.
What happens is, the type of take is known, so the type of the argument that disp expects is known too. It is Int. So the appropriate read will be applied.
Do less, get done more.

Related

Haskell: How to use a where statement under a do block [duplicate]

This question already has an answer here:
How to use variable from do block assignment line in a where clause?
(1 answer)
Closed 2 years ago.
I was making a program with Haskell and it's IO, when I came across an error I don't understand. When I use a where statement after a do block it does not do the same thing as without the do block.
The program that works is:
import Control.Monad
prog :: IO()
prog = do m <- getLine
n <- getLine
p <- getLine
replicateM_ (read m :: Int) (putStrLn n)
replicateM_ (read m :: Int) (putStrLn p)
But when I replace the read m :: Int with a where statement like this:
import Control.Monad
prog1 :: IO()
prog1 = do m <- getLine
n <- getLine
p <- getLine
replicateM_ (a) (putStrLn n)
replicateM_ (a) (putStrLn p)
where
a = read m :: Int
I get the error:
Template.hs:23:21: error: Variable not in scope: m :: String
|
23 | a = read m :: Int
| ^
I have looked what the problem could be, and I think it has to do with the type of m, which is IO String. I know you have to stay in the IO type (once you are in it) to be able to work with the string. But I don't understand why the 'where' would "break out" of this IO type. To my understanding the two examples I gave are functional the same. First I thought that the error wouldn't be fixed by writing the program without the where, because the function read is from the type read :: Read a => String -> a and my input in the first program is also IO String. So why didn't my first program give me an error? Could someone explain what I understand wrong and how I can fix my program so I only have to execute read m :: Int once? Just some tips of how to use a where statement under a do block would also help.
The original program I got the problem in is longer and not all relevant so I used this minimal working example to explain the essence of my question. In my original program I have multiple statements after where, so I don't want to substitute it all like I did in this example.
The bindings in the do block are opaque to the where statement after it, so you can't reference anything defined in the do block inside the where statement. You don't need to either, since you can use let directly inside do:
prog1 = do m <- getLine
n <- getLine
p <- getLine
-- alternatively: [m, n, p] <- replicateM 3 getLine
-- use a let statement
let a = read m :: Int
replicateM_ a (putStrLn n)
replicateM_ a (putStrLn p)

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"]

Using content of a string to call function with same name

I have a main like the following:
main :: IO ()
main = do
args <- getArgs
putStrLn $ functionName args
where
functionName args = "problem" ++ (filter (/= '"') $ show (args!!0))
Instead of putting the name to stdout like I do it right now, I want to call the function.
I am aware of the fact, that I could use hint (as mentioned in Haskell: how to evaluate a String like "1+2") but I think that would be pretty overkill for just getting that simple function name.
At the current stage it does not matter if the program crashes if the function does not exist!
Without taking special measures to preserve them, the names of functions will likely be gone completely in a compiled Haskell program.
I would suggest just making a big top-level map:
import Data.Map ( Map )
import qualified Data.Map as Map
functions :: Map String (IO ())
functions = Map.fromList [("problem1", problem1), ...]
call :: String -> IO ()
call name =
case Map.lookup name of
Nothing -> fail $ name + " not found"
Just m -> m
main :: IO ()
main = do
args <- getArgs
call $ functionName args
where
functionName args = "problem" ++ (filter (/= '"') $ show (args!!0))
If you're going to do this, you have a few approaches, but the easiest by far is to just pattern match on it
This method requires that all of your functions you want to call have the same type signature:
problem1 :: Int
problem1 = 1
problem2 :: Int
problem2 = 2
runFunc :: String -> Maybe Int
runFunc "problem1" = Just problem1
runFunc "problem2" = Just problem2
runFunc _ = Nothing
main = do
args <- getArgs
putStrLn $ runFunc $ functionName args
This requires you to add a line to runFunc each time you add a new problemN, but that's pretty manageable.
You can't get a string representation of an identifier, not without fancy non-standard features, because that information isn't retained after compilation. As such, you're going to have to write down those function names as string constants somewhere.
If the function definitions are all in one file anyway, what I would suggest is to use data types and lambdas to avoid having to duplicate those function names altogether:
Data Problem = {
problemName :: String,
evalProblem :: IO () # Or whatever your problem function signatures are
}
problems = [Problem]
problems = [
Problem {
problemName = "problem1",
evalProblem = do ... # Insert code here
},
Problem
problemName = "problem2",
evalProblem = do ... # Insert code here
}
]
main :: IO ()
main = do
args <- getArgs
case find (\x -> problemName x == (args!!0)) problems of
Just x -> evalProblem x
Nothing -> # Handle error
Edit: Just to clarify, I'd say the important takeaway here is that you have an XY Problem.

Read single line from stdin in Haskell using IO.readLn

This code does not compile in GHC 7.0.3:
import System.IO
main = do
z <- readLn
print z
My intention is to read one line from stdin and store it in z, to do more advanced stuff with it later on. Error message looks like:
test.hs:5:9:
Ambiguous type variable `a0' in the constraints:
(Show a0) arising from a use of `print' at test.hs:5:9-13
(Read a0) arising from a use of `readLn' at test.hs:4:14-19
Probable fix: add a type signature that fixes these type variable(s)
In a stmt of a 'do' expression: print z
In the expression:
do { z <- readLn;
print z;
return () }
In an equation for `main':
main
= do { z <- readLn;
print z;
return () }
Obviously there is something fundamental I haven't understood yet; please explain to me why it doesn't work and how to fix it.
EDIT1: I fixed the compile error by changing print z to putStrLn z, so GHC understands that I want to read a string. But when I run the program, I get a runtime error which I can't understand:
$ ./test
hello!
test: user error (Prelude.readIO: no parse)
$
I just typed "hello!" and then enter. Note that I'm running x86_64 GHC on OS X, which is considered unstable.
EDIT2: I changed readLn to getLine and it magically works for no reason. I would like to know why, but I'm happy it works.
Final code:
import System.IO
main = do
z <- getLine
print z
readLn as the type: Read a => IO a. It reads a line from the user, and then parses the string into type a. What is type a? It is whatever you want (as long as it is an instance of Read). For example:
readAInt :: IO Int
readAInt = readLn
readABool :: IO Bool
readABool = readLn
print has the type Show a => a -> IO (). It takes a type that is an instance of Show, and prints it out. For example, to print True, you can use print True. To print the Int 42, you can use print 42.
In your example, you are using print and readLn together. This doesn't work, as haskell can't figure out what type readLn should return. print can take any type that is showable, so it doesn't restrict to one what type would be returned. This makes the return type of readLn ambiguous as haskell can't figure out the type. This is what the error message is saying.
What you probably what it to store just the string being entered by the user, rather than reading it into your own type. You can do this with getLine, which has the type getLine :: IO String. Similarily you can use putStrLn instead of print to just print a String. putStrLn has the type String -> IO ().
This is what you changed your code to, right?
import System.IO
main = do
z <- readLn
putStrLn z
putStrLn writes a String to stdout, so z is a String. Therefore readLn will read a String from stdin.
BUT... readLn expects to read a Haskell-formatted value from stdin. i.e. instead of expecting you to type something like This is a string, it anticipates it in quote marks: "This is a string".
To fix, replace readLn with getLine, which reads in literal text, not a Haskell-formatted string.
import System.IO
main = do
z <- getLine
putStrLn z
readLn reads back a type that you specify, and so can't be used in this way: it needs to be used in a function that specifies its type. getLine, on the other hand, always returns a String, so it does what you want.
It's worth noting that you might want to use putStrLn instead of print as well; print will add quotation marks.
Thus do { z <- getLine; putStrLn z; } in GHCi should do what you want.

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