diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml deleted file mode 100644 index 4f031f9..0000000 --- a/.github/workflows/book.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Deploy Book on GitHub Pages -on: - push: - branches: - - master -permissions: - contents: write -jobs: - deploy: - name: Deploy Book on GitHub Pages - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install nix - uses: cachix/install-nix-action@v24 - with: - github_access_token: ${{ secrets.GITHUB_TOKEN }} - - - name: Build website - run: | - nix develop .#book --command bash -c "mdbook build book" - - - name: Deploy to GitHub pages - uses: JamesIves/github-pages-deploy-action@v4 - with: - branch: pages # The branch the action should deploy to. - folder: book/book # The folder the action should deploy. - clean: true # Automatically remove deleted files from the deployment branch. diff --git a/book/book.toml b/book/book.toml deleted file mode 100644 index c25c24c..0000000 --- a/book/book.toml +++ /dev/null @@ -1,6 +0,0 @@ -[book] -authors = ["Christian Lewe"] -language = "en" -multilingual = false -src = "src" -title = "The SimplicityHL Programming Language" diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md deleted file mode 100644 index 5c9d07f..0000000 --- a/book/src/SUMMARY.md +++ /dev/null @@ -1,14 +0,0 @@ -# Summary - -[Introduction](./introduction.md) - -# Types and Values -- [Types and Values](./type.md) -- [Type Aliases](./type_alias.md) -- [Type Casting](./type_casting.md) - -# Writing a Program -- [Let Statements](./let_statement.md) -- [Match Expression](./match_expression.md) -- [Functions](./function.md) -- [Programs](./program.md) diff --git a/book/src/function.md b/book/src/function.md deleted file mode 100644 index 7d41c2a..0000000 --- a/book/src/function.md +++ /dev/null @@ -1,105 +0,0 @@ -# Functions - -Functions are defined and called [just like in Rust](https://doc.rust-lang.org/std/keyword.fn.html). - -```rust -fn add(x: u32, y: u32) -> u32 { - let (carry, sum): (bool, u32) = jet::add_32(x, y); - match carry { - true => panic!(), // overflow - false => {}, // ok - }; - sum -} -``` - -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`. - -The body of the function is a block expression `{ ... }` that is executed from top to bottom. -The function returns on the final line _(note the missing semicolon `;`)_. -In the above example, `x` and `y` are added via the `add_32` jet. -The function then checks if the carry is true, signaling an overflow, in which case it panics. -On the last line, the value of `sum` is returned. - -The above function is called by writing its name `add` followed by a list of arguments `(40, 2)`. -Each parameter needs an argument, so the list of arguments is as long as the list of parameters. -Here, `x` is assigned the value `40` and `y` is assigned the value `2`. - -```rust -let z: u32 = add(40, 2); -``` - -## No early returns - -SimplicityHL has no support for an early return via a "return" keyword. -The only branching that is available is via [match expressions](./match_expression.md). - -## No recursion - -SimplicityHL has no support for recursive function calls. -A function can be called inside a function body if it has been defined before. -This means that a function cannot call itself. -Loops, where `f` calls `g` and `g` calls `f`, are also impossible. - -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. - -```rust -fn level_0() -> u32 { - 0 -} - -fn level_1() -> u32 { - let (_, next) = jet::increment_32(level_0()); - next -} - -fn level_2() -> u32 { - let (_, next) = jet::increment_32(level_1()); - next -} -``` - -## Order matters - -If function `g` calls function `f`, then `f` **must** be defined before `g`. - -```rust -fn f() -> u32 { - 42 -} - -fn g() -> u32 { - f() -} -``` - -## Main function - -The `main` function is the entry point of each SimplicityHL program. -Running a program means running its `main` function. -Other functions are called from the `main` function. - -```rust -fn main() { - // ... -} -``` - -The `main` function is a reserved name and must exist in every program. -SimplicityHL programs are always "binaries". -There is no support for "libraries". - -## Jets - -Jets are predefined and optimized functions for common use cases. - -```rust -jet::add_32(40, 2) -``` - -Jets live inside the namespace `jet`, which is why they are prefixed with `jet::`. -They can be called without defining them manually. - -It is usually more efficient to call a jet than to manually compute a value. - -[The jet documentation](https://docs.rs/simfony-as-rust/latest/simfony_as_rust/jet/index.html) lists each jet and explains what it does. diff --git a/book/src/introduction.md b/book/src/introduction.md deleted file mode 100644 index 9610e60..0000000 --- a/book/src/introduction.md +++ /dev/null @@ -1,7 +0,0 @@ -# Introduction - -SimplicityHL is a high-level language for writing Bitcoin smart contracts. -In other words, SimplicityHL is a language for expressing spending conditions of UTXOs on the Bitcoin blockchain. - -SimplicityHL looks and feels like [Rust](https://www.rust-lang.org/). -Developers write SimplicityHL, Bitcoin full nodes run Simplicity. diff --git a/book/src/let_statement.md b/book/src/let_statement.md deleted file mode 100644 index 0950953..0000000 --- a/book/src/let_statement.md +++ /dev/null @@ -1,68 +0,0 @@ -# Let Statement - -Variables are defined in let statements, [just like in Rust](https://doc.rust-lang.org/std/keyword.let.html). - -```rust -let x: u32 = 1; -``` - -The above let statement defines a variable called `x`. -The variable is of type `u32` and it is assigned the value `1`. - -```rust -let x: u32 = f(1337); -``` - -Variables can be assigned to the output value of any expression, such as function calls. - -## Explicit typing - -In SimplicityHL, the type of a defined variable **always** has to be written. -This is different from Rust, which has better type inference. - -## Immutability - -SimplicityHL variables are **always** immutable. -There are no mutable variables. - -## Redefinition and scoping - -The same variable can be defined twice in the same scope. -The later definition overrides the earlier definition. - -```rust -let x: u32 = 1; -let x: u32 = 2; -assert!(jet::eq_32(x, 2)); // x == 2 -``` - -Normal scoping rules apply: -Variables from outer scopes are available inside inner scopes. -A variable defined in an inner scope shadows a variable of the same name from an outer scope. - -```rust -let x: u32 = 1; -let y: u32 = 2; -let z: u32 = { - let x: u32 = 3; - assert!(jet::eq_32(y, 2)); // y == 2 - x -}; -assert!(jet::eq_32(z, 3)); // z == 3 -``` - -## Pattern matching - -There is limited pattern matching support inside let statements. - -```rust -let (x, y, _): (u8, u16, u32) = (1, 2, 3); -let [x, _, z]: [u32; 3] = [1, 2, 3]; -``` - -In the first line, the tuple `(1, 2, 3)` is deconstructed into the values `1`, `2` and `3`. -These values are assigned to the variable names `x`, `y` and `_`. -The variable name `_` is a special name that ignores its value. -In the end, two variables are created: `x: u32 = 1` and `y: u16 = 2`. - -Similarly, arrays can be deconstructed element by element and assigned to a variable each. diff --git a/book/src/match_expression.md b/book/src/match_expression.md deleted file mode 100644 index 5e2e2e0..0000000 --- a/book/src/match_expression.md +++ /dev/null @@ -1,95 +0,0 @@ -# Match Expression - -A match expression conditionally executes code branches. -Which branch is executed depends on the input to the match expression. - -```rust -let result: u32 = match f(42) { - Left(x: u32) => x, - Right(x: u16) => jet::left_pad_low_16_32(x), -}; -``` - -In the above example, the output of the function call `f(42)` is matched. -`f` returns an output of type `Either`. -If `f(42)` returns a value that matches the pattern `Left(x: u32)`, then the first match arm is executed. -This arm simply returns the value `x`. -Alternatively, if `f(42)` returns a value that matches the pattern `Right(x: u16)`, then the second match arm is executed. -This arm extends the 16-bit number `x` to a 32-bit number by padding its left with zeroes. -Because of type constraints, the output of `f` must match one of these two patterns. -The whole match expression returns a value of type `u32`, from one of the two arms. - -## Explicit typing - -In SimplicityHL, the type of variables inside match arms must **always** be written. -This is different from Rust, which has better type inference. - -## Pattern matching - -There is limited support for pattern matching inside match expressions. - -Boolean values can be matched. -The Boolean match expression is the replacement for an "if-then-else" in SimplicityHL. - -```rust -let bit_flip: bool = match false { - false => true, - true => false, -}; -``` - -Optional values can be matched. -The `Some` arm introduces a variable which must be explicitly typed. - -```rust -let unwrap_or_default: u32 = match Some(42) { - None => 0, - Some(x: u32) => x, -}; -``` - -Finally, `Either` values can be matched. -Again, variables that are introduced in match arms must be explicitly typed. - -```rust -let map_either: u32 = match Left(1337) { - Left(x: u32) => f(x), - Right(y: u32) => f(y), -}; -``` - -Match expressions don't support further pattern matching, in contrast to Rust. - -```rust -let unwrap_or_default: u32 = match Some((4, 2)) { - None => 0, - // this doesn't compile - Some((y, z): (u16, u16)) => <(u16, u16)>::into((y, z)), -}; -``` - -However, the match arm can contain code that performs the deconstruction. -For example, the tuple `x` of type `(u16, u16)` can be deconstructed into two integers `y` and `z` of type `u16`. - -```rust -let unwrap_or_default: u32 = match Some((4, 2)) { - None => 0, - Some(x: (u16, u16)) => { - let (y, z): (u16, u16) = x; - <(u16, u16)>::into((y, z)) - } -}; -``` - -The match arm can also contain match expressions for further deconstruction. -For example, the sum value `x` of type `Either` can be matched as either `Left(y: u32)` or `Right(z: u32)`. - -```rust -let unwrap_or_default: u32 = match Some(Left(42)) { - None => 0, - Some(x: Either) => match x { - Left(y: u32) => y, - Right(z: u32) => z, - }, -}; -``` diff --git a/book/src/program.md b/book/src/program.md deleted file mode 100644 index dfb6ec1..0000000 --- a/book/src/program.md +++ /dev/null @@ -1,25 +0,0 @@ -# Programs - -A SimplicityHL program consists of a `main` [function](./function.md). - -A program may also have [type aliases](./type_alias.md) or custom [function definitions](./function.md). -The `main` function comes last in the program, because everything it calls must be defined before it. - -```rust -type Furlong = u32; -type Mile = u32; - -fn to_miles(distance: Either) -> Mile { - match distance { - Left(furlongs: Furlong) => jet::divide_32(furlongs, 8), - Right(miles: Mile) => miles, - } -} - -fn main() { - let eight_furlongs: Either = Left(8); - let one_mile: Either = Right(1); - assert!(jet::eq_32(1, to_miles(eight_furlongs))); - assert!(jet::eq_32(1, to_miles(one_mile))); -} -``` diff --git a/book/src/type.md b/book/src/type.md deleted file mode 100644 index faf7f48..0000000 --- a/book/src/type.md +++ /dev/null @@ -1,170 +0,0 @@ -# Types and Values - -SimplicityHL mostly uses a subset of Rust's types. -It extends Rust in some ways to make it work better with Simplicity and with the blockchain. - -## Boolean Type - -| Type | Description | Values | -|--------|-----------------|-----------------| -| `bool` | Boolean | `false`, `true` | - -Values of type `bool` are truth values, which are either `true` or `false`. - -## Integer Types - -| Type | Description | Values | -|--------|-----------------|--------------------------------------------------------| -| `u1` | 1-bit integer | `0`, `1` | -| `u2` | 2-bit integer | `0`, `1`, `2`, `3` | -| `u4` | 4-bit integer | `0`, `1`, …, `15` | -| `u8` | 8-bit integer | `0`, `1`, …, `255` | -| `u16` | 16-bit integer | `0`, `1`, …, `65535` | -| `u32` | 32-bit integer | `0`, `1`, …, `4294967295` | -| `u64` | 64-bit integer | `0`, `1`, …, `18446744073709551615` | -| `u128` | 128-bit integer | `0`, `1`, …, `340282366920938463463374607431768211455` | -| `u256` | 256-bit integer | `0`, `1`, …, 2256 - 1[^u256max] | - -Unsigned integers range from 1 bit to 256 bits. -[`u8`](https://doc.rust-lang.org/std/primitive.u8.html) to [`u128`](https://doc.rust-lang.org/std/primitive.u128.html) are also supported in Rust. -`u1`, `u2`, `u4` and `u256` are new to SimplicityHL. -Integer values can be written in decimal notation `123456`, binary notation[^bin] `0b10101010` or hexadecimal notation[^hex] `0xdeadbeef`. -There are no signed integers. - -[^u256max]: The maximal value of type `u256` is `115792089237316195423570985008687907853269984665640564039457584007913129639935`. -[^bin]: The number of bits must be equal to the bit width of the type. -[^hex]: The number of hex digits must correspond to the bit width of the type. - -## Tuple Types - -| Type | Description | Values | -|--------------|-------------|---------------------------------------------------| -| `()` | 0-tuple | `()` | -| `(A)` | 1-tuple | `(a0,)`, `(a1,)`, … | -| `(A, B)` | 2-tuple | `(a0, b0)`, `(a1, b1)`, `(a2, b2)`, `(a3, b3)`, … | -| … | … | … | -| `(A, B, …)` | n-tuple | `(a0, b0, …)`, … | - -[Tuples work just like in Rust](https://doc.rust-lang.org/std/primitive.tuple.html). - -The empty tuple `()` contains no information. -It is also called the "unit". -It is mostly used as the return type of functions that don't return anything. - -Singletons `(a0,)` must be written with an extra comma `,` to differentiate them from function calls. - -Bigger tuples `(a0, b0, …)` work like in pretty much any other programming language. -Each tuple type `(A1, A2, …, AN)` defines a sequence `A1`, `A2`, …, `AN` of types. -Values of that type must mirror the sequence of types: -A tuple value `(a1, a2, …, aN)` consists of a sequence `a1`, `a2`, …, `aN` of values, where `a1` is of type `A1`, `a2` is of type `A2`, and so on. -Tuples are always finite in length. - -> Tuples are different from arrays: -> Each element of a tuple can have a different type. -> Each element of an array must have the same type. - -## Array Types - -| Type | Description | Values | -|----------|-------------|---------------------------------------------------| -| `[A; 0]` | 0-array | `[]` | -| `[A; 1]` | 1-array | `[a0]`, `[a1]`, … | -| `[A; 2]` | 2-array | `[a0, a1]`, `[a2, a3]`, `[a4, a5]`, `[a6, a7]`, … | -| … | … | … | -| `[A; N]` | n-array | `[a0, …, aN]`, … | - -[Arrays work just like in Rust](https://doc.rust-lang.org/std/primitive.array.html). - -The empty array `[]` is basically useless, but I included it for completeness. - -Arrays `[a0, …, aN]` work like in pretty much any other programming language. -Each array type `[A; N]` defines an element type `A` and a length `N`. -An array value `[a0, …, aN]` of that type consists of `N` many elements `a0`, …, `aN` that are each of type `A`. -Arrays are always of finite length. - -> Arrays are different from tuples: -> Each element of an array must have the same type. -> Each element of a tuple can have a different type. - -## List Types - -| Type | Description | Values | -|---------------------------|---------------------|------------------------------------------------------| -| `List` | <2-list | `list![]`, `list![a1]` | -| `List` | <4-list | `list![]`, …, `list![a1, a2, a3]` | -| `List` | <8-list | `list![]`, …, `list![a1, …, a7]` | -| `List` | <16-list | `list![]`, …, `list![a1, …, a15]` | -| `List` | <32-list | `list![]`, …, `list![a1, …, a31]` | -| `List` | <64-list | `list![]`, …, `list![a1, …, a62]` | -| `List` | <128-list | `list![]`, …, `list![a1, …, a127]` | -| `List` | <256-list | `list![]`, …, `list![a1, …, a255]` | -| `List` | <512-list | `list![]`, …, `list![a1, …, a511]` | -| … | … | … | -| `List` | <2^N-list | `list![]`, …, `list![a1, …, a_{2^N - 1}]` | - -Lists hold a variable number of elements of the same type. -This is similar to [Rust vectors](https://doc.rust-lang.org/std/vec/struct.Vec.html), but SimplicityHL doesn't have a heap. -In SimplicityHL, lists exist on the stack, which is why the maximum list length is bounded. - -<2-lists hold fewer than 2 elements, so zero or one element. -<4-lists hold fewer than 4 elements, so zero to three elements. -<8-lists hold fewer than 8 elements, so zero to seven elements. -And so on. -For technical reasons, the list bound is always a power of two. -The bound 1 is not supported, because it would only allow empty lists, which is useless. - -> Lists are different from arrays: -> List values hold a variable number of elements. -> Array values hold a fixed number of elements. - -On the blockchain, you pay for every byte that you use. -If you use an array, then you pay for every single element. -For example, values of type `[u8; 512]` cost roughly as much as 512 many `u8` values. -However, if you use a list, then you only pay for the elements that you actually use. -For example, the type `List` allows for up to 511 elements. -If you only use three elements `list![1, 2, 3]`, then you pay for exactly three elements. -You **don't** pay for the remaining 508 unused elements. - -## Option Types - -| Type | Values | -|-------------|-----------------------------------| -| `Option` | `None`, `Some(a0)`, `Some(a1)`, … | - -Options represent values that might not be present. [They work just like in Rust](https://doc.rust-lang.org/std/option/index.html). - -An option type is generic over a type `A`. -The value `None` is empty. -The value `Some(a)` contains an inner value `a` of type `A`. - -In Rust, we implement options as follows. - -```rust -enum Option { - None, - Some(A), -} -``` - -## Either Types - -| Type | Values | -|----------------|--------------------------------------------------------| -| `Either` | `Left(a0)`, `Left(a1)`, …, `Right(b0)`, `Right(b1)`, … | - -Sum types represent values that are of some "left" type in some cases and that are of another "right" type in other cases. -[They work just like in the either crate](https://docs.rs/either/latest/either/enum.Either.html). -[The Result type from Rust is very similar, too](https://doc.rust-lang.org/std/result/index.html). - -A sum type is generic over two types, `A` and `B`. -The value `Left(a)` contains an inner value `a` of type `A`. -The value `Right(b)` contains an inner value `b` of type `B`. - -In Rust, we implement sum types as follows. - -```rust -enum Either { - Left(A), - Right(B), -} -``` diff --git a/book/src/type_alias.md b/book/src/type_alias.md deleted file mode 100644 index 5f23423..0000000 --- a/book/src/type_alias.md +++ /dev/null @@ -1,56 +0,0 @@ -# Type Aliases - -SimplicityHL currently doesn't support Rust-like `struct`s for organizing data. - -```rust -struct User { - active: bool, - id: u256, - sign_in_count: u64, -} -``` - -SimplicityHL programmers have to handle long tuples of unlabeled data, which can get messy. - -```rust -(bool, u256, u64) -``` - -To help with the situation, programmers can define custom type aliases. -Aliases define a new name for an existing type. -In contrast, `struct`s define an entirely new type, so aliases are different from `struct`s. -However, aliases still help us to make the code more readable. - -```rust -type User = (bool, u256, u64); -``` - -There is also a list of builtin type aliases. -These aliases can be used without defining them. - -| Builtin Alias | Definition | -|------------------|-------------------------------| -| `Amount1` | `Either<(u1, u256), u64>` | -| `Asset1` | `Either<(u1, u256), u256>` | -| `Confidential1` | `(u1, u256)` | -| `Ctx8` | `(List, (u64, u256))` | -| `Distance` | `u16` | -| `Duration` | `u16` | -| `ExplicitAmount` | `u256` | -| `ExplicitAsset` | `u256` | -| `ExplicitNonce` | `u256` | -| `Fe` | `u256` | -| `Ge` | `(u256, u256)` | -| `Gej` | `((u256, u256), u256)` | -| `Height` | `u32` | -| `Lock` | `u32` | -| `Message` | `u256` | -| `Message64` | `[u8; 64]` | -| `Nonce` | `Either<(u1, u256), u256>` | -| `Outpoint` | `(u256, u32)` | -| `Point` | `(u1, u256)` | -| `Pubkey` | `u256` | -| `Scalar` | `u256` | -| `Signature` | `[u8; 64]` | -| `Time` | `u32` | -| `TokenAmount1` | `Either<(u1, u256), u64>` | diff --git a/book/src/type_casting.md b/book/src/type_casting.md deleted file mode 100644 index ba4d10d..0000000 --- a/book/src/type_casting.md +++ /dev/null @@ -1,81 +0,0 @@ -# Casting - -A SimplicityHL type can be cast into another SimplicityHL type if both types share the same structure. -The structure of a type has to do with how the type is implemented on the Simplicity "processor". -I will spare you the boring details. - -Below is a table of types that can be cast into each other. - -| Type | Casts To (And Back) | -|----------------|------------------------------------| -| `bool` | `Either<(), ()>` | -| `Option` | `Either<(), A>` | -| `u1` | `bool` | -| `u2` | `(u1, u1)` | -| `u4` | `(u2, u2)` | -| `u8` | `(u4, u4)` | -| `u16` | `(u8, u8)` | -| `u32` | `(u16, u16)` | -| `u64` | `(u32, u32)` | -| `u128` | `(u64, u64)` | -| `u256` | `(u128, u128)` | -| `(A)` | `A` | -| `(A, B, C)` | `(A, (B, C))` | -| `(A, B, C, D)` | `((A, B), (C, D))` | -| … | … | -| `[A; 0]` | `()` | -| `[A; 1]` | `A` | -| `[A; 2]` | `(A, A)` | -| `[A; 3]` | `(A, (A, A))` | -| `[A; 4]` | `((A, A), (A, A))` | -| … | … | -| `List` | `Option` | -| `List` | `(Option<[A; 2]>, List)` | -| `List` | `(Option<[A; 4]>, List)` | -| `List` | `(Option<[A; 8]>, List)` | -| `List` | `(Option<[A; 16]>, List)` | -| `List` | `(Option<[A; 32]>, List)` | -| `List` | `(Option<[A; 64]>, List)` | -| `List` | `(Option<[A; 128]>, List)` | -| `List` | `(Option<[A; 256]>, List)` | -| … | … | - -## Casting Rules - -Type `A` can be cast into itself (reflexivity). - -If type `A` can be cast into type `B`, then type `B` can be cast into type `A` (symmetry). - -If type `A` can be cast into type `B` and type `B` can be cast into type `C`, then type `A` can be cast into type `C` (transitivity). - -## Casting Expression - -All casting in SimplicityHL happens explicitly through a casting expression. - -```rust -::into(input) -``` - -The above expression casts the value `input` of type `Input` into some output type. -The input type of the cast is explicit while the output type is implicit. - -In SimplicityHL, the output type of every expression is known. - -```rust -let x: u32 = 1; -``` - -In the above example, the meaning of the expression `1` is clear because of the type `u32` of variable `x`. -Here, `1` means a string of 31 zeroes and 1 one. -_In other contexts, `1` could mean something different, like a string of 255 zeroes and 1 one._ - -The SimplicityHL compiler knows the type of the outermost expression, and it tries to infer the types of inner expressions based on that. -When it comes to casting expressions, the compiler has no idea about the input type of the cast. -The programmer needs to supply this information by annotating the cast with its input type. - -```rust -let x: u32 = <(u16, u16)>::into((0, 1)); -``` - -In the above example, we cast the tuple `(0, 1)` of type `(u16, u16)` into type `u32`. -Feel free to consult the table above to verify that this is a valid cast.