Fuzion Logo
flang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

Blocks And Indentation

Languages like Python (context free movie) are indentation based instead of using lexer tokens such as { } or BEGIN END or similar for blocks.

Developer opinion about assigning semantic meaning to white space is diverse, many love Python's, while you see hateful articles about this like in the CACM Kode Vicious column by George V. Neville-Neil (Sanity vs. Invisible Markings—Tabs vs. spaces that polemically suggests using the pile of poo unicode character instead.

Block Structure using Tokens

Any reasonable style guidelines require blocks to be indented properly, even if this is redundant when begin and end tokens such as { } are used. Hence, for any multi-line blocks, using tokens in a properly formatted source code is just redundant and a waste of space.

However, things become tricky when

One solution might be to add additional keywords to avoid ambiguity, e.g., defining a routine with f do { } would make the braces obsolete.

Block Structure in other languages

C/C++/Java/C# family: { }

Control statements such as if and while have a single statement as their condition code or loop code:

  if (condition)
    statement;
  else
    statement;

  while (condition)
    statement;

A clear separation between the condition and the statement is achieved by putting the condition in paranetheses.

A block is a statement that consists of braces with a semiclonon separated sequence of statements:

  {
    statement1;
    statement2;
    statement3;
  }

Consequently, braces can be used in control statement as follows

  if (condition)
  {
    statement1;
    statement2;
    statement3;
  }
  else
  {
    statement4;
    statement5;
    statement6;
  }

  while (condition)
  {
    statementA;
    statementB;
    statementC;
  }

Some cases require a block, e.g., a function definition

  void f(String s)
  {
    print(s);
  }


  // illegal:
  void f(String s)
    print(s);

Pascal/Modula/Oberon/Eiffel family: begin / end

Control statements start a sequence of statements with a keyword and end it with a keyword. Statements are separated by semicolons:

  IF condition THEN
    statement1;
    statement2;
    statement3
  ELSE
    statement4;
    statement5;
    statement6
  END

  WHILE condition DO
    statementA;
    statementB;
    statementC
  END

Declarations of procedures use BEGIN / END:

PROCEDURE f(VAR x: STRING);
BEGIN
  print(x)
END f;

Python/Nim family: colon and indentation

Within a control statement, statement sequences are introduced with a colon : followed by 0 or more new lines with an indentation level deeper than the surrounding control statement:

  if condition:
    statement1
    statement2
    statement3
  else:
    statement4
    statement5
    statement6

  while condition:
    statementA
    statementB
    statementC

Functions use the same pattern: Colon followed by indentation:

def f(x):
  print(x)

LF as brace

When a block extends over several lines, standard formatting rules require the statements to be indented to a level deeper than the statement surrounding the block:

  if cond {
    stdout.println("we are in the if clause")
    stdout.println("and we have checked that ")
    stdout.println("cond is true")
  } else {
    stdout.println("we are in the else clause")
    stdout.println("hence cond is not true")
  }
  stdout.println("this is always executed")

or, alternatively

  if cond
  {
    stdout.println("we are in the if clause")
    stdout.println("and we have checked that ")
    stdout.println("cond is true")
  }
  else
  {
    stdout.println("we are in the else clause")
    stdout.println("hence cond is not true")
  }
  stdout.println("this is always executed")

In both these cases, the braces { } are redundant with the indentation. If the parser would treat a LF followed by code with deeper indentation like an opening brace { and the corresponding LF follwed by shallower indentation like the corresponding closing brace }>, the code could become.

  if cond
    stdout.println("we are in the if clause")
    stdout.println("and we have checked that ")
    stdout.println("cond is true")
  else
    stdout.println("we are in the else clause")
    stdout.println("hence cond is not true")
  stdout.println("this is always executed")

Additional blank lines or comments should be ignored independent of their indentation.

Blocks in a Single Line

Braces can still be used in a single line:

  if col == red    { if useRGB { r = 0xff; g = 0x00; b = 0x00 } else { c = 0; m =   1; y = 1; k = 0 } }
  if col == yellow { if useRGB { r = 0xff; g = 0xff; b = 0x00 } else { c = 0; m =   0; y = 1; k = 0 } }
  if col == pink   { if useRGB { r = 0xff; g = 0x80; b = 0xff } else { c = 0; m = 0.5; y = 1; k = 0 } }

If we eagerly parse blocks that do not start with a LF until the EOL, we could even omit the braces here:

  if col == red    if useRGB r = 0xff; g = 0x00; b = 0x00 else c = 0; m =   1; y = 1; k = 0
  if col == yellow if useRGB r = 0xff; g = 0xff; b = 0x00 else c = 0; m =   0; y = 1; k = 0
  if col == pink   if useRGB r = 0xff; g = 0x80; b = 0xff else c = 0; m = 0.5; y = 1; k = 0

This appears a bit confusing, though.

Disallowing nesting in single lines, like Python, would require the code to look like this:

  if col == red
    if useRGB
      r = 0xff
      g = 0x00
      b = 0x00
    else
      c = 0
      m =   1
      y = 1
      k = 0
  if col == yellow
    if useRGB
      r = 0xff
      g = 0xff
      b = 0x00
    else
      c = 0
      m =   0
      y = 1
      k = 0
  if col == pink
    if useRGB
      r = 0xff
      g = 0x80
      b = 0xff
    else
      c = 0
      m = 0.5
      y = 1
      k = 0

which makes it hard to see the symmetry and spot bugs that are easier to spot when using one-liners.

It seems best to allow blocks in a single line iff braces are used.

Routine Declaration

The most simple syntax to declare a routine is a feature name followed by a block of statements, while a call is just the feature name:

  feat1      // declare feat1 as a routine
  {
    stmnt1
    stmnt2
  }

  feat1      // call feat1

Not using braces but LF with indentation like this

  feat1      // declare feat1 as a routine
    stmnt1
    stmnt2

  feat1      // call feat1

can turn problematic if the block becomes empty, e.g, by commenting out the statements:

  feat1      // !!! this is now a call to feat1 !!!
    // stmnt1
    // stmnt2

  feat1      // call feat1

this change turns the feature declaration into a feature call. These kind of semantic changes from commenting a statement are not acceptable, we need a different solution.

Routine Declaration with braces

Requiring braces for any routine declaration solves this:

  feat1 {    // declare feat1 as a routine
    // stmnt1
    // stmnt2
  }

  feat1      // call feat1

Routine Declaration with keyword

Requiring another keyword like is would also solve this:

  feat1 is    // declare feat1 as a routine
    // stmnt1
    // stmnt2

  feat1      // call feat1

Empty Blocks

The Fuzion syntax for feature declarations still poses problems using indenting line breaks since there is no way to distinguish an empty block from no block:

  feature1 { print("in feature1") }

  feature2
    print("in feature2")

  feature3 { }

  feature4
    // does nothing

  feature5 {
    print("in feature5")
  }

Here, feature4 would be parsed as a call to feature4, not a declaration, while feature1, feature2, feature3 and feature5 would be parsed as feature declarations.

Requiring is in feature declarations

The can be solbed by requiring a keyword is or similar:

  feature1 is { print("in feature1") }

  feature2 is
    print("in feature2")

  feature3 is { }

  feature4 is
    // does nothing

  feature5 {
    print("in feature5")
  }

is could be made optional if { is used:

  feature1 { print("in feature1") }

  feature2 is
    print("in feature2")

  feature3 { }

  feature4 is
    // does nothing

  feature5 {
    print("in feature5")
  }

We could also add a notation such as . as shortcut code { }:

  feature1 { print("in feature1") }

  feature2 is
    print("in feature2")

  feature3.

  feature4.
    // does nothing

  feature5 {
    print("in feature5")
  }

Requiring keyword before feature declarations

Alternatively, we could add aa keyword such as feature (or proc, function, etc.) before a feature declaration:

  feature feature1 { print("in feature1") }

  feature feature2
    print("in feature2")

  feature feature3 { }

  feature feature4
    // does nothing

  feature5 {
    print("in feature5")
  }

Block Structure in Fuzion

The basic idea is to use a block structure based on braces { } and semicolon ; as in the C/C++/Java/C# family (and make those familiar to this style immediately happy), but to have additional rules that make these symbols optional when line breaks and indentation clearly separate statements or blocks.

Flat Line Breaks as Statement Delimiters

Let's start with statement squences: Statements within a block as separated by the statement delimitor. A statement delimitor is either a semicolon ; or a line break that maintains the indentation level (a flat line break) of the statement sequence. So the following statments sequences are legal and equivalent:

  // using semicolon as statement delimitor
  statement1; statement2; statement3

  // using flat line break that maintains indentation level as statement delimitor
  statement1
  statement2
  statement3

  // mixing semicolon and flat line break as statement delimitor
  statement1
  statement2; statement3

  // mixing semicolon and flat line break as statement delimitor
  statement1; statement2
  statement3

Indenting Line Breaks as Block Delimiters

A line break is called indenting if it increases the indentation level, while a line break that decreases the indentation level is reverse-indenting. With these definitions, we can now allow line breaks as block delimiters: An opening curly brace { is equivalent to an indenting line break, while a closing curly brace } is equivalent to a reverse-indenting line break, so we can define blocks as follows:

  // blocks using braces

  if condition {
    statement1
    statement2
    statement3
  } else {
    statement4
    statement5
    statement6
  }

  while condition {
    statementA
    statementB
    statementC
  }

  // blocks using braces in separate lines

  if condition
  {
    statement1
    statement2
    statement3
  }
  else
  {
    statement4
    statement5
    statement6
  }

  while condition
  {
    statementA
    statementB
    statementC
  }

  // blocks using indentation

  if condition
    statement1
    statement2
    statement3
  else
    statement4
    statement5
    statement6

  while condition
    statementA
    statementB
    statementC

  // blocks using indentation and semicolons

  if condition
    statement1; statement2; statement3
  else
    statement4; statement5; statement6

  while condition
    statementA; statementB; statementC

This syntax is in a sense more radical than Python's syntax, we can do without the colon after the condition, the indenting line break is sufficient.

Single line code

Single line code using braces { }

Using braces and semicolons, it is sill possible to create more complex code in a single-line if this appears useful:

  // single-line statments using braces and semicolons

  if condition { statement1; statement2; statement3 } else { statement4; statement5; statement6 }

  while condition { statementA; statementB; statementC }

Such code is particularly convenient for repeated code pattern that differ only in a few places:

  match color
    | yellow  => if rgb { r = 0xff; g = 0xff; b = 0x00 } else { c = 0.0; m = 0.0; y = 1.0; k = 0.0 }
    | magenta => if rgb { r = 0xff; g = 0x00; b = 0xff } else { c = 0.0; m = 1.0; y = 0.0; k = 0.0 }
    | cyan    => if rgb { r = 0x00; g = 0xff; b = 0xff } else { c = 1.0; m = 0.0; y = 0.0; k = 0.0 }
    | red     => if rgb { r = 0xff; g = 0x00; b = 0x00 } else { c = 0.0; m = 1.0; y = 1.0; k = 0.0 }
    | green   => if rgb { r = 0x00; g = 0xff; b = 0x00 } else { c = 1.0; m = 0.0; y = 1.0; k = 0.0 }
    | blue    => if rgb { r = 0x00; g = 0x00; b = 0xff } else { c = 1.0; m = 1.0; y = 0.0; k = 0.0 }
    | white   => if rgb { r = 0xff; g = 0xff; b = 0xff } else { c = 0.0; m = 0.0; y = 0.0; k = 0.0 }
    | black   => if rgb { r = 0x00; g = 0x00; b = 0x00 } else { c = 0.0; m = 0.0; y = 0.0; k = 1.0 }
    | grey    => if rgb { r = 0x80; g = 0x80; b = 0x80 } else { c = 0.0; m = 0.0; y = 0.0; k = 0.5 }

Single line code using eager parsing to EOL

If the code in a statement sequence cannot be parsed as part of a preceding expression, we could do without braces and eagerly parse statements until a keyword such as else or the end of the line is encountered:

  // single-line statments using braces and semicolons

  if condition statement1; statement2; statement3 else statement4; statement5; statement6

  while condition statementA; statementB; statementC

The code above then becomes:

  match color
    | yellow  => if rgb r = 0xff; g = 0xff; b = 0x00 else c = 0.0; m = 0.0; y = 1.0; k = 0.0
    | magenta => if rgb r = 0xff; g = 0x00; b = 0xff else c = 0.0; m = 1.0; y = 0.0; k = 0.0
    | cyan    => if rgb r = 0x00; g = 0xff; b = 0xff else c = 1.0; m = 0.0; y = 0.0; k = 0.0
    | red     => if rgb r = 0xff; g = 0x00; b = 0x00 else c = 0.0; m = 1.0; y = 1.0; k = 0.0
    | green   => if rgb r = 0x00; g = 0xff; b = 0x00 else c = 1.0; m = 0.0; y = 1.0; k = 0.0
    | blue    => if rgb r = 0x00; g = 0x00; b = 0xff else c = 1.0; m = 1.0; y = 0.0; k = 0.0
    | white   => if rgb r = 0xff; g = 0xff; b = 0xff else c = 0.0; m = 0.0; y = 0.0; k = 0.0
    | black   => if rgb r = 0x00; g = 0x00; b = 0x00 else c = 0.0; m = 0.0; y = 0.0; k = 1.0
    | grey    => if rgb r = 0x80; g = 0x80; b = 0x80 else c = 0.0; m = 0.0; y = 0.0; k = 0.5

Is this too much?

Complex Statements

Sometimes, it might be necessary to break a statement into several lines to avoid overly long lines, e.g.

  if perform_affine_tranform
    (new_x, new_y, new z) := apply_affine_transformation_matrix(old_x, old_y, old_z, affineMatrix(factor_xx, factor_xy, factor_xz, factor_yx, factor_yy, factor_yz, factor_zx, factor_zy, factor_zz), translationVector(translate_x, translate_y, translate_z))

should better be written somthing like this (it should best be broken up into several statements, but this is not the point here):

  if perform_affine_tranform
    (new_x,
     new_y,
     new z) :=
      apply_affine_transformation_matrix(
        old_x, old_y, old_z,
        affineMatrix
          (factor_xx, factor_xy, factor_xz,
           factor_yx, factor_yy, factor_yz,
           factor_zx, factor_zy, factor_zz),
        translationVector(translate_x,
                          translate_y,
                          translate_z))

Additional line breaks within one statement should be allowed provided that:

Complex Conditions

A line break within an expression can cause ambiguity for the parser, e.g.

  // This cannot be parsed well:
  if z.real * z.real
    + z.imaginary * z.imaginary < 2
    draw(x,y,black)
  else
    draw(x,y,colors[depth % 16])

The problem is that the code before the indenting line break z.real * z.real syntactically is a valid expression and the code after the line break + z.imaginary * z.imaginary < 2 syntactically is a valid statement, so the parser has no easy way to tell the end of the condition from the beginning of the statement sequence.

A simple solution is to restrict line breaks within conditions to code within indenting token pairs and apply the same rules as for line breaks withing statements.

Then, a legal way to add line breaks in this example would be

  if (z.real * z.real
      + z.imaginary * z.imaginary < 2)
    draw(x,y,black)
  else
    draw(x,y,colors[depth % 16])

or

  if 2 > (z.real * z.real +
          z.imaginary * z.imaginary)
    draw(x,y,black)
  else
    draw(x,y,colors[depth % 16])

but not

  // This cannot be parsed well:
  if (z.real * z.real
      + z.imaginary * z.imaginary) < 2
    draw(x,y,black)
  else
    draw(x,y,colors[depth % 16])

Expressions with '.'

Restricting expressions to a single line would disallow code using cascading calls such as

  (1..n)
    .asStream
    .filter(fun (x i32) => (n % x) == 0)
    .print

Since no legal statement starts with a '.', we can relax the single-line restriction by allowing line breaks before '.'.

Routine Declarations

To be able to distinguish routines with no code from calls, Fuzion will require routine declarations to either use braces { } around the code of a routine or perform the declaration using is.

Using is also gives a nice syntax for the definition of abstract (deferred in Eiffel-parlance) or intrinsic features

  // all of these are equivalent:

  show1(o Obj) { print(o.asString) }

  show2(o Obj) {
    print(o.asString)
  }

  show3(o Obj)
  {
    print(o.asString)
  }

  show4(o Obj) is print(o.asString)

  show4(o Obj) is
    print(o.asString)

  show5(o Obj) is { print(o.asString) }

  show6(o Obj) is {
    print(o.asString)
  }

  show7(o Obj) is
  {
    print(o.asString)
  }

  // all of these are empty features

  f1 is

  f2 is { }

  f3 { }

  f4 {
  }

  f5
  {
  }

  // special features

  asString is abstract
  infix +° (o i32) i32 is intrinsic