How to properly use the readMaybe function in IO - haskell

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

Related

Use of putStrLn to show result

I am using the Idone.com site and wanted to run this code but do not know the syntax putStrLn to compile from stdin Use this code but strip error.
main = putStrLn (show (sumaCifras x))
sumaCifras:: Int -> Int
sumaCifras x = div x 1000 + mod (div x 100) 10 + mod (div x 10) 10 + mod x 10
Compiler is having a problem, because you use x in main function, which isn't bound in this scope. At first you must read a value from input and then pass it to your function. You can do it in 2 ways.
More natural for people used to imperative languages is "do" syntax, in which it will look like that:
main = do
x <- getLine
putStrLn (show (sumaCifras (read x :: Int)))
When you want to use x as Int, you must use "read" function with type signature, so compiler will know what to expect.
To write it in more functional way, you may use monad transformations, to pass it like that
main = getLine >>= (\x -> putStrLn(show (sumaCifras (read x :: Int)))
The ">>=" operator will get result value from first monadic action (in here it is IO action of getting input) and apply it to function on the right (in here it is lambda function that reads input as Integer, applies your function and returns it to putStrLn, which prints it on the screen). "do" syntax is essentially just a syntactic sugar for this monadic operations, so it will not affect the execution or performance of program.
You can go one step further in writing it functionally by writing it totally point-free
main = getLine >>= (putStrLn . show . sumaCifras . (read :: String -> Int))
Note that here you have type signature for read function, not for application of this function to argument, hence the String -> Int. In here first executed is the getLine function. Input obtained from it is then passed to the read, where it is casted to Int, next is sumaCifras, which then is casted to String by show and printed with putStrLn.

Write list of random numbers to file. No Instance for (Show (IO a0))

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

Extract records from a file - Haskell

///Edit
I have a problem with haskell. If someone can help me, that would be great.
I'm inserting records into a file using the following code:
check :: Int -> Int
check a
|a > 0 && a<=10 = a
|otherwise = error "oh. hmm.. enter a number from the given interval"
ame :: IO ()
ame = do
putStr "Enter the file name: "
name <- getLine
putStrLn "Do you want to add new records? "
question <- getLine
if question == "yes" then do
putStrLn "Enter your records:"
newRec <- getLine
appendFile name ('\n':newRec)
--new lines--
putStrLn "enter a number between 0 and 10: "
something <- getLine
return (read something:: Int)
let response = check something
putStrLn response
appendFile name ('\n':something)
putStrLn "enter something new again: "
something2 <- getLine
appendFile name ('\n':something2)
putStrLn "a"
else
putStr "b"
Now I want to extract some records from this file, using a specific criteria. For example, i want to extract and display records from even(or odd or any other criteria) rows. can I do that? if yes, how?
Also...
I want to check the user input. Let's say that I don't want him/her to enter a string instead of an integer. can I also check his/her input? do I need to create another function and call that function inside the code from above?
///Edit
thank you for answering. but how can i embed it into my previous code?
I've tried now to create a function(you can see the above code) and then call that function in my IO. but it doesn't work..
Yes, it is certainly possible to display only certain rows. If you want to base it off of the row number, the easiest way is to use zip and filter
type Record = String
onlyEven :: [Record] -> [Record]
onlyEven records =
map snd $ -- Drop the numbers and return the remaining records
filter (even . fst) $ -- Filter by where the number is even
zip [1..] -- All numbers
records -- Your records
This technique can be used in a lot of circumstances, you could even abstract it a bit to
filterByIdx :: Integral i => (i -> Bool) -> [a] -> [a]
filterByIdx condition xs = map snd $ filter (condition . fst) $ zip [1..] xs
-- Could also use 0-based of `zip [0..] xs`, up to you
onlyEven :: [a] -> [a]
onlyEven = filterByIdx even
If you want to check if an input is an Int, the easiest way is to use the Text.Read.readMaybe function:
import Text.Read (readMaybe)
promptUntilInt :: IO Int
promptUntilInt = do
putStr "Enter an integer: "
response <- getLine
case readMaybe response of
Just x -> return x
Nothing -> do
putStrLn "That wasn't an integer!"
promptUntilInt
This should give you an idea of how to use the function. Note that in some cases you'll have to specify the type signature manually as case (readMaybe response :: Maybe Int) of ..., but here it'll work fine because it can deduce the Int from promptUntilInt's type signature. If you get an error about how it couldn't figure out which instance for Read a to use, you need to manually specify the type.
You have
something <- getLine
return (read something:: Int)
let response = check something
putStrLn response
To step through what you're trying to do with these lines:
something <- getLine
getLine has the type IO String, meaning it performs an IO action and returns a String. You can extract that value in do notation as
something <- getLine
Just as you have above. Now something is a String that has whatever value was entered on that line. Next,
return (read something :: Int)
converts something to an Int, and then passes it to the function return. Remember, return is not special in Haskell, it's just a function that wraps a pure value in a monad. return 1 :: Maybe Int === Just 1, for example, or return 1 :: [Int] === [1]. It has contextual meaning, but it is no different from the function putStrLn. So that line just converts something to an Int, wraps it in the IO monad, then continues on to the next line without doing anything else:
let response = check something
This won't compile because check has the type Int -> Int, not String -> String. It doesn't make any sense to say "hello, world" > 0 && "hello, world" <= 10, how do you compare a String and an Int? Instead, you want to do
let response = check (read something)
But again, this is unsafe. Throwing an error on an invalid read or when read something is greater than 10 will crash your program completely, Haskell does errors differently than most languages. It's better to do something like
check :: Int -> Bool
check a = a > 0 && a <= 10
...
something <- getLine
case readMaybe something of
Nothing -> putStrLn "You didn't enter a number!"
Just a -> do
if check a
then putStrLn "You entered a valid number!"
else putStrLn "You didn't enter a valid number!"
putStrLn "This line executes next"
While this code is a bit more complex, it's also safe, it won't ever crash and it handles each case explicitly and appropriately. By the way, the use of error is usually considered bad, there are limited capabilities for Haskell to catch errors thrown by this function, but errors can be represented by data structures like Maybe and Either, which give us pure alternatives to unsafe and unpredictable exceptions.
Finally,
putStrLn response
If it was able to compile, then response would have the type Int, since that's what check returns. Then this line would have a type error because putStrLn, as the name might suggest, puts a string with a new line, it does not print Int values. For that, you can use print, which is defined as print x = putStrLn $ show x
Since this is somewhat more complex, I would make a smaller function to handle it and looping until a valid value is given, something like
prompt :: Read a => String -> String -> IO a
prompt msg failMsg = do
putStr msg
input <- getLine
case readMaybe input of
Nothing -> do
putStrLn failMsg
prompt
Just val -> return val
Then you can use it as
main = do
-- other stuff here
-- ...
-- ...
(anInt :: Int) <- prompt "Enter an integer: " "That wasn't an integer!"
-- use `anInt` now
if check anInt
then putStrLn $ "Your number multiplied by 10 is " ++ show (anInt * 10)
else putStrLn "The number must be between 1 and 10 inclusive"
You don't have to make it so generic, though. You could easily just hard code the messages and the return type like I did before with promptUntilInt.

Haskell Read - no parse error

I have a type called PartialDate
Then I have a function
readPartialDate :: String -> Maybe PartialDate
Bit of test code
main = do
[d] <- getArgs
return $ show $ readPartialDate d
runhaskell PartialDate.hs "12-2-2010"
"Just 12-2-2010"
All OK
Then I create a read simply by dispatching on readPartialDate:
instance Read PartialDate where
readsPrec _ s = case (readPartialDate s) of
Nothing -> []
Just p -> [(p, s)]
Test code:
main = do
[d] <- getArgs
return $ show $ ((read d) :: PartialDate)
runHaskell PartialDate.hs 12-2-2010
PartialDate.hs: Prelude.read: no parse
Does anyone know why putting a working function into a read might give rise to a parse error?
readPartialDate uses Parsec, and also uses reverse, so might there be a laziness issue here?
The problem is that in the definition of readsPrec,
readsPrec _ s = case (readPartialDate s) of
Nothing -> []
Just p -> [(p, s)]
you give the input String itself as the second component of the readsPrec result pair. read requires that the reads result have the second component empty, meaning that the entire input has been consumed to determine the value (in general, when you write Read instances, make sure you don't forget to consume trailing whitespace). Change the instance to
readsPrec _ s = case (readPartialDate s) of
Nothing -> []
Just p -> [(p, "")]
and it should work.

Weird return in Haskell

checkstring :: [String] -> Int -> [String]
checkstring p n = do z <- doesFileExist (p !! n)
if z
then p
else error $ "'" ++ (p !! n) ++ "' file path does not exist"
It checks for a element in the string by looking at "n"(so if n = 2 it will check if the second string in the list) then see if it exists. If it does exist it will return the original string list, if not it will error.Why does it do this? :
Couldn't match expected type `[t0]' with actual type `IO Bool'
In the return type of a call of `doesFileExist'
In a stmt of a 'do' expression: z <- doesFileExist (p !! n)
The type of doesFileExist is String -> IO Bool. If your program wants to know whether a file exists, it has to interact with the file system, which is an IO action. If you want your checkString function to do that, it will also have to have some kind of IO-based type. For example, I think something like this would work, though I haven't tried it:
checkstring :: [String] -> Int -> IO [String]
checkstring p n = do z <- doesFileExist (p !! n)
if z
then return p
else error $ "'" ++ (p !! n) ++ "' file path does not exist"
To add to what MatrixFrog has mentioned in his answer. If you look at your function signature i.e [String] -> Int -> [String] it indicates that this function is a pure function and doesn't involved any side effects, where as in your function body you are using doesFileExist which has a signature of String -> IO Bool where the presence of IO indicates it is a impure function i.e it involves some IO. In haskell there is a strict separation between impure and pure functions and as a matter of fact if your function calls some other function which is impure than your function is also impure. So in your case your function checkString needs to be impure and that can be done by making it return IO [String], which is what MatrixFrog has mentioned in his answer.
On another note, I would suggest that you can make the function to be something like:
checkString :: String -> IO (Maybe String) ,as your function doesn't need the whole list of string as it just need a specific string from the list to do its work and rather than throwing an error you can use Maybe to detect the error.
This is just a suggestion but it also depends on how your function is being used.
I think the problem is that your type signature forces the do block to assume that it is some other monad. For example, suppose you're working in the list monad. Then, you could write
myFcn :: [String] -> Int -> [String]
myFcn p n = do
return (p !! n)
In the case of the list monad, the return simply returns the singleton list, so you get behavior like,
> myFcn ["a", "bc", "d"] 1
["bc"]
(My personal opinion is that it would be very helpful if the GHC had an option to print out common mistakes that could cause a type error; I sympathize with the asker in that I've gotten a lot of type error messages that take time to figure out).

Resources