The Do-Let-Braces problem
Summary: Haskell layout has problems within 'do' (see below). Can we change Haskell to improve it? We teach Haskell to all our second-year students, and we like to teach the OPTION of using explicit braces and ;'s. (If students write nested do constructs, even though we discourage this, they often have trouble with the layout rules.) We would like to be able to teach this: "To use explicit structuring, instead of layout, you can just put braces around all the statements in a 'do' block, plus semicolons at the end of each statement. (like in C++/Java!)." But this does not work. For example, the first definition below parses, but the second does not. fa::IO () fa = do putStr "Enter Data: " line <- getLine let line2 = line putStr line2 fb::IO () fb = do { putStr "Enter Data: "; line <- getLine; let line2 = line; putStr line2; } ERROR "f.hs" (line 13): Syntax error in definition (unexpected symbol "putStr") Fix 1: Put explicit braces around the body of the 'let': let {line2 = line}; This is ugly and a nuisance to teach. It would be much nicer to be able to teach 'let pattern = expr' as a simple statement, and avoid mentioning the possibility of multiple definitions within a single 'let'. (Which is rarely needed within 'do' syntax anyway, because you can usually use several separate let statements instead. Mutually recursive definitions is probably the only reason.) Fix 2: an alternative fix is to move the semicolon at the end of the let to the beginning of the next line, but this is ugly! f2 = do { putStr "Enter Data: "; line <- getLine; let line2 = line ;putStr line2 } Fix 3: I might be happy to put ALL the semicolons at the beginning of lines, which also works, but you are not allowed a semicolon at the beginning of the first brace, so this looks asymmetric. f3 = do { putStr "Enter Data: " ;line <- getLine ;let line2 = line ;putStr line2 } Question: Is it possible to change the layout handling of Haskell slightly so that f1 is legal? Perhaps the explicit semicolon at the end of the let line could terminate the (implicit) layout of the let statement? Mark. Dr Mark Utting, Senior Lecturer Department of Computer Science School of Computing and Mathematical Sciences The University of Waikato Tel: +64 7 838 4791 Private Bag 3105 Fax: +64 7 858 5095 Hamilton Email: marku@cs.waikato.ac.nz New Zealand Web: http://www.cs.waikato.ac.nz/~marku It is a time-proven principle of software engineering to try and strengthen the static description of systems as much as possible. [Clemens Szyperski]
Mark Utting wrote:
Summary: Haskell layout has problems within 'do' (see below). Can we change Haskell to improve it?
I don't think the language has to be changed. It seems to me that the problems is more with the idea of editing the source code as plain text. Ideally, I would like an editor be able to draw a pretty square bracket (graphically, or with semi-graphic characters) around blocks, a bit like this:
f3 = do |~ putStr "Enter Data: " | line <- getLine | let line2 = line |_ putStr line2
XEmacs could probably be tricked into doing that, but it seems quite difficult. And you would have to redefine most key bindings so that it behaves correctly. You may have a look at Mjolner (http://www.mjolner.com/mjolner-system/), it is a development environment for BETA with a hierarchical editor which works in a similar way. It wouldn't be adequate for Haskell, but it demonstrates the idea. By the way, with a proper editor, you wouldn't even need the layout rule. Source code would be stored with explicit bracing, and the editor could hide all these cluttering symbols, or display them in any fancy way you like. -- Sebastien Carlier
Thu, 15 Feb 2001 15:20:19 +1300, Mark Utting <marku@goblin.cs.waikato.ac.nz> pisze:
fb::IO () fb = do { putStr "Enter Data: "; line <- getLine; let line2 = line; putStr line2; }
I have a different proposal. Let's drop the 'let' keyword in value bindings in a 'do' block! The largest continouos block of bindings is translated to a single 'let'. (They can define a function by cases and can be mutually recursive.) fb1 ::IO () fb1 = do { putStr "Enter Data: "; line <- getLine; line2 = line; putStr line2; } fb2 ::IO () fb2 = do putStr "Enter Data: " line <- getLine line2 = line putStr line2 A disadvantage of let is that when the definition must be continued on the next line, the word after 'let' set the indentation and the continuation must be indented more than expected: do foo let x = a very long expression - note that the next line must be indented *too much*! bar I would like to have indents of the width four and a single indent here. My proposal solves both of these problems. A disadvantage: it breaks code which uses separate 'let' clauses which shadow their variables. -- __("< Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/ \__/ ^^ SYGNATURA ZASTÊPCZA QRCZAK
Mark Utting writes:
fb::IO () fb = do { putStr "Enter Data: "; line <- getLine; let line2 = line; putStr line2; }
ERROR "f.hs" (line 13): Syntax error in definition (unexpected symbol "putStr")
I find it hard to determine exactly from the Report whether this is a bug in Haskell, or just a bug in ghc. For what it's worth, nhc98 parses and accepts your example exactly as you intended.
Fix 3: I might be happy to put ALL the semicolons at the beginning of lines, which also works, but you are not allowed a semicolon at the beginning of the first brace, so this looks asymmetric. f3 = do { putStr "Enter Data: " ;line <- getLine ;let line2 = line ;putStr line2 }
A common style I have seen is do { putStr "Enter Data: " ; line <- getLine ; let line2 = line ; putStr line2 } which matches nicely what Sebastien Carlier proposed in his message:
Ideally, I would like an editor be able to draw a pretty square bracket (graphically, or with semi-graphic characters) around blocks, a bit like this:
f3 = do |~ putStr "Enter Data: " | line <- getLine | let line2 = line |_ putStr line2
Regards, Malcolm
fb = do { putStr "Enter Data: "; line <- getLine; let line2 = line; putStr line2; }
I suggest doing this:
fb = do { putStr "Enter Data: " ; line <- getLine ; let line2 = line ; putStr line2 }
which looks nicer and has the advantage of being much easier to keep track of where the semicolons are. -Paul
participants (5)
-
Malcolm Wallace -
Mark Utting -
Paul Hudak -
qrczak@knm.org.pl -
Sebastien Carlier