Callers can accept one set of parameters to call a function, using their parameters to get the parameters of the function.
Must be defined with the keyword call, followed by the name, then a list of parameters, then ":' and the function call:
fn foo(a: i32) -> i32 { a + 1 }
call bar(a: i32, b: i32) : foo(a + b);
It is questionable whether it is necessary to prescribe a return value for the caller, but if it is, the syntax should be like this:
call bar(a: i32, b: i32) -> i32 : foo(a + b);
In addition, another alterative syntax for callers was invented, where the callers would be spelled out immediately after the function. If this syntax will be present, it is questionable whether we should leave it alone and when should we write the return value:
fn foo(a: i32) -> i32
call bar(a: i32, b: i32) : foo(a + b)
{ a + 1 }
Callers can have the same name as the function being called, but with certain restrictions:
- A caller cannot have as many parameters as the function being called or as many parameters as already defined callers for that function with the same name.
- Callers cannot be created for template functions
Example:
fn foo(a: i32) -> i32 { a + 1 }
call foo(a: i32, b: i32) : foo(a + b);
All default parameters of a function must be expanded to an appropriate number of corresponding callers:
fn foo(a: i32, b: i32 = 0) -> i32 { a + b }
//At the compile stage it turns into :
fn foo(a: i32, b: i32) -> i32 { a + b }
call foo(a: i32) : foo(a, 0);
Calling a caller is not different from calling a function.
This substitution of function overloads is completely explicit. Any call is unambiguous and defined by the function/caller name + number of arguments. The same name still always calls the same code, only in a fraction of the cases with changes in the arguments. The use of callers should be allowed wherever it is possible to write functions.
Using callers with a name other than function allows to reduce the size of the code. And using callers with the same name as a function allows you to use them in the same situations where function overloading is useful.
A real example:
fn harmonic_mean(vec: Vec<f64>) -> f64 {
let mut sum = 0.;
for x in vec {
sum += 1/x;
}
vec.len() as f64 / sum
}
call harmonic_mean(a: f64, b: f64) : harmonic_mean(vec![a, b]);
Examples of Alternative Syntax:
fn foo(a: i32, b: i32 = 0) -> i32 { a + b }
call foo(a: i32) : foo(a, 0);
//...
call foo(a: i32) -> i32 : foo(a, 0);
//If the possibility of other names will be absent:
call foo(a: i32) : (a, 0);
//If they can be written only after the function signature:
fn foo(a: i32) -> i32
call bar(a: i32, b: i32) : (a + b)
{ a + 1 }
//...
fn foo(a: i32) -> i32
call bar(a: i32, b: i32) : (a + b),
call baz(a: i32, b: i32, c: i32) : (a + b + c)
{ a + 1 }
//...
fn foo(a: i32) -> i32
call bar(a: i32, b: i32) : (a + b), baz(a: i32, b: i32, c: i32) : (a + b + c)
{ a + 1 }
//If the possibility of other names will be absent and if they
//can be written only after the function signature:
fn foo(a: i32) -> i32
call(a: i32, b: i32) : (a + b)
{ a + 1 }
//...
fn foo(a: i32) -> i32
call(a: i32, b: i32) : (a + b), call(a: i32, b: i32, c: i32) : (a + b + c)
{ a + 1 }
//...
fn foo(a: i32) -> i32
call(a: i32, b: i32) : (a + b), (a: i32, b: i32, c: i32) : (a + b + c)
{ a + 1 }