Skip to content

Commit 7ccc9b2

Browse files
committed
Functions & bindings
1 parent 165f22f commit 7ccc9b2

File tree

6 files changed

+197
-0
lines changed

6 files changed

+197
-0
lines changed

docs/bindings.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,42 @@ slug: /bindings
33
---
44

55
# Bindings
6+
7+
Rue is a [purely-functional](https://en.wikipedia.org/wiki/Purely_functional_programming) programming language, and doesn't directly support mutation.
8+
9+
Practically, this means a few things:
10+
11+
1. The output of a function will remain the same every time, as long as its arguments don't change
12+
2. There is no support for mutable variables
13+
3. A function cannot change the outer scope in any way
14+
15+
However, even though _mutation_ isn't supported, you can still define `let` bindings:
16+
17+
```rue
18+
fn main(num: Int) -> Int {
19+
let squared = num * num;
20+
let doubled = squared * 2;
21+
doubled + 100
22+
}
23+
```
24+
25+
In this example, we declare two bindings to make the code more readable. These can be reused multiple times to avoid calculating the same value twice or writing duplicate code.
26+
27+
## Inline Bindings
28+
29+
You can mark a `let` binding as `inline`, which forces its value to be inserted everywhere it's referenced rather than being defined a single time and reused.
30+
31+
As an example, this will insert `num * num` multiple times, rather than computing it once up front:
32+
33+
```rue
34+
fn main(num: Int) -> Int {
35+
inline let squared = num * num;
36+
square + squared
37+
}
38+
```
39+
40+
This should be used sparingly, since it can come with either a performance hit, an increase in compiled output size, or both.
41+
42+
:::tip
43+
The compiler will automatically inline `let` bindings if they are only referenced a single time. You almost never need to explicitly mark them as `inline`.
44+
:::

docs/builtins.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
slug: /builtins
3+
---
4+
5+
# Builtins

docs/functions.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,143 @@ slug: /functions
33
---
44

55
# Functions
6+
7+
A [function](<https://en.wikipedia.org/wiki/Function_(computer_programming)>) is a self contained part of a program. You can break up complex code into multiple programs to make it more readable, or use recursive functions to implement looping.
8+
9+
Function items can be declared at the top level:
10+
11+
```rue
12+
fn add(a: Int, b: Int) -> Int {
13+
a + b
14+
}
15+
```
16+
17+
And they can be called by other functions:
18+
19+
```rue
20+
add(42, 34)
21+
```
22+
23+
## Main
24+
25+
The `main` function is special, in that it's the entrypoint to every compiled program. It's used to determine which symbols are used in the program, and its body is compiled verbatim (along with any functions and constants it depends on) rather than being called.
26+
27+
For example, the following program:
28+
29+
```rue
30+
fn main() -> Int {
31+
42
32+
}
33+
```
34+
35+
Will compile to the CLVM `(q . 42)`.
36+
37+
## Inline Functions
38+
39+
You can mark a function as `inline`. This will change the behavior of the compiler to insert the function body everywhere it's referenced, substituting each of the parameters with the corresponding arguments in the function call, instead of defining the function separately and calling it.
40+
41+
As an example, this will insert the parameter value `num + 10` four times, twice for each multiplication, rather than defining the function once and calling it twice:
42+
43+
```rue
44+
fn main(num: Int) -> Int {
45+
square(num + 10) + square(num + 10)
46+
}
47+
48+
inline fn square(num: Int) -> Int {
49+
num * num
50+
}
51+
```
52+
53+
This should be used sparingly, since it can come with either a performance hit, an increase in compiled output size, or both.
54+
55+
A recursive function cannot be marked as `inline`.
56+
57+
:::tip
58+
The compiler will automatically inline functions if they are referenced a single time and their parameters are referenced a single time. You almost never need to explicitly mark them as `inline`.
59+
:::
60+
61+
## Spread Syntax
62+
63+
Typically, there is a fixed number of arguments in a function. However, if you want to allow a variable number of arguments, or extend the argument list with another type, you can use the spread operator.
64+
65+
For example, the following function accepts any number of arguments:
66+
67+
```rue
68+
fn sum(...numbers: List<Int>) -> Int {
69+
if numbers is nil {
70+
0
71+
} else {
72+
numbers.first + sum(...numbers.rest)
73+
}
74+
}
75+
```
76+
77+
And can be called as such:
78+
79+
```rue
80+
let zero = sum();
81+
let added = sum(42, 34);
82+
83+
// You can spread arguments too
84+
let list = [1, 2, 3, 4, 5];
85+
let total = sum(42, ...list);
86+
```
87+
88+
It's also possible to use the spread operator with non-list types, which allows for compatibility with arbitrary functions:
89+
90+
```rue
91+
fn main(
92+
inner_puzzle: fn(...inner_solution: Any) -> List<Condition>,
93+
...inner_solution: Any,
94+
) -> List<Condition> {
95+
inner_puzzle(...inner_solution)
96+
}
97+
```
98+
99+
Another use case for this feature is extending the list of arguments with a struct:
100+
101+
```rue
102+
struct Solution {
103+
public_key: PublicKey,
104+
conditions: List<Condition>,
105+
}
106+
107+
fn main(...solution: Solution) -> List<Condition> {
108+
let message = tree_hash(solution.conditions as Any);
109+
let agg_sig = AggSigMe {
110+
public_key: solution.public_key,
111+
message: message,
112+
};
113+
[agg_sig, ...solution.conditions]
114+
}
115+
```
116+
117+
## Lambdas
118+
119+
Because functions can be passed around as values, it's sometimes useful to be able to write a function inline.
120+
121+
You can do this with a lambda:
122+
123+
```rue
124+
let adder = fn(a: Int, b: Int) => a + b;
125+
let sum = adder(42, 34);
126+
```
127+
128+
### Closures
129+
130+
Lambdas can automatically capture values that are defined in their parent scope, much like how functions in general capture other functions or constants that are defined externally.
131+
132+
Here's an example of a lambda that captures its environment in a closure:
133+
134+
```rue
135+
fn main() -> Int {
136+
let adder = create_adder(42);
137+
adder(100)
138+
}
139+
140+
fn create_adder(a: Int) -> fn(b: Int) -> Int {
141+
fn(b) => a + b
142+
}
143+
```
144+
145+
In this case, `create_adder` returns a closure which captures the value of `a` into the lambda function.

docs/operators.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
slug: /operators
3+
---
4+
5+
# Operators

docs/standard-library.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
slug: /standard-library
3+
---
4+
5+
# Standard Library

sidebars.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ const sidebars: SidebarsConfig = {
1818
"type-system/guards",
1919
],
2020
},
21+
"operators",
22+
"builtins",
23+
"standard-library",
2124
"security",
2225
],
2326
};

0 commit comments

Comments
 (0)