Functions are logic containers with no side effects other than those explicitly stated by the developer. They may take any combination of variables and provide any form of output the developer specifies.
All functions must explicitly declare inputs and outputs using in[]
and out[]
:
// Basic function structure
fn functionName in[input_declarations] out[output_declarations] {
// Function body
}
// Function with no inputs/outputs
fn logHello in[] out[] {
print("Hello world");
}
// Function with typed inputs/outputs
fn multiplyBy5 in[i32 x] out[i32 y] {
x * 5 = y;
}
// Multiple inputs/outputs
fn vectorOp in[i32 a, i32 b] out[i32 sum, i32 product] {
a + b = sum;
a * b = product;
}
i32 value = 4;
i32 result;
// Calling a function
multiplyBy5 in[value] out[result];
// Nested function calls (output routing)
fn square in[i32 x] out[i32 y] {
x * x = y;
}
i32 finalValue;
square in[multiplyBy5 in[value] out] out[finalValue];
Variables inside functions are sandboxed by default:
fn foo in[] out[] {
i32 counter = 0;
fn bar in[] out[] {
counter++; // ERROR: counter is out of scope!
}
}
To share variables, use explicit declarations:
fn foo in[] out[] {
@shared i32 counter = 0;
fn bar in[] out[] {
counter++; // Legal, explicitly shared
}
}
in[]
and out[]
syntax2^64
(configurable to 2^32
or 2^16
or 2^8
per function) This avoids the halting problem.// JavaScript surprise mutation
function foo() {
let x = 0;
function bar() { x++; }
bar();
return x; // x modified unexpectedly
}
Mercury eliminates this issue:
foo {
i32 x = 0;
bar {
x++; // ERROR: x is not in scope
}
bar();
}
// Rust borrow checker issues
fn foo() {
let mut x = 0;
let bar = || x += 1;
bar();
}
Mercury simplifies ownership to:
foo {
@mut i32 x = 0;
bar {
x++; // Allowed since x is @mut
}
}
Mercury allows direct routing of function outputs into another function's inputs using a special bracket syntax.
Function outputs can be directly routed to another function's inputs by omitting the output brackets:
// Standard function call:
foo in[] out[];
// Nested function call (output routing):
bar in[foo in[x] out] out[];
Key Rule: When you remove the []
from a function's out
declaration, its output will be placed in the input block of the containing function call instead of creating its own output.
fn double in[i32 num] out[i32 result] {
num * 2 = result;
}
fn printNumber in[i32 val] out[] {
print(val);
}
// Chained call:
printNumber in[double in[5] out] out[];
This executes double(5)
and automatically passes the result to printNumber
.
Chained outputs must match the expected input type exactly:
fn A out[i32] { ... }
fn B in[str] { ... }
B in[A out ] out[]; // COMPILE ERROR: i32 ≠ str
When a function returns multiple values but you only need specific ones:
// Function with multiple outputs
fn getData in[] out[i32 count, str name, f64 price] {
// ... implementation ...
}
// Call while ignoring 'price'
i32 items;
str title;
getData in[] out[count=items, name=title, ~ignore_f64price];
// Same function, only using 'name'
str productName;
getData in[] out[~var_ignore_except[name], name=productName];
Syntax | Alternatives | Purpose |
---|---|---|
~ignore_i32var |
~IGN_VN_ , ~Vign_ |
Ignore specific output variable |
~var_ignore_except[a,b] |
~FVIWE_ , ~VignEx_ |
Ignore all outputs except specified |
a simple example of a situation in which you'll use this
bar in[] out[a,b];
you want to route bar's outputs to foo. they have the same datatypes but different names oh no! what do i do if i want to route x to b and y to a? simply use a dataflow routing OR a explicit transfer statement like so
foo in[bar in[] bar out (a=y,b=x)];
As you can see here, we take a from bar and set the y input of foo to it. it's very simple. to indicate the statement, simply enclose it in a parenthesis to show the conversion statement, simular to how parenthesis are evaluated first in mathematics. this is one of the only uses of parenthesis in mercury.
Important Notes:
fn process in[i32 width, i32 height] out[] { /* ... */ }
fn getDims in[] out[i32 w, i32 h] { /* ... */ }
// Route outputs to inputs with different names
process in[getDims in[] out (width=h, height=w)] out[];
FlowMap DimsToProcess {
width = h, // simular to getDims.h -> process.width in c++
height = w // essentially getDims.w -> process.height in c++
}
// Usage with FlowMap
process in[getDims in[] out via DimsToProcess] out[];
Important Notes:
()
are only used for variable routing conversions