3.8 KiB
Leo RFC 013: Constant Functions
Author(s)
The Aleo Team.
Status
IMPLEMENTED
Summary
This RFC proposes the additional of an optional const
modifier to function declarations
to explicitly designate functions that return constant values that can be calculated at compile time.
Motivation
Explicitly designating constant functions makes user intention explicit and simplifies the Leo compiler's checks, as explained below.
Background
Leo code is partially evaluated on its const
inputs prior to being translated to R1CS.
A function that returns a value that only depends on const
inputs directly or indirectly,
can be partially evaluated away, without having to be inlined during flattening.
Design
Syntax
The ABNF grammar is extended by adding an optional const
modifier to function declarations:
function-declaration = *annotation [ %s"const" ] %s"function" identifier
"(" [ function-parameters ] ")" [ "->" type ]
block
This applies to both top-level and member functions.
Static Semantics
A const
function declaration must satisfy the following conditions:
- All its parameters are
const
, including theself
parameter for instance circuit member functions. - The body does not reference the special
input
variable. - The body only calls other
const
functions.
Dynamic Semantics
This has no impact on the dynamic semantics of Leo, viewed as a traditional programming language.
Flattening
Given that const
expressions are evaluated completely during flattening,
the values of the arguments of a const
function call are known during flattening,
and therefore the function call can be completely evaluated as well.
If the function is recursive (individually or with others), the evaluation involves the bounded recursion analysis described in a separate RFC.
Implementation Considerations
ASTs for function declarations are extended with a boolean flag const_
.
If a const
function has a non-const
parameter,
an ASG error occurs.
If the body of a const
function references the input
variable or calls a non-const
function,
an ASG error occurs.
The description of static semantics, dynamic semantics, and flattening given above are expressed in terms of Leo, because that is the user's view of the language. In the implementation, flattening occurs after the Leo code is translated to the IR.
Examples
const function len(const arr: [u8; _]) -> u32 {
return arr.len();
}
circuit Sample {
x: [char; 5]
const function say_hi(const self) -> [char; 5] {
return self.x;
}
}
Drawbacks
This extension does not appear to bring any drawbacks.
Effect on Ecosystem
None.
Alternatives
No Constant Designation
Without an explicit designation of constant functions,
the Leo compiler needs to perform an inter-procedural analysis:
if f
calls g
, in order for f
to be constant, also g
must be constant.
In other words, the call graph must be taken into account.
In contrast, with the const
designation,
an intra-procedural analysis suffices,
as discussed in the static semantics section above.
Future Extensions
In other languages like Rust, const
functions are not required to have all constant parameters.
They are just required to return constant results for constant arguments,
i.e. they must not access global variables and they must only call other const
functions.
In other words, these const
functions are polymorphic over "constancy".
This could be also realized in Leo, because type inference/checking determines const
and non-const
expressions.
This tells the compiler which function calls have all const
arguments and which ones do not.
Therefore, the compiler can selectively evaluate, during flattening, only the calls of const
functions on const
arguments.