Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does Haskell's "do" keyword do?

I'm a C++/Java programmer, and I'm trying to learn Haskell (and functional programming in general), and I've been having a rough go at it. One thing I tried was this:

isEven :: Int -> Bool
isEven x =
    if mod x 2 == 0 then True
    else False

isOdd :: Int -> Bool
isOdd x =
    not (isEven x)

main =
    print (isEven 2)
    print (isOdd 2)

But this failed with this error during compilation:

ghc --make doubler.hs -o Main
[1 of 1] Compiling Main             ( doubler.hs, doubler.o )

doubler.hs:11:5: error:
    • Couldn't match expected type ‘(a0 -> IO ()) -> Bool -> t’
              with actual type ‘IO ()’
    • The function ‘print’ is applied to three arguments,
      but its type ‘Bool -> IO ()’ has only one
      In the expression: print (isEven 2) print (isOdd 2)
      In an equation for ‘main’: main = print (isEven 2) print (isOdd 2)
    • Relevant bindings include main :: t (bound at doubler.hs:10:1)
make: *** [all] Error 1

So, I saw some code online with the "do" keyword, so I tried it like this:

isEven :: Int -> Bool
isEven x =
    if mod x 2 == 0 then True
    else False

isOdd :: Int -> Bool
isOdd x =
    not (isEven x)

main = do
    print (isEven 2)
    print (isOdd 2)

And it worked exactly like I thought it should.

What's going on here? Why doesn't the first code snippet work? And what does adding "do" actually do?

PS. I saw something about "monads" on the internet related to the "do" keyword, does that have something to do with this?

like image 958
Dovahkiin Avatar asked Nov 27 '16 18:11

Dovahkiin


People also ask

When would you use a DO statement Haskell?

As a syntactical convenience, do notation does not add anything essential, but it is often preferable for clarity and style. However, do is not needed for a single action, at all. The Haskell "Hello world" is simply: main = putStrLn "Hello world!"

What does <$> mean in Haskell?

It's merely an infix synonym for fmap , so you can write e.g. Prelude> (*2) <$> [1.. 3] [2,4,6] Prelude> show <$> Just 11 Just "11" Like most infix functions, it is not built-in syntax, just a function definition. But functors are such a fundamental tool that <$> is found pretty much everywhere.

What does >> mean in Haskell?

Essentially, a >> b can be read like "do a then do b , and return the result of b ". It's similar to the more common bind operator >>= .

What does the operator do in Haskell?

Haskell provides special syntax to support infix notation. An operator is a function that can be applied using infix syntax (Section 3.4), or partially applied using a section (Section 3.5).


2 Answers

Why doesn't the first code snippet work?

Outside of a do block, line breaks don't have any significance. So your first definition of main is equivalent to main = print (isEven 2) print (isOdd 2), which fails because print only takes one argument.

Now you may wonder why we can't just use line breaks to signify that one function should be called after another. The problem with that is that Haskell is (usually) lazy and purely functional, so functions don't have side-effects and there's no meaningful concept of calling one function after another.

So then how does print work at all? print is a function that takes a string and produces a result of type IO (). IO is a type that represents possibly side-effecting operations. main produces a value of this type and the operations described by that value will then be executed. And while there's no meaningful concept of calling one function after another, there is a meaningful concept of executing one IO value's operation after another one's. For this we use the >> operator, which chains two IO values together.

I saw something about "monads" on the internet related to the "do" keyword, does that have something to do with this?

Yes, Monad is a type class (if you don't know what those are yet: they're similar to interfaces in OO languages), which (among others) provides the functions >> and >>=. IO is one instance of that type class (in OO terms: one type that implements that interface), which uses those methods to chain multiple operations after each other.

The do syntax is a more convenient way of using >> and >>=. Specifically your definition of main is equivalent to the following without do:

main = (print (isEven 2)) >> (print (isOdd 2))

(The extra parentheses aren't necessary, but I added them to avoid any confusion about precedence.)

So main produces an IO value that executes the steps of print (isEven 2), followed by those of print (isOdd 2).

like image 198
sepp2k Avatar answered Oct 19 '22 08:10

sepp2k


I think for the time being you will just have to accept it. Yes, the do-notation is syntactic sugar for the monad type class. Your code could be desugared to the following:

main = print (isEven 2) >> print (isOdd 2)

(>>) means something like do this after that in this particular case. However there is really no good in trying to explain Haskell IO and monads in a StackOverflow answer. Instead I recommend you to just keep learning until your book or whatever you use as a learning resource covers the topic.

Here is however a quick example of what you can do inside of IO-do. Don't bother about the syntax too much.

import System.IO
main = do
  putStr "What's your name? "  -- Print strings
  hFlush stdout                -- Flush output
  name <- getLine              -- Get input and save into variable name
  putStrLn ("Hello " ++ name)
  putStr "What's your age? "
  hFlush stdout
  age <- getLine
  putStr "In one year you will be "
  print (read age + 1)         -- convert from string to other things with read
                               -- use print to print things that are not strings
like image 5
Julia Path Avatar answered Oct 19 '22 06:10

Julia Path