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.