title: What does Haskell's "do" keyword do?
body: <p>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:</p>
  
  <pre><code>isEven :: Int -&gt; Bool
  isEven x =
      if mod x 2 == 0 then True
      else False
  
  isOdd :: Int -&gt; Bool
  isOdd x =
      not (isEven x)
  
  main =
      print (isEven 2)
      print (isOdd 2)
  </code></pre>
  
  <p>But this failed with this error during compilation:</p>
  
  <pre><code>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 -&gt; IO ()) -&gt; Bool -&gt; t’
                with actual type ‘IO ()’
      • The function ‘print’ is applied to three arguments,
        but its type ‘Bool -&gt; 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
  </code></pre>
  
  <p>So, I saw some code online with the "do" keyword, so I tried it like this:</p>
  
  <pre><code>isEven :: Int -&gt; Bool
  isEven x =
      if mod x 2 == 0 then True
      else False
  
  isOdd :: Int -&gt; Bool
  isOdd x =
      not (isEven x)
  
  main = do
      print (isEven 2)
      print (isOdd 2)
  </code></pre>
  
  <p>And it worked exactly like I thought it should.</p>
  
  <p>What's going on here? Why doesn't the first code snippet work? And what does adding "do" actually do?</p>
  
  <p>PS. I saw something about "monads" on the internet related to the "do" keyword, does that have something to do with this?</p>