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

Postfix/Prefix Calls

Motivation

In Fuzion, operators can be prefix -x, infix x*y, or postfixx!. A call to f with one argument a is of the form f a, so it can be interpreted like a prefix operator.

This is nice and good, but for adding physical units this is unfortunate. Image a routine sec that takes in u64 and produces a duration that can be passed to wait:

  delay := sec 5
  wait delay

It would be more natural to write

  delay := 5 sec
  wait delay

or even

  wait (5 sec)

or even

  wait 5 sec

maybe even

  wait 5sec

Solution

Along the lines of postfix operator declaration

  i32.postfix ′ => duration val*1_000

we could allow postfix calls to be declared:

  i32.postfix sec => duration val*1_000

Implementation

Expression Parsing

The grammar needs to be extended to permit a term to be followed by a qualified call that is then converted into a postfix call.

Call resolution

Furthermore, calls that could not be resolved, like wait n sec when wait expects only one argument, should be analyzed for arguments that themselves cannot be resolved, like sec, which does not exist without an argument but exists as a postfix call. These should then be converted to wait (n sec) automatically.

Lexer treatment of Literals

Parsing of a literal may stop as soon as a lexer error would occur, i.e., in the code

  x := 1E3
  y := 1F3

the 1E3 in the first line would be parsed a a single numeric literal 1E3, while the lexer would split 1F3 in the second line into a numeric literal 1 followed by an identifier F3, that can then be parsed as a postfix call, i.e., this is equivalent to

  x := 1000
  y := postfix F3(1)

Problems

Single-line conditionals

Single-line if-statements without then can become ambiguous, e.g.

  if a < 10 sec y

this is currently interpreted as

  if (a < 10) then { sec(y) }

but should be

  if (a < (10 sec)) then { y }

A solution would be to require then in a single-line if-statement. This might be a good idea anyways since we already have the same problem with code like this:

  if a < b y

where b y is a call to b with argument y, i.e., it is parsed as

  if (a < (b y)) then { }

, while the same code using 10 instead of b

  if a < 10 y

is parsed as

  if (a < 10) then { y }

.

Solution

Make then required in single-line if-statements.

Prefix Calls

Problem with Prefix Calls

Calls already use prefix notation as long as the target of the call is disregarded. For mathematical operations such as sin this means that we have a choice of either defining them in some math feature as in

  math is
    ...
    sin(f f64) f64 is ...
    ...

and then call them as

  a := π/6
  y := math.sin a * r

or alternatively, define them as part of the float types

  f64 is
    ...
    sin f64 is ...
    ...

and then call them as

  a := π/6
  y := a.sin * r

both solutions are unsatisfactory since they do not match the established mathematical notation.

Solution using Prefix Calls

If we allow prefix calls similar to postfix calls, i.e.,

  f64 is
    ...
    prefix sin f64 is ...
    ...

we could write

  a := π/6
  y := sin a * r

Implementation

The implementation could work as follows: For a call f a1 a2 a3 that cannot be resolved in the current feature, the compiler could convert this call into a1.prefix f a2 a3 in case the static type of a1 defines prefix f.

Other Languages

Nim treats calls of the form o.f or p.g(a,b) as syntax sugar for f(o) and g(p,a,b), respectively. This is different to the suggestions for Fuzion which would have a clear distinction between plain calls and prefix calls.