Confusion about IO and do notation - haskell

I'm a beginner in Haskell and confused about this code I wrote
readRecords :: String -> [Either String Record]
readRecords path = do
f <- B.readFile path
map parseLogLine (C8.lines f)
But it gives me this error:
Main.hs:15:10:
Couldn't match type `IO' with `[]'
Expected type: [C8.ByteString]
Actual type: IO C8.ByteString
In the return type of a call of `B.readFile'
In a stmt of a 'do' block: f <- B.readFile path
In the expression:
do { f <- B.readFile path;
map parseLogLine (C8.lines f) }
parseLogLine's type signature is parseLogLine :: B8.ByteString -> Either String Record.
I'm completely surprised. B.readFile path should return IO ByteString and so f should be ByteString. C8.lines f should return [ByteString] and map should return [Either String Record].
Where am I wrong?

As a starting point, readRecords is defined with the wrong type. If the do block is to work in the IO monad, then it will produce an IO value, but you've defined it as returning [Either String Record] which is in the [] monad. That means that you can't call B.readFile which returns IO without triggering the type error you got.
Once you fix that, you'll find that the map expression on the last line of the do block has the wrong type because it produces [Either String Record] when it should produce IO [Either String Record]. Wrap the call in return to fix this.

Related

How to use value from different type monad in Haskell

Honestly, I feel like this must have a dupe somewhere, but I couldn't find it even after searching .
Say I have the following code to simply get read a double from the user and echo it back:
import qualified Control.Monad.Except as E
import Text.Read(readMaybe)
data Error = ParseError String
| Default String deriving (Show)
type ThrowsError = Either Error
main = do
putStrLn "Enter your number: "
val <- getDouble
print val
parseString :: String -> ThrowsError Double
parseString val = maybe (E.throwError $ ParseError val) return
(readMaybe val :: Maybe Double)
getDouble :: ThrowsError Double
getDouble = getLine >>= parseString
This breaks in two places:
In main, putStrLn is type IO Double but getDouble is type ThrowsError Double.
In getDouble, getLine is type IO Double but parseString returns IO Double.
Essentially, I'd want to be able to extract the value out of the IO monad, apply computations on it, and put it back inside the appropriate monad. However, the bind function seems to expect the same monad types for input and output, so what I want to do doesn't work.
What's the way around it?
You don't need any transformers. parseString is a pure function, so to apply it to a monadic action you use fmap (aka (<$>)), not (>>=) as you have.
getDouble :: IO (ThrowsError Double)
getDouble = parseString <$> getLine
You would use (>>=) if parseString returned an IO something.

How to use readFile

I am having trouble reading in a level file in Haskell. The goal is to read in a simple txt file with two numbers seperated by a space and then commas. The problem I keep getting is this: Couldn't match type `IO' with `[]'
If I understand correctly the do statement is supposed to pull the String out of the Monad.
readLevelFile :: FilePath -> [FallingRegion]
readLevelFile f = do
fileContent <- readFile f
(map lineToFallingRegion (lines fileContent))
lineToFallingRegion :: String -> FallingRegion
lineToFallingRegion s = map textShapeToFallingShape (splitOn' (==',') s)
textShapeToFallingShape :: String -> FallingShape
textShapeToFallingShape s = FallingShape (read $ head numbers) (read $ head
$ tail numbers)
where numbers = splitOn' (==' ') s
You can't pull things out of IO. You can think of IO as a container (in fact, some interpretations of IO liken it to the box containing Schrödinger's cat). You can't see what's in the container, but if you step into the container, values become visible.
So this should work:
readLevelFile f = do
fileContent <- readFile f
return (map lineToFallingRegion (lines fileContent))
It does not, however, have the type given in the OP. Inside the do block, fileContent is a String value, but the entire block is still inside the IO container.
This means that the return type of the function isn't [FallingRegion], but IO [FallingRegion]. So if you change the type annotation for readLevelFile to
readLevelFile :: FilePath -> IO [FallingRegion]
you should be able to get past the first hurdle.
Let's look at your first function with explicit types:
readLevelFile f = do
(fileContent :: String) <-
(readFile :: String -> IO String) (f :: String) :: IO String
fileContent is indeed of type String but is only available within the execution of the IO Monad under which we are evaluating. Now what?
(map lineToFallingRegion (lines fileContent)) :: [String]
Now you are suddenly using an expression that is not an IO monad but instead is a list value - since lists are also a type of monad the type check tries to unify IO with []. What you actually wanted is to return this value:
return (map lineToFallingRegion (lines fileContent)) :: IO [String]
Now recalling that we can't ever "exit" the IO monad your readLevelFile type must be IO - an honest admission that it interacts with the outside world:
readLevelFile :: FilePath -> IO [FallingRegion]

Read Strings from command line util one can be 'read' as a Float

I'm trying to write code that will prompt the user to enter a Float and will continue to do so until a valid float is entered.
I've tried the following approach:
getFloat :: Float
getFloat = do
input <- getLine
case (readMaybe input :: Maybe Float) of Just f -> f
Nothing -> do getFloat
But I'm getting the following error:
Main.hs:41:5:
Couldn't match type `IO b0' with `Float'
Expected type: IO String -> (String -> IO b0) -> Float
Actual type: IO String -> (String -> IO b0) -> IO b0
In a stmt of a 'do' block: input <- getLine
In the expression:
do { input <- getLine;
case (readMaybe input :: Maybe Float) of {
Just f -> f
Nothing -> do { ... } } }
In an equation for `getFloat':
getFloat
= do { input <- getLine;
case (readMaybe input :: Maybe Float) of {
Just f -> f
Nothing -> ... } }
Main.hs:42:56:
Couldn't match expected type `IO b0' with actual type `Float'
In the expression: f
In a case alternative: Just f -> f
Main.hs:43:60:
Couldn't match expected type `IO b0' with actual type `Float'
In a stmt of a 'do' block: getFloat
In the expression: do { getFloat }
I'm a beginner a would very much appreciate if someone could explain what am I missing here.
For the Just case, use -> return f instead of -> f.
And then just remove the type signature for getFloat. After it compiles, have ghci tell you what the type signature for getFloat is.
Complete code:
getFloat = do
input <- getLine
case (readMaybe input :: Maybe Float) of Just f -> return f
Nothing -> do getFloat
Update
You might be interested in this highly-polymorphic version of the loop:
{-# LANGUAGE NoMonomorphismRestriction #-}
import Text.Read
getItem = do
input <- getLine
case readMaybe input of
Nothing -> getItem
Just x -> return x
I have purposely written getItem without a type signature - this is something that GHC can infer and fill in for you. I've also used the NoMonomorphismRestriction pragma so that getItem remains polymorphic.
The idea is that getItem can be used for any type that can be read - Floats, Doubles, Strings, etc. The type used by readMaybe can be controlled by the caller in various ways. Here are some examples:
main = do
f1 <- getItem
f2 <- getItem
let r = f1 + f2 :: Float
print r
By forcing r to be type Float, f1 and f2 must also be Floats, and therefore getItem will try to parse a Float.
Here is another way to influence the type that readMaybe uses:
main = do
f <- getItem :: IO Float
i <- getItem :: IO Int
print $ f^i -- compute f raised to the i-th power
getFloat :: Float
This states that getFloat is a constant of type Float, which is not what you want.
getFloat :: IO Float
This instead states that getFloat is an IO action producing a Float.
Once this is fixed, then you need to add return in front of your f, as #ErikR already explained. The return turns the pure float value f into an IO action which produces it, without actually performing any IO.
Finally, you do not need the do in the last do getFLoat. The do syntax is useful to sequence IO actions: if you only have one, it is redundant.

Haskell types missmatch

I want to make a function that takes a string "path" this is the path of a file that has only one line, I want to take this line and check it if it's a proper expression and if it is to build a tree out of this string, here is the code
`
loadExpression :: String -> Tree Char
loadExpression path = do
contents <- readFile path
if checkIfProper $ filter (/=' ') contents
then buildTreeFromString contents
else EmptyTree
`
But It gives me error "Couldn't match type IO' withTree' " . I know the IO string is different from the normal one but isn't <- suppost to do just that ? Converting IO string to a normal one. If I call buildTreeFromString with a string like "(1+2)*3" it works fine, same for the checkIfProper.
The whole error is :
Couldn't match type `IO' with `Tree'
Expected type: Tree String
Actual type: IO String
In the return type of a call of `readFile'
In a stmt of a 'do' block: contents <- readFile path
readFile has type FilePath -> IO String, so your do block is in the IO monad. Your entire function therefore returns an IO (Tree Char), not a Tree Char, so you need to change your type signature.
EDIT: You can separate the effectual and pure parts of your function, by creating a function to load the tree from an input string. You can then pass the string from readFile into this function:
readTree :: String -> Tree Char
readTree contents =
if checkIfProper $ filter (/=' ') contents
then buildTreeFromString contents
else EmptyTree
loadExpression then becomes:
loadExpression :: FilePath -> IO (Tree Char)
loadExpression path = do
contents <- readFile path
return (readTree contents)
or you can use fmap:
loadExpression = fmap readTree readFile

Parsec and Applicative style

can someone help me to understand how to use Applicative style for writing Parsec parsers? This is the code i have:
module Main where
import Control.Applicative hiding (many)
import Text.Parsec
import Data.Functor.Identity
data Cmd = A | B deriving (Show)
main = do
line <- getContents
putStrLn . show $ parseCmd line
parseCmd :: String -> Either ParseError String
parseCmd input = parse cmdParse "(parser)" input
cmdParse :: Parsec String () String
cmdParse = do
slash <- char '/'
whatever <- many alphaNum
return (slash:whatever)
cmdParse2 :: String -> Parsec String () String
cmdParse2 = (:) <$> (char '/') <*> many alphaNum
but when i try to compile it, i get following:
/home/tomasherman/Desktop/funinthesun.hs:21:13:
Couldn't match expected type `Parsec String () String'
with actual type `[a0]'
Expected type: a0 -> [a0] -> Parsec String () String
Actual type: a0 -> [a0] -> [a0]
In the first argument of `(<$>)', namely `(:)'
In the first argument of `(<*>)', namely `(:) <$> (char '/')'
Failed, modules loaded: none.
The idea is that i want cmdParse2 to do same thing that cmdParse does, but using applicative stuff...my approach is probably completely wrong, i'm new to haskell
Your applicative usage is spot on, you just have an incorrect signature. Try:
cmdParse2 :: Parsec String () String
Your approach looks correct to me, the problem is that cmdParse2 has the wrong type. It should have the same type as cmdParse. By the way, you can omit the parens around char '/' in the applicative style parser.

Resources