Binding in expression - haskell

I am trying to on wheatear argument with file name is passed to either parse the file or read from standard input:
let formulae = fmap parseInput $ if isInputFile args then (hGetContents $ openFile (last args) ReadMode) else hGetContents stdin
but of course this error occurs:
formula-2-bdd.hs:89:79:
Couldn't match expected type `Handle' with actual type `IO Handle'
In the second argument of `($)', namely
`openFile (last args) ReadMode'
In the expression: (hGetContents $ openFile (last args) ReadMode)
If I bind IO Handle first I can't decided according to `isInputFile args:
handle <- openFile (last args) ReadMode
formulae = fmap parseInput $ if isInputFile args then hGetContents handle else hGetContents stdin

Let's understand what the actual problem with the original let expression is.
let formulae = fmap parseInput $ if isInputFile args
then (hGetContents $ openFile (last args) ReadMode)
else hGetContents stdin
The problem is that stdin is a Handle, while the return value of openFile is an IO Handle. To handle (no pun intended) both cases with the same code, you need to promote stdin to an IO Handle.
let formulae = fmap parseInput $ if isInputFile args
then (hGetContents $ openFile (last args) ReadMode)
else (hGetContents (pure stdin))
This can be rewritten a little more simply calling hGetContents on the result of the if expression, rather than having the if expression return the result of hGetContents.
let formulae = do handle <- if isInputFile
then openFile (last args) ReadMode
else return stdin
data <- hGetContents handle
fmap parseInput data

Related

Strange monadic behaviour

I tried to write a program which takes filepaths as (command line) arguments and returns the first line of each file
main = getArgs >>= ( mapM_ ( \file -> ( openFile file ReadMode >>= ( (\handle -> hGetLine handle >>= print) >> hClose ) ) ) )
I know that this doesn't look very beautiful but I am just a beginner in Haskell. I did also avoid the do notation on purpose because I just don't feel very comfortable with her (yet).
So the Code above compiles and returns an error for invalid file paths, and nothing (i.e. especially not the first line of a file) for valid paths.
I must confess that I have pretty much no idea what I did wrong, but I made the following observation:
If I add the following to check which parts still get executed
main = getArgs >>= ( mapM_ ( \file -> ( openFile file ReadMode >>= ( (\handle -> hGetLine handle >>= print) >> (const $ putStr "Hello1") >> hClose >> (const $ putStr "Hello2") ) ) ) )
the program prints only the second "Hello", this reminded me of the type signature of (>>):
(>>) :: Monad m => m a -> m b -> m b
so taking into perspective that only something of the type of the second argument gets returned, maybe the first argument is just ignored?
But the first argument against this theory is that such a function would not seem to be very useful (at least not in the context of the IO Monad), and the second is that the program
main = (putStr "Hello" >> putStr "World" >> putStr "!")
returns 'HelloWorld!' as expected. Hence I must be completely on the wrong track, which is why I came here.
Thanks for your help!
I think you main error is that you messed up with the handle:
main = getArgs >>= (mapM_ (\file -> (openFile file ReadMode >>= (\handle -> (hGetLine handle >>= print) >> hClose handle) ) ) )
this way you did it >> was for the (-> handle) Monad (it's a reader monad - see there is an Monad instance for (->) c for constant c) not the IO!
So it did indeed pass the handle to both hGetLine handle >>= print and hClose but >> ignored the first resulting IO action and returned the hClose one as the result to >>
Here the effect was passing the handle!
So yes in the end the only executed IO-effect was closing the file!
It's subtle and not obvious as you seldom see/think about the reader-monad instance like this.
here is this with do notation
main = do
args <- getArgs
mapM_ (\file -> do
handle <- openFile file ReadMode
line <- hGetLine handle
print line
hClose handle) args
and I'd suggest switching to forM_ (from Control.Monad) for the args parameter:
main = do
args <- getArgs
forM_ args (\file -> do
handle <- openFile file ReadMode
line <- hGetLine handle
print line
hClose handle)
now you should make sure you close the handle - you can use bracket from Control.Exception for this:
main = do
args <- getArgs
forM_ args (\file -> do
bracket
(openFile file ReadMode)
hClose
(\h -> do
line <- hGetLine h
print line
)
)
or (as this is very common) just withFile from System.IO which does the opening/closing for you:
main = do
args <- getArgs
forM_ args (\file -> do
withFile file ReadMode
(\h -> do
line <- hGetLine h
print line
)
)
finally you don't really have to use all the handle stuff you can use the (lazy) readFile instead and be a bit safer with empty files too:
main = do
args <- getArgs
forM_ args (\file -> do
content <- readFile file
let ls = lines content
case ls of
[] -> putStrLn "no line in file"
(firstLine:_) -> putStrLn firstLine
)

Haskell Handle doesn't close properly

import GHC.Conc (pseq)
import System.Directory (removeFile, renameFile)
import System.IO
main :: IO ()
main = do
let fp = "handleCollision.txt"
h <- openFile fp ReadMode
l <- hGetLine h
(hClose h) `pseq` (func fp)
func :: FilePath -> IO ()
func fp = do
h <- openFile fp ReadMode
cont <- hGetContents h
chk <- openFile ('.':fp) WriteMode >>= (\h -> hPutStrLn h (unlines . map (map succ) $ lines cont) >> hClose h)
chk `pseq` (hClose h `pseq` removeFile fp)
renameFile ('.':fp) fp
In the code above, the result of calling main is an error in the attempt to remove the file referred to by fp.
*** Exception: DeleteFile "handleCollision.txt": permission denied (The process cannot access the file because it is being used by another process.)
If I only call 'func "handleCollision.txt"', then no error occurs. Why does the Handle in main seem to collide with the deletion of fp in func despite the fact that 'hClose h' is treated as strict and 'func fp' is treated as lazy?
You should be using Control.Parallel.pseq instead of GHC.Conc.pseq for portability. However, it does not do what you think it does.
pseq only forces evaluation to WHNF. For the expression (hClose h) :: IO (), the WNHF is exactly that - the representation of an IO action. It does not perform the action.
Just take out the pseq. Better yet, use methods like withFile to avoid manually tracking file open and close.
main = do
let fp = "handleCollision.txt"
l <- withFile fp ReadMode hGetLine
func fp
func fp = do
cont <- readFile fp
writeFile ('.':fp) $ unlines $ map (map succ) $ lines cont
renameFile ('.':fp) fp

How to use a value produced in another do block?

I just watched a video on Haskell so I tried to play a little bit with it but I can't get to understand this (In short I want to print one random value):
import System.Random
import System.IO
randomNum = do
gen <- newStdGen
let ns = randoms gen :: [Int]
let val = take 10 ns
print $ head val
writeToFile = do
theFile <- openFile "test.txt" WriteMode
let val = randomNum;
hPutStrLn theFile ("Random number " ++ randomNum)
hClose theFile
readFromFile = do
theFile2 <- openFile "test.txt" ReadMode
contents <- hGetContents theFile2
putStr contents
hClose theFile2
The randomNum seems to work fine but when I try to put that on writeToFile it triggers an error. What can I do?
Thanks in advance!
EDIT: The error I get in the beginning is:
Prelude> :r
[1 of 1] Compiling Main ( haskell.hs, interpreted )
haskell.hs:207:48:
No instance for (Show (IO ())) arising from a use of `show'
In the second argument of `(++)', namely `show randomNum'
In the second argument of `hPutStrLn', namely
`("Random number " ++ show randomNum)'
In a stmt of a 'do' block:
hPutStrLn theFile ("Random number " ++ show randomNum)
Failed, modules loaded: none.
It looks like what you need is
randomNum = do
gen <- newStdGen
return (head (randoms gen :: [Int]))
writeToFile = do
theFile <- openFile "test.txt" WriteMode
val <- randomNum
hPutStrLn theFile ("Random number " ++ show val)
hClose theFile
You could try this instead:
import System.Random
import System.IO
writeToFile = do
gen <- newStdGen
let ns = randoms gen :: [Int]
let val = head ns;
theFile <- openFile "test.txt" WriteMode
hPutStrLn theFile ("Random number " ++ show val)
hClose theFile
readFromFile = do
theFile2 <- openFile "test.txt" ReadMode
contents <- hGetContents theFile2
putStr contents
hClose theFile2
One problem was that the do block in your randomNum did not return a value; rather, it performed the action you told it to do: print a random number. As an alternative, see Louis Wasserman's answer for a way to make randomNum actually return a value. In this answer's code, the random number generation was just moved into writeToFile.
Also notice that I shortened the code to get one random value: ns is already a list, so you can take its head right away. The take 10 was redundant.
Finally, val was an Int, which cannot be concatenated directly onto a string. Using show val converts it to a string which can be concatenated with "Random number "

Why I can't use the (cnt <- hGetContents h) expression instead of cnt?

I learn Haskell. It works fine:
import System.IO
main = do
h <- openFile "text.txt" ReadMode
cnt <- hGetContents h
mapM_ putStrLn $ lines cnt
hClose h
But this isn't working:
import System.IO
main = do
h <- openFile "text.txt" ReadMode
mapM_ putStrLn $ lines (cnt <- hGetContents h)
hClose h
Why my second variant isn't working? I expected both variants are equal, because the (cnt <- hGetContents h) is an expression and returns the value too.
The problem is that cnt <- hGetContents h is not an expression, it's some special syntax sugar inside do notation. This means it is a different way of writing the following normal Haskell code:
hGetContents h >>= \ cnt -> {- rest of do block -}
The part before the {- rest of the do block -} is not a whole expression here, because the rest of the do block is needed to complete the lambda's body.
You could desugar it manually to get something like:
hGetContents h >>= \ cnt -> mapM_ putStrLn (lines cnt)
or the point-free version
hGetContents h >>= mapM_ putStrLn . lines
You can tell that it's a special expression because it introduces a new identifier (cnt) that you can use in the rest of your code, outside of the expression itself. This is not something that normal Haskell expressions get to do (at least without compile-time magic).
cnt <- hGetContents h is essentially syntactical sugar for hGetContents h >>= \cnt ->.
It is not an expression, it is sugar intended for its own line in a do-block.
If you still want to keep it on one line, you can do this, though you will not be able to refer to the file's contents later on:
import System.IO
main = do
h <- openFile "text.txt" ReadMode
hGetContents h >>= mapM_ putStrLn . lines
hClose h

Haskell, can i call function without IO output working with monads?

Why i can't do this?
Its forbidden the use of 'do' in this question :/
How i can call words in my list and at same time result an IO?
Thanks.. this is my actual code :/
main :: IO()
main =
putStr "Name of File: " >>
getLine >>=
\st ->
openFile st ReadMode >>=
\handle ->
hGetContents handle >>=
\y ->
words y >>=
\strings ->
strings !! 1 >>=
\string->
putStr string
[Edit] Solution :
main :: IO()
main =
putStr "Name of File: " >>
getLine >>=
\st ->
openFile st ReadMode >>=
\handle ->
hGetContents handle >>=
\y ->
return (words y) >>=
\strings ->
return (strings !! 1) >>=
\string->
putStr string
Use return (words y) instead of just words y. return wraps a pure value (such as the [String] that words returns) into a monad.
From your wording, it sounds like this question is homework. If so, it should be tagged as such.
(This doesn't directly answer the question, but it will make your code more idiomatic and thus easier to read.)
You are using the pattern \x -> f x >>= ... a lot, this can (and should) be eliminated: it is (mostly) unnecessary noise which obscures the meaning of the code. I won't use your code, since it is homework but consider this example (note that I'm using return as suggested by the other answer):
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>=
\str -> return (lines str) >>=
\lns -> return (length lns) >>=
\num -> print num
(It reads a file name from the user, and then prints the number of lines in that file.)
The easiest optimisation is the section where we count the number of lines (this corresponds to the part where you are separating the words and getting the second one): the number of lines in a string str is just length (lines str) (which is the same as length . lines $ str), so there is no reason for us to have the call to length and the call to lines separate. Our code is now:
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>=
\str -> return (length . lines $ str) >>=
\num -> print num
Now, the next optimisation is on \num -> print num. This can be written as just print. (This is called eta conversion). (You can think about this as "a function that takes an argument and calls print on it, is the same as print itself"). Now we have:
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>=
\str -> return (length . lines $ str) >>= print
The next optimisation we can do is based on the monad laws. Using the first one, we can turn return (length . lines $ str) >>= print into print (length . lines $ str) (i.e. "creating a container that contains a value (this is done by return) and then passing that value to print is the same as just passing the value to print"). Again, we can remove the parenthesis, so we have:
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>=
\str -> print . length . lines $ str
And look! We have an eta-conversion we can do: \str -> print . length . lines $ str becomes just print . length . lines. This leaves:
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>= print . length . lines
At this point, we can probably stop, since that expression is much simpler than our original one (we could keep going, by using >=> if we wanted to). Since it is so much simpler, it is also easier to debug (imagine if we had forgotten to use lines: in the original main it wouldn't be very clear, in the last one it's obvious.)
In your code, you can and should do the same: you can use things like sections (which mean \x -> x !! 1 is the same as (!! 1)), and the eta conversion and monad laws I used above.

Resources