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)
Related
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.
I am trying to write to file a list of random Integers in a file. There seems to be a problem with writeFile here. When I use my function randomFile it says no instance for (Show (IO a0)). I see writeFile doesn't print anything to screen but IO(), so when I call the function randomFile 1 2 3 it says no Instance for Show (IO a0) but actually I just want to execute the function and not have to print anything but how can I avoid this problem. I might be making a lot of errors here. Any help.
import Control.Monad
import Control.Applicative
import System.Random
randNo mind maxd = randomRIO (mind,maxd)
randomFile mind maxd noe = do
let l=(replicate (fromInteger(noe ^ noe)) ( mind `randNo` maxd))
writeFile "RFile.txt" (show l)
I think you have a misunderstanding of what IO is. If you haven't done it, I strongly recommend going through the Input and Output section of Learn You a Haskell.
IO doesn't necessarily have anything to do with print. In Haskell every entry in memory that was made by your own code is considered "pure" while any entry that touches the rest of the computer lives in IO (with some exceptions you will learn about over time).
We model IO using something called a Monad. Which you will learn more about the longer you do Haskell. To understand this, let's look at an example of some code that does and doesn't use IO:
noIOused :: Int -> Int
noIOused x = x + 5
usesIO :: Int -> IO Int
usesIO x = print x >> return (x + 5)
usesIO2 :: Int -> IO Int
usesIO2 x = do
print x
return (x + 5)
The first function is "pure". The second and third functions have an IO "effect" that comes in the form of printing to the screen. usesIO and usesIO2 are just 2 different ways of doing the same thing (it's the same code but with different syntax). I'll use the second format, called do notation from here.
Here are some other ways you could have had IO effects:
add5WithFile :: Int -> IO Int
add5WithFile x = do
writeFile "someFile.txt" (show x)
return (x + 5)
Notice that in that function we didn't print anything, we wrote a file. But writing a file has a side effect and interacts with the rest of the system. So any value we return has to get wrapped in IO.
addRandom :: Int -> IO Int
addRandom x = do
y <- randomRIO (1,10)
return (x + y)
In addRandom we called randomRIO (1,10). But the problem is that randomRIO doesn't return an Int. It returns an IO Int. Why? Because in order to get true randomness we need to interact with the system in some way. To get around that, we have to temporarily strip away the IO. That's where this line comes in:
y <- randomRIO (1,10)
That <- arrow tells us that we want a y value outside of IO. For as long as we remain inside the do syntax that y value is going to be "pure". Now we can use it just like any other value.
So for example we couldn't do this:
let w = x + (randomRIO (1,10))
Because that would be trying to add Int to IO Int. And unfortunately our + function doesn't know how to do that. So first we have to "bind" the result of randomRIO to y before we can add it to x.
Now let's look at your code:
let l=(replicate (fromInteger(noe ^ noe)) ( mind `randNo` maxd))
writeFile "RFile.txt" (show l)
The type of l is actually IO a0. It's a0 because you haven't told the compiler what kind of number you want. So it doesn't know if you want a fraction, a double, a big integer or whatever.
So the first problem is to let the compiler know a little bit more about what kind of random number you want. We do this by adding a type annotation:
randNo :: Int -> Int -> IO Int
randNo mind maxd = randomRIO (mind,maxd)
Now both you and the compiler knows what kind of value randNo is.
Now we need to "bind" that value inside of the do notation to temporarily escape IO. You might think that would be simple, like this:
randomFile mind maxd noe = do
l <- replicate (fromInteger(noe ^ noe)) ( mind `randNo` maxd)
writeFile "RFile.txt" (show l)
Surely that will "bind" the IO Int to l right? Unfortunately not. The problem here is that replicate is a function of the form Int -> a -> [a]. That is, given a number and a type, it will give you a list of that type.
If you give replicate an IO Int it's going to make [IO Int]. That actually looks more like this: List (IO Int) except we use [] as syntactic sugar for lists. Unfortunately if we want to "bind" an IO value to something with <- it has to be the out-most type.
So what you need is a way to turn an [IO Int] into an IO [Int]. There are two ways to do that. If we put \[IO a\] -> IO \[a\] into Hoogle we get this:
sequence :: Monad m => [m a] -> m [a]
As I mentioned before, we generalise IO to something called a Monad. Which isn't really that big a deal, we could pretend that sequence has this signature: sequence :: [IO a] -> IO [a] and it would be the same thing just specialised to IO.
Now your function would be done like this:
randomFile mind maxd noe = do
l <- sequence (replicate (fromInteger(noe ^ noe)) ( mind `randNo` maxd))
writeFile "RFile.txt" (show l)
But a sequence followed by replicate is something people have to do all the time. So someone went and made a function called replicateM:
replicateM :: Monad m => Int -> m a -> m [a]
Now we can write your function like this:
randomFile mind maxd noe = do
l <- replicateM (fromInteger(noe ^ noe)) ( mind `randNo` maxd)
writeFile "RFile.txt" (show l)
And for some real Haskell magic, you can write all 3 lines of code in a single line, like this:
randomFile mind maxd noe = randomRIO >>= writeFile "RFile.txt" . replicateM (fromInteger(noe ^ noe))
If that looks like gibberish to you, then there's a lot you need to learn. Here is the suggested path:
If you haven't already, start from the beginning with Learn You a Haskell
Then learn about how You could have invented Monads
Then learn more about how to use randomness in Haskell
Finally see if you can complete the 20 intermediate Haskell exercises
I started with programming in Haskell about 4 month ago and now I came to the point where I have to deal with the IO system of Haskell.
I already did a lot of IO actions and haven't faced any problems I couldn't solve by myself, but this time I googled for almost two hours for no avail to get some information about the function readMaybe. So I have the following problem set to solve and I already tried a lot of different approaches to solve it but all the time I get the same failure message from my compiler:
No instance for (Read a0) arising from a use of `readMaybe'
The type variable `a0' is ambiguous
I understand what the compiler does want to tell me but I have no idea how to solve this problem. I already tried to add a class constraint, but without success.
So here is my very small and simple program that is just counting how many valid numbers the user has entered. The program is meant to terminate when the user enters an empty line.
This is just a auxiliary function I want to use for my project later on.
countNumbers :: IO Int
countNumbers = do
x <- count 0
return x where
count :: Int -> IO Int
count n = do
line <- getLine
case line of
"" -> do
return n
_ -> case readMaybe line of
Just _ -> do
x <- count (n+1)
return x
Nothing -> do
x <- count n
return x
Unfortunately I couldn't find out a lot of informations about the function readMaybe. The only thing I could find was in the Haskell library Text.Read:
readMaybe :: Read a => String -> Maybe aSource
Parse a string using the Read instance. Succeeds if there is exactly one valid result.
The very weird thing for me is that I have already written such a function that uses the readMaybe function and it worked perfectly ...
This program is just asking the user for a number and keeps asking as long as the user enters a valid number
getLineInt :: IO Int
getLineInt = do
putStrLn "Please enter your guess"
line <- getLine
case readMaybe line of
Just x -> do
return x
Nothing -> do
putStrLn "Invalid number entered"
x <- getLineInt
return x
So far as I can see there are no differences between the usage of the function readMaybe in the two programs and therefore it works in the one but not in the other :)
I would be really thankful for any hints from you!!
This has nothing to do with IO, so maybe you don't understand what the compiler is trying to tell you. There is a type variable a in readMaybe's signature; a has to have a Read instance, but other than that it can be anything. The compiler is telling you that it doesn't have any way to determine what you want a to be.
In getLineInt you don't have this problem, because you are returning the result of readMaybe and the type signature says it should be Int. In countNumbers, you're not using the result of readMaybe, so there's nothing that can be used to determine the correct type. You can fix this by adding an explicit type signature (I picked Int since you're apparently counting numbers):
_ -> case readMaybe line :: Maybe Int of
Finally a word about do notation: it's just syntactic sugar, you don't have to use it all the time. Instead of do return x you can simply write return x, and instead of
x <- getLineInt
return x
you can simply do
getLineInt
That makes things more readable:
getLineInt :: IO Int
getLineInt = do
putStrLn "Please enter your guess"
line <- getLine
case readMaybe line of
Just x -> return x
Nothing -> putStrLn "Invalid number entered" >> getLineInt
Why does this happen?
In your second function, it is clear that readMaybe line is used as String -> Maybe Int, since type inference notices that you use return x and therefore x must be an Int.
In your first function, you don't use the Maybe's value at all, you just want to check whether the read succeeded. However, since you didn't specify the type (neither explicit nor implicit with type inference), the type variable is ambiguous:
_ -> case readMaybe line of
There's an easy fix: annotate the type:
_ -> case readMaybe line :: Maybe Int of
By the way, this is exactly the same behaviour you encounter when you use read in ghci without any type context:
> read "1234"
<interactive>:10:1:
No instance for (Read a0) arising from a use of `read'
The type variable `a0' is ambiguous
As soon as you make the type clear everything is fine:
> read "1234" :: Int
1234
Making things clear
Now that we've seen why the error happens, lets make this program much simpler. First of all, we're going to use a custom readMaybe:
readMaybeInt :: String -> Maybe Int
readMaybeInt = readMaybe
Now how does one count numbers? Numbers are those words, where readMaybeInt doesn't return Nothing:
countNumbers :: String -> Int
countNumbers = length . filter isJust . map readMaybeInt . words
How does one now calculate the numbers in the standard input? We simply take input until one line is completely empty, map countNumbers on all those lines and then sum:
lineNumberCount :: IO Int
lineNumberCount =
getContents >>= return . sum . map countNumbers . takeWhile (/= "") . lines
If you're not used to the bind methods, that's basically
lineNumberCount :: IO Int
lineNumberCount = do
input <- getContents
return . sum . map countNumbers . takeWhile (/= "") . lines $ input
All in all we get the following terse solution:
import Control.Monad (liftM)
import Data.Maybe (isJust)
import Text.Read (readMaybe)
readMaybeInt :: String -> Maybe Int
readMaybeInt = readMaybe
countNumbers :: String -> Int
countNumbers = length . filter isJust . map readMaybeInt . words
lineNumberCount :: IO Int
lineNumberCount =
getContents >>= return . sum . map countNumbers . takeWhile (/= "") . lines
Now there's only one function working in the IO monad, and all functions are basically applications of standard functions. Note that getContents will close the handle to the standard input. If you want to use you're better of using something like
input :: String -> IO [String]
input delim = do
ln <- getLine
if ln == delim then return []
else input delim >>= return . (ln:)
which will extract lines until a line matching delim has been found. Note that you need to change lineNumberCount in this case:
lineNumberCount :: IO Int
lineNumberCount =
input "" >>= return . sum . map countNumbers
I want to write a function that read some data using getLine and return i.e. a tuple (Integer, Integer) but using do-notation. Something like this (of course it doesn't work):
fun :: (Integer, Integer)
fun = do
a <- read (getLine::Integer)
b <- read (getLine::Integer)
return (a, b)
Do I have to write my own monad for this? Is there any solution to not writing a new monad?
EDIT
So I can write main function that use fun, I think it's the only solution:
main :: IO ()
main = do
tuple <- fun
putStrLn (show tuple)
fun :: IO (Integer, Integer)
fun = do
a1 <- getLine
b1 <- getLine
let a = read (a1)
b = read (b1)
return (a, b)
And above code works.
You type of function should be
fun :: IO (Integer, Integer)
as mentioned by #kaan you should not try to get a mondic value (with side effects) out of the monad as that will break referential transparency. Running fun should always return same value no matter how many times it is run and if we use your type this will not happen. However if the type is IO (Integer, Integer) then it returns the same action every time you use that function and running this action actually perform the side effect of reading the values from the console.
Coming back to using you function. You can do that inside another IO monad like
main = do
(a,b) <- fun
print a
print b
Although there are ways of getting things out of IO using unsafe functions but that is not recommended until you know exactly what you are doing.
As mentioned, you will need to give fun the type IO (Integer, Integer) instead of (Integer, Integer). However, once you have resigned yourself to this fate, there are many ways to skin this cat. Here are a handful of ways to get your imagination going.
fun = do
a <- getLine
b <- getLine
return (read a, read b)
-- import Control.Applicative for (<$>)
-- can also spell (<$>) as fmap, liftA, liftM, and others
fun = do
a <- read <$> getLine
b <- read <$> getLine
return (a, b)
fun = do
a <- readLn
b <- readLn
return (a, b)
fun = liftM2 (,) readLn readLn
-- different type!
-- use in main like this:
-- main = do
-- [a, b] <- fun
-- foo
-- import Control.Monad for replicateM
fun :: IO [Integer]
fun = replicateM 2 readLn
I have a problem, user inputs number m (meaning matrix has m-rows, m-columns), then the matrix is read from stdin. I want to transpose the matrix and then output the matrix back on the screen in the same format.
for example:
Main> main
3
1 2 3
4 5 6
7 8 9
1 4 7
2 5 8
3 6 9
Anyway, I got somewhere in the middle, but i dont know how to parse the [[Int]] back to IO, so it can be print on the screen.
here is my code:
import Control.Monad (replicateM)
transpose :: [[a]]->[[a]]
transpose ([]:_) = []
transpose x = (map head x) : transpose (map tail x)
readMany :: Read a => IO [a]
readMany = fmap (map read . words) getLine
parse :: IO ([[Int]])
parse = do
[m] <- readMany
xss <- replicateM m readMany
let matrix = transpose xss
return (matrix)
main :: IO ()
main = do
parse
?? --
print ??
First, you have to extract the value you parse:
main :: IO ()
main = do
matrix <- parse
...
print ???
Now, what's the type of print?
print :: (Show a) => a -> IO ()
Well, [[Int]] is an instance of Show, so we can treat print as a specialisation of its type:
print :: [[Int]] -> IO ()
So, in fact, it's as simple as filling in the list we want to print in place of the ???:
main :: IO ()
main = do
matrix <- parse
print matrix
However, this prints the data out in the same way that GHCi does. If you want to print the data out in a custom format, you want:
putStr :: String -> IO ()
Let's say you write a function to format the matrix the way you want into a string:
formatMatrix :: [[Int]] -> String
This makes the complete program:
main :: IO ()
main = do
matrix <- parse
putStr (formatMatrix matrix)
The difference between foo <- bar and let foo = bar is that the former extracts the result of an IO computation, while the latter just gives a name to a value.
So basically, the answer is that you don't need to put anything into IO to do this, since all the functions that create IO actions to do things like print strings out take pure values. This doesn't make them limited to printing the results of completely pure computations, however, since you can extract values from previous IO computations, and then feed them in to the printing functions as pure values. (In fact, this is the essence of monads, and why Haskell uses them to model IO in the first place!)
It is entirely possible to do exactly what you ask for, but sometimes it is just a little bit easier to make the string and then print it. In this case it is in fact very easy. All you need to know are the two functions words and lines, and their inverse unwords and unlines.
Here is a possible solution to your problem (note that I don't check that the inputs are integers and that I use the transpose from Data.List)
import Data.List
import Control.Monad
main = do
n <- liftM read getLine
xs <- replicateM n getLine
putStrLn (unlines . map unwords . transpose . map words $ xs)
since replicateM n getLine gives us a list of lines, what lines would have done to the entire input, we are just sandwiching transpose in between map words . lines and its inverse.