Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

io

haskell

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
like image 461
Victor Casé Avatar asked Dec 17 '22 02:12

Victor Casé


2 Answers

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.

like image 200
bitbucket Avatar answered Apr 26 '23 23:04

bitbucket


(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.

like image 24
huon Avatar answered Apr 27 '23 00:04

huon