Skip to content

Commit a0379a8

Browse files
committed
feat: Add Simfony book
1 parent 74df9c3 commit a0379a8

11 files changed

+632
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ target/
1515

1616
# Vscode project files
1717
.vscode
18+
19+
# mdbook HTML output dir
20+
book/book/

book/book.toml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[book]
2+
authors = ["Christian Lewe"]
3+
language = "en"
4+
multilingual = false
5+
src = "src"
6+
title = "The Simfony Programming Language"

book/src/SUMMARY.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Summary
2+
3+
[Introduction](./introduction.md)
4+
5+
# Types and Values
6+
- [Types and Values](./type.md)
7+
- [Type Aliases](./type_alias.md)
8+
- [Type Casting](./type_casting.md)
9+
10+
# Writing a Program
11+
- [Let Statements](./let_statement.md)
12+
- [Match Expression](./match_expression.md)
13+
- [Functions](./function.md)
14+
- [Programs](./program.md)
15+
16+
# Locking and Unlocking UTXOs

book/src/function.md

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Functions
2+
3+
Functions are defined and called [just like in Rust](https://doc.rust-lang.org/std/keyword.fn.html).
4+
5+
```rust
6+
fn add(x: u32, y: u32) -> u32 {
7+
let (carry, sum): (bool, u32) = jet::add_32(x, y);
8+
match carry {
9+
true => panic!(), // overflow
10+
false => {}, // ok
11+
};
12+
sum
13+
}
14+
```
15+
16+
The above example defines a function called `add` that takes two parameters: variable `x` of type `u32` and variable `y` of type `u32`. The function returns a value of type `u32`.
17+
18+
The body of the function is a block expression `{ ... }` that is executed from top to bottom.
19+
The function returns on the final line _(note the missing semicolon `;`)_.
20+
In the above example, `x` and `y` are added via the `add_32` jet.
21+
The function then checks if the carry is true, signaling an overflow, in which case it panics.
22+
On the last line, the value of `sum` is returned.
23+
24+
The above function is called by writing its name `add` followed by a list of arguments `(40, 2)`.
25+
Each parameter needs an argument, so the list of arguments is as long as the list of arguments.
26+
Here, `x` is assigned the value `40` and `y` is assigned the value `2`.
27+
28+
```rust
29+
let z: u32 = add(40, 2);
30+
```
31+
32+
## No early returns
33+
34+
Simfony has no support for an early return via a "return" keyword.
35+
The only branching that is available is via [match expressions](./match_expression.md).
36+
37+
## No recursion
38+
39+
Simfony has no support for recursive function calls.
40+
A function can be called inside a function body if it has been defined before.
41+
This means that a function cannot call itself.
42+
Loops, where `f` calls `g` and `g` calls `f`, are also impossible.
43+
44+
What _is_ possible are stratified function definitions, where level-0 functions depend on nothing, level-1 functions depend on level-0 functions, and so on.
45+
46+
```rust
47+
fn level_0() -> u32 {
48+
0
49+
}
50+
51+
fn level_1() -> u32 {
52+
let (_, next) = jet::increment_32(level_0());
53+
next
54+
}
55+
56+
fn level_2() -> u32 {
57+
let (_, next) = jet::increment_32(level_1));
58+
next
59+
}
60+
```
61+
62+
## Order matters
63+
64+
If function `g` calls function `f`, then `f` **must** be defined before `g`.
65+
66+
```rust
67+
fn f() -> u32 {
68+
42
69+
}
70+
71+
fn g() -> u32 {
72+
f()
73+
}
74+
```
75+
76+
## Main function
77+
78+
The `main` function is the entry point of each Simfony program.
79+
Running a program means running its `main` function.
80+
Other functions are called from the `main` function.
81+
82+
```rust
83+
fn main() {
84+
// ...
85+
}
86+
```
87+
88+
The `main` function is a reserved name and must exist in every program.
89+
Simfony programs are always "binaries".
90+
There is no support for "libraries".
91+
92+
## Jets
93+
94+
Jets are predefined and optimized functions for common usecases.
95+
96+
```rust
97+
jet::add_32(40, 2)
98+
```
99+
100+
Jets live inside the namespace `jet`, which is why they are prefixed with `jet::`.
101+
They can be called without defining them manually.
102+
103+
It is usually more efficient to call a jet than to manually compute a value.
104+
105+
[The jet documentation](https://docs.rs/simfony-as-rust/latest/simfony_as_rust/jet/index.html) lists each jet and explains what it does.

book/src/introduction.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Introduction
2+
3+
Simfony is a high-level language for writing Bitcoin smart contracts.
4+
In other words, Simfony is a language for expressing spending conditions of UTXOs on the Bitcoin blockchain.
5+
6+
Simfony looks and feels like [Rust](https://www.rust-lang.org/).
7+
Developers write Simfony, Bitcoin full nodes run Simplicity.

book/src/let_statement.md

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Let Statement
2+
3+
Variables are defined in let statements, [just like in Rust](https://doc.rust-lang.org/std/keyword.let.html).
4+
5+
```rust
6+
let x: u32 = 1;
7+
```
8+
9+
The above let statement defines a variable called `x`.
10+
The variable is of type `u32` and it is assigned the value `1`.
11+
12+
```rust
13+
let x: u32 = f(1337);
14+
```
15+
16+
Variables can be assigned to the output value of any expression, such as function calls.
17+
18+
## Explicit typing
19+
20+
In Simfony, the type of a defined variable **always** has to be written.
21+
This is different from Rust, which has better type inference.
22+
23+
## Immutability
24+
25+
Simfony variables are **always** immutable.
26+
There are no mutable variables.
27+
28+
## Redefinition and scoping
29+
30+
The same variable can be defined twice in the same scope.
31+
The later definition overrides the earlier definition.
32+
33+
```rust
34+
let x: u32 = 1;
35+
let x: u32 = 2;
36+
assert!(jet::eq_32(x, 2)); // x == 2
37+
```
38+
39+
Normal scoping rules apply:
40+
Variables from outer scopes are available inside inner scopes.
41+
A variable defined in an inner scope shadows a variable of the same name from an outer scope.
42+
43+
```rust
44+
let x: u32 = 1;
45+
let y: u32 = 2;
46+
let z: u32 = {
47+
let x: u32 = 3;
48+
assert!(jet::eq_32(y, 2)); // y == 2
49+
x
50+
};
51+
assert!(jet::eq_32(x, 3)); // z == 3
52+
```
53+
54+
## Pattern matching
55+
56+
There is limited pattern matching support inside let statements.
57+
58+
```rust
59+
let (x, y, _): (u8, u16, u32) = (1, 2, 3);
60+
let [x, _, z]: [u32; 3] = [1, 2, 3];
61+
```
62+
63+
In the first line, the tuple `(1, 2, 3)` is deconstructed into the values `1`, `2` and `3`.
64+
These values are assigned to the variable names `x`, `y` and `_`.
65+
The variable name `_` is a special name that ignores its value.
66+
In the end, two variables are created: `x: u32 = 1` and `y: u16 = 2`.
67+
68+
Similarly, arrays can be deconstructed element by element and assigned to a variable each.

book/src/match_expression.md

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Match Expression
2+
3+
A match expression conditionally executes code branches.
4+
Which branch is executed depends on the input to the match expression.
5+
6+
```rust
7+
let result: u32 = match f(42) {
8+
Left(x: u32) => x,
9+
Right(x: u16) => jet::left_pad_low_16_32(x),
10+
};
11+
```
12+
13+
In the above example, the output of the function call `f(42)` is matched.
14+
`f` returns an output of type `Either<u32, u16>`.
15+
If `f(42)` returns a value that matches the pattern `Left(x: u32)`, then the first match arm is executed.
16+
This arm simply returns the value `x`.
17+
Alternatively, if `f(42)` returns a value that matches the pattern `Right(x: u16)`, then the second match arm is executed.
18+
This arm extends the 16-bit number `x` to a 32-bit number by padding its left with zeroes.
19+
Because of type constraints, the output of `f` must match one of these two patterns.
20+
The whole match expression returns a value of type `u32`, from one of the two arms.
21+
22+
## Explicit typing
23+
24+
In Simfony, the type of variables inside match arms must **always** be written.
25+
This is different from Rust, which has better type inference.
26+
27+
## Pattern matching
28+
29+
There is limited support for pattern matching inside match expressions.
30+
31+
Boolean values can be matched.
32+
The Boolean match expression is the replacement for an "if-then-else" in Simfony.
33+
34+
```rust
35+
let bit_flip: bool = match false {
36+
false => true,
37+
true => false,
38+
};
39+
```
40+
41+
Optional values can be matched.
42+
The `Some` arm introduces a variable which must be explicitly typed.
43+
44+
```rust
45+
let unwrap_or_default: u32 = match Some(42) {
46+
None => 0,
47+
Some(x: u32) => x,
48+
};
49+
```
50+
51+
Finally, `Either` values can be matched.
52+
Again, variables that are introduced in match arms must be explicitly typed.
53+
54+
```rust
55+
let map_either: u32 = match Left(1337) {
56+
Left(x: u32) => f(x),
57+
Right(y: u32) => f(y),
58+
};
59+
```
60+
61+
Match expressions don't support further pattern matching, in contrast to Rust.
62+
63+
```rust
64+
let unwrap_or_default: u32 = match Some((4, 2)) {
65+
None => 0,
66+
// this doesn't compile
67+
Some((y, z): (u16, u16)) => <(u16, u16)>::into((y, z)),
68+
};
69+
```
70+
71+
However, the match arm can contain code that performs the deconstruction.
72+
For example, the tuple `x` of type `(u16, u16)` can be deconstructed into two integers `y` and `z` of type `u16`.
73+
74+
```rust
75+
let unwrap_or_default: u32 = match Some((4, 2)) {
76+
None => 0,
77+
Some(x: (u16, u16)) => {
78+
let (y, z): (u16, u16) = x;
79+
<(u16, u16)>::into((y, z))
80+
}
81+
};
82+
```
83+
84+
The match arm can also contain match expressions for further deconstruction.
85+
For example, the sum value `x` of type `Either<u32, u32>` can be matched as either `Left(y: u32)` or `Right(z: u32)`.
86+
87+
```rust
88+
let unwrap_or_default: u32 = match Some(Left(42)) {
89+
None => 0,
90+
Some(x: Either<u32, u32>) => match x {
91+
Left(y: u32) => y,
92+
Right(z: u32) => z,
93+
},
94+
};
95+
```

book/src/program.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Programs
2+
3+
A Simfony program consists of a `main` [function](./function.md).
4+
5+
A program may also have [type aliases](./type_alias.md) or custom [function definitions](./function.md).
6+
The `main` function comes last in the program, because everything it calls must be defined before it.
7+
8+
```rust
9+
type Furlong = u32;
10+
type Mile = u32;
11+
12+
fn to_miles(distance: Either<Furlong, Mile>) -> Mile {
13+
match distance {
14+
Left(furlongs: Furlong) => jet::divide_32(furlongs, 8),
15+
Right(miles: Mile) => miles,
16+
}
17+
}
18+
19+
fn main() {
20+
let eight_furlongs: Either<Furlong, Mile> = Left(8);
21+
let one_mile: Either<Furlong, Mile> = Right(1);
22+
assert!(jet::eq_32(1, to_miles(eight_furlongs)));
23+
assert!(jet::eq_32(1, to_miles(one_mile)));
24+
}
25+
```

0 commit comments

Comments
 (0)