Why is Haskell reading the next line as an argument for the previous function?

Viewed 88

I am trying to run this extremely simple test program because I was having too many argument errors (reading the next line as an argument) in the code I was working with before.

When I run this code:

main = do{
    putStrLn ("test")
    print ("works")
}

I get the message:

• Couldn't match expected type ‘(a0 -> IO ()) -> [Char] -> t’
                  with actual type ‘IO ()’
    • The function ‘putStrLn’ is applied to three arguments,
      but its type ‘String -> IO ()’ has only one
      In a stmt of a 'do' block: putStrLn ("test") print ("works")
      In the expression: do putStrLn ("test") print ("works")
    • Relevant bindings include main :: t (bound at test.hs:1:1)

Am I missing something extremely simple here?

1 Answers

In the simplest case, there's two ways of writing block in Haskell. One uses whitespace to implicitly delineate the beginning and ending of the block and to separate parts; the other uses explicit markers. Here's how they both look:

-- implicit
do
    part one of block
    part two of block
      more indentend and so still part two of block
    part three of block
  less indented and so block has ended

-- explicit
do {
    part one of block;
      part two of block;
  part three of block; part four of block } block has ended

It turns out that the way this is specified lets you mix and match a little bit. Including semicolons to separate parts of the block is allowed in implicit mode:

do
    part one of block; part two of block
    part three of block

But mixing the other way, using curly braces to mark the beginning and end of the block but whitespace to separate parts, is not allowed.

Consequently, when you write:

main = do{
    putStrLn ("test")
    print ("works")
}

This is equivalent to one of the following:

-- explicit -- note, no semicolons!
main = do { putStrLn ("test") print ("works") }

-- implicit*
main = do
    putStrLn ("test") print ("works")

That is, this is the application of putStrLn to three arguments, ("test"), print, and ("works"). You can fix it either by adding a semicolon after putStrLn ("test") or by deleting the curly braces.

-- explicit
main = do{
    putStrLn ("test");
    print ("works")
}

-- implicit
main = do
    putStrLn ("test")
    print ("works")

* I suspect some experts will be thinking it odd that I put the block on the next line for the implicit version but not the explicit one. For pedagogy and parallelism with the earlier sections, I have written the first line of the block on a separate line. But yes, this is also equivalent to main = do putStrLn ("test") print ("works") all on one line -- and countless other variations on the theme.

Related