Postfix/Prefix Calls
Motivation
In Fuzion, operators can be prefix -x
, infix x*y
,
or postfix x!
. 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 an 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. 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
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
. 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 => ... ...
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 => ... ...
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 => ... ...
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.