Call to arms: lambda-case is stuck and needs your help

Tyson Whitehead twhitehead at
Sat Jul 7 06:08:26 CEST 2012

On July 6, 2012 11:49:23 Tyson Whitehead wrote:
> Currently it depends on the depth of this new level of indentation relative
> to all the groupings started on that line.  I think most people would
> expect it to just apply to the last grouping though.  That is
> where { f x = do {
>   stmt1
>   stmt2
> } }
> mask $ let { x = do {
>   stmt1
>   stmt2
> } }
> The rule in this case would be that if the grouping began on a newline that
> is idented farther then the previous line, the grouping is assocated with
> the grouping token and when it closes, it closes all those deeper than
> itself.

I've thought some more about this and it seems to me that there are two ways 
people might intuitively think about doing grouping via indentation.

1 - the first item is on the same line and subsequent ones are lined up with it

  do stmt1

2 - the first item is on a new line and subsequent ones are lined up with it.


The current layout engine is targeted at (1).  It appears to do (2), but it is 
not really reliable as things start to go south if the first line happened to 
open more than one grouping (precisely the problem that make '\' a group token 
would introduce in codes).  For an example, consider

  let greet name = do
    putStr "hello "
    putStrLn name
  in f "world"

It currently translates into

  let { greet name = do {} } putStr "hello " putStrLn name in f "world"

This results in an unituituve "Empty 'do' construct" error message.

I propose we detected (2) and make it work too.  That is, if the line ends 
with a grouping construct and the next line is indented relative to that line, 
then assume we really don't want '{}' and instead always start grouping (even 
if it isn't indented further than other possible groupings also started).

In other words, translate the above into

  let { greet name = do {
    putStr "hello";
    putStrLn name
  }} in f "world"

This would then correctly handle the problamatic case raised in wiki where

  mask $ \restore -> do

is in translated into

  mask $ \ { restore -> do {} } stmt1 stmt2

under the current rules if '\' is made a grouping token.

The very limited scope of this (i.e., it would only apply to lines that end 
with a grouping construct where the next line is indented further than that 
line) should also address Simon's concerns regarding things like

   f x y = x + y
     where  -- I just left this where here by accident

   g x = ...


   instance Exception Foo where
   instance Exception Bar

Cheers!  -Tyson

PS:  To be fully precise, the modified layout decoder in 9.3 would be

  L (<n>:ts)     i (m:ms) = ; : (L ts n (m:ms))   if m = n
                          = } : (L (<n>:ts) n ms) if n < m
  L (<n>:ts)     i ms     = L ts n ms
  L ({n}:<n>:ts) i ms     = { : (L ts n (n:ms))   if n > i (new rule)
  L ({n}:ts)     i (m:ms) = { : (L ts i (n:m:ms)) if n > m  (Note 1)
  L ({n}:ts)     i []     = { : (L ts i [n])      if n > 0  (Note 1)
  L ({n}:ts)     i ms     = { : } : (L (<n>:ts) i ms)       (Note 2)
  L (}:ts)       i (0:ms) = } : (L ts i ms)                 (Note 3)
  L (}:ts)       i ms     = parse-error                     (Note 3)
  L ({:ts)       i ms     = { : (L ts i (0:ms))             (Note 4)
  L (t:ts)       i (m:ms) = } : (L (t:ts) i ms)   if m /= 0 and parse-error(t) 
(Note 5)
  L (t:ts)       i ms     = t : (L ts i ms)
  L []           i []     = []
  L []           i (m:ms) = } : L [] i ms         if m /= 0 (Note 6)

As before, the function 'L' maps a layout-sensitive augmented token stream to 
a non-layout-sensitive token stream, where the augmented token stream includes 
'<n>' and '{n}' to, respectively, give the indentation level of the first token 
on a new line and that following a grouping token not followed by '{'.

This time though, we allow the '{n}' '<n>' sequence (before it was supressed 
to just '{n}').  We also add a new state variable 'i' to track the indentation 
of the  current line.  The new rule now opens a grouping over a newline so 
long as the indentation is greater than the current line.

Upon a less indented line, it will then close all currently open groups with 
an indentation less than the new line.

More information about the Glasgow-haskell-users mailing list