# Syntax
This file provides a reference of each possible syntax of bend programming language.
Click [here](#imp-syntax) to see the syntax for "imp", the variant of bend that looks like an imperative language like python.
Click [here](#fun-syntax) to see the syntax for "fun", the variant of bend that looks like a functional language like Haskell or ML.
Click [here](#import-syntax) to see the import syntax.
Click [here](#comments) to see the syntax for commenting code.
Click [here](#imp-type-syntax) to see the imperative type syntax.
Click [here](#fun-type-syntax) to see the functional type syntax.
Both syntaxes can be mixed in the same file like the example below:
```python
object Point { x, y }
type Bool:
True
False
#{
The identity function is a function that always returns the value that
was used as its argument.
#}
def identity(x):
return x
main =
let result = (identity 41)
(+ result 1)
```
<div id="imp-syntax"></div>
# Imp Syntax
## Top-level definitions
### Def
Defines a top level function.
```python
def add(x: u24, y: u24) -> u24:
result = x + y
return result
def unchecked two() -> u24:
return 2
def main() -> u24:
return add(40, two)
```
A function definition is composed by a name, a sequence of parameters and a body.
A top-level name can be anything matching the regex `[A-Za-z0-9_.-/]+`, except it can't have `__` (used for generated names) or start with `//`.
The last statement of each function must either be a `return` or a selection statement (`if`, `switch`, `match`, `fold`)
where all branches `return`.
Each parameter of the function can receive a type annotation with `param_name: type` and the return value of the function can also be annotated with `def fn_name(args) -> return_type:`.
We can force the type-checker to run or not on a specific function by adding `checked` or `unchecked` between `def` and the function name.
### Type
Defines an algebraic data type.
```python
type Option:
Some { value }
None
type Tree(T):
Node { value: T, ~left: Tree(T), ~right: Tree(T) }
Leaf
```
Type names must be unique, and should have at least one constructor.
For a generic or polymorphic type, all type variables used in the constructors must be declared first in the type definition with `type Name(type_var1, ...):`
Each constructor is defined by a name followed by its fields. The fields can be annotated with types that will be checked when creating values of that type.
The `~` notation indicates a recursive field. To use `fold` statements with a type its recursive fields must be correctly marked with `~`.
The constructor names inherit the name of their types and become functions (`Tree/Node` and `Tree/Leaf` in this case).
The exact function they become depends on the encoding.
Read [defining data types](./defining-data-types.md) to know more.
### Object
Defines a type with a single constructor (like a struct, a record or a class).
```python
object Pair(A, B) { fst: A, snd: B }
object Function(T) { name: String, args, body: T }
object Vec { len, data }
```
The constructor created from this definition has the same name as the type.
Since it only has one constructor, `fold`ing a recursive `object` requires some additional stop condition apart from pattern matching on the value itself (like an `if` statement).
## Statements
### Assignment
```python
value = 2
return value
(first, second) = (1, 2)
return second
{x y} = {2 3}
```
Assigns a value to a variable.
It's possible to assign to a pattern, like a tuple or superposition, which will destructure the value returned by the expression.
```python
(first, second) = (1, 2)
first, second = 1, 2
```
### Use
```rust
use x = 2 + 3
return x + x
```
Inline copies of the declared bind, it is equivalent to this code:
```rust
return ((2 + 3) + (2 + 3))
```
### In-Place Operation
```python
x += 1
return x
```
The in-place operation does an infix operation and re-assigns a variable.
The operations are:
- Addition `+=`
- Subtraction `-=`
- Multiplication `*=`
- Division `/=`
- Bit And `&=`
- Bit Or `|=`
- Bit Xor `^=`
- Mapper `@=`
The mapper in-place operation applies a function and re-assigns the variable:
```python
x = "hello"
x @= String/uppercase
```
### Return
```python
return "hello"
```
Returns the expression that follows. The last statement of each branch of a function must be a `return`.
```py
# Allowed, all branches return
def max(a, b):
if a > b:
return a
else:
return b
```
```py
# Not allowed, early return
def Foo(x):
if test_condition(x):
return "err"
else:
y = map(x)
return y
```
```py
# Not allowed, one of the branches doesn't return
def Foo(a, b):
if a < b:
return a
else:
c = a + b
```
### If
```python
if condition:
return 0
else:
return 1
```
A branching statement where `else` is mandatory.
The condition must return a `u24` number, where 0 will run the `else` branch and any other value will return the first one.
It is possible to make if-chains using `elif`:
```python
if condition1:
return 0
elif condition2:
return 1
elif condition3:
return 2
else:
return 3
```
The conditions are evaluated in order, one by one, stopping at the first successful case.
### Switch
```python
switch x = 5:
case 0:
return 6
case 1:
return 7
case _:
return x-2
```
A switch binds a variable name to the result of a given condition and branches to the case matching its value. Cases
must be listed from least to greatest, beginning with `0` and incrementing by 1. The last case must be `_`, which
catches all values not explicitly enumerated. Switches may only be used with native numbers values.
In the last case, the predecessor value is available with the name `bound_var-next_num`, where `bound_var` is the variable
set by the condition and `next_num` is the expected value of the next case. For example, the above example code returns
`3`, since `x-2` is bound to `5 - 2` and the value of `x` doesn't match any explicit case.
This switch statement is equivalent to the `if` from the previous section:
```python
switch _ = condition:
case 0:
# else branch
return 1
case _:
# then branch
return 0
```
### Match
```python
match x = Option/none:
case Option/some:
y = x.value
case Option/none:
y = 0
```
A pattern matching statement, the cases must be the constructor names of the matching value.
It is possible to bind a variable name to the matching value. The fields of the matched constructor are bound to `matched_var.field_name`.
### Fold
```python
fold x = Tree/Leaf:
case Tree/Node:
return x.value + x.left + x.right
case Tree/Leaf:
return 0
```
A fold statement. Reduces the given value with the given match cases.
It is possible to bind a variable name to the matching value. Just like in `match`, the fields are bound to `matched_var.field_name`.
For fields notated with `~` in the type definition, the fold function is called implicitly.
It is equivalent to the inline recursive function:
```python
def fold(x: Tree(u24)) -> u24:
match x:
case Tree/Node:
return x.value + fold(x.left) + fold(x.right)
case Tree/Leaf:
return 0
...
fold(Tree/Leaf)
```
### Bend
Bend can be used to create recursive data structures:
```rust
bend x = 0:
when x < 10:
left = fork(x + 1)
right = fork(x + 1)
y = Tree/Node(left, right)
else:
y = Tree/Leaf(x)
```
Which binds a variable to the return of an inline recursive function.
The function `fork` is available inside the `when` arm of the `bend` and calls it recursively.
It is possible to pass multiple state variables, which can be initialized:
```python
bend x = 1, y = 2 ...:
when condition(x, y, ...):
...
```
When calling `fork`, the function must receive the same number of arguments as the number of state variables.
It is equivalent to this inline recursive function:
```python
def bend(x, y, ...):
if condition(x, y, ...):
...
return ... bend(x, y, ...) ...
else:
return ...
```
### Open
```python
p = Point { x: 1, y: 2 }
open Point: p
return Point { x: p.x * p.x, y: p.y * p.y }
```
Brings the inner fields of an object into scope. The original variable can still be accessed, but doing so will cause any used fields to be duplicated.
It's equivalent to pattern matching on the object, with the restriction that its type must have only one constructor.
```python
open Point: p
...
# Equivalent to:
match p:
Point:
...
```
### With block
```python
with Result:
x <- safe_div(2, 0)
return x
```
A monadic `with` block.
Where `x <- ...` performs a monadic operation.
Expects `Result` to be a type defined with `type` or `object` and the function `Result/bind` to be defined.
The monadic bind function should be of type `(Result a) -> (a -> Result b) -> Result b`, like this:
```python
def Result/bind(res, nxt):
match res:
case Result/Ok:
nxt = undefer(nxt)
return nxt(res.value)
case Result/Err:
return res
```
However, the second argument, `nxt`, is actually a deferred call to the continuation, passing any free variables as arguments.
Therefore, all `bind` functions must call the builtin function `undefer` before using the value of `nxt`, as in the example above.
This is necessary to ensure that the continuation in recursive monadic functions stays lazy and doesn't expand infinitely.
This is an example of a recursive function that would loop if passing the variable `a` to the recursive call `Result/foo(a, b)` was not deferred:
```python
def Result/foo(x, y):
with Result:
a <- Result/Ok(1)
if b:
b = Result/Err(x)
else:
b = Result/Ok(y)
b <- b
return Result/foo(a, b)
```
Other statements are allowed inside the `with` block and it can both return a value at the end and bind a variable, like branching statements do.
```python
# Also ok:
with Result:
x <- safe_div(2, 0);
y = x
return y
```
The name `wrap` is bound inside a `with` block as a shorthand for `Type/wrap`,
and it calls the unit function of the monad, also called `pure` in some languages:
```python
def Result/wrap(x):
return Result/Ok(x)
with Result:
x <- some_operation(...)
y <- some_operation(...)
return wrap(x * y)
```
### Def
Creates a local function visible in the current block capturing variables:
```python
def main() -> _:
y = 41
x = 1
def aux_add(x):
return x + y
return aux_add(x)
```
## Expressions
### Variables
```python
some_var
foo/bar
```
A variable can be anything matching the regex `[A-Za-z0-9_.-/]+` but with some restrictions:
- It can not start with `//`
- It can not contain `__`
A variable is a name for some immutable expression. It is possible to rebind variables with the same name.
```python
x = 1
x = x + 1
```
Note that `-` is also used for negative numbers and as the numeric operator. Bend's grammar is greedily parsed from left to right, meaning that `x-3` always represents a name and not `x - 3` or a sequence of expressions like in `[x -3]`.
### Lambdas
```python
lambda x: x
lambda x, y: y
λx y: x
```
Lambdas represents anonymous inline functions, it can bind a variable and has an expression as body.
Using `,` is optional.
### Unscoped Lambdas and Variables
```python
lambda $x: $x
λ$x $y: $x
```
Like lambdas, with the exception that the variable starts with a `$` sign. Every unscoped variable in a function must have a unique name and must be used exactly once.
Unscoped variables are not transformed and linearized like normal scoped variables.
Read [using scopeless lambdas](/docs/using-scopeless-lambdas.md) to know more about their behavior.
### Function Call
```python
callee(arg_1, arg_2, arg_n)
```
A call is written with a callee followed by a list of arguments. Arguments can be optionally separated by `,`.
The effect of a function call is to substitute the callee with it's body and replace the arguments by the passed variables.
The called function can be any expression and it supports partial applications.
Optionally, if you call a function by its name, you can used named arguments:
```python
callee(expr1, expr2, arg4 = expr3, arg3 = expr4)
```
In case named arguments are used, they must come after the positional arguments and the function must be called with exactly the number of arguments of its definition.
### Eraser
```python
*
eraser = *
*(41 + 1) # applies 41 + 1 to `*` erasing the number and returns `*`
* = 41 + 1 # erases 41 + 1
```
The effect of an eraser is to free memory. Erasers behave like a `null`.
It's impossible to compare or match eraser values.
It is implicitly inserted for variables that have not been used:
```python
def constant(x):
return 8345
```
### Tuple
```python
(3, 9)
```
A Tuple is surrounded by `(` `)` and should contain 2 or more elements. Elements are separated by `,`.
### Superposition
```python
{1 2 3}
```
A superposition of values is defined using `{` `}` with at least 2 expressions inside. Elements can be optionally separated by `,`.
Read [sups and dups](./dups-and-sups.md) to know more.
### Numbers and Infix Operations
Currently, bend supports 3 types of numbers: floats, integers and unsigned integers. All of then are 24 bit sized.
```python
f24 = +88.012
i24 = -42
u24 = 42
```
Currently, We can't write operations that mix two types of number but we can explicitly convert between them.
| Addition | x + y | int, float, uint |
| Subtraction | x - y | int, float, uint |
| Multiplication | x \* y | int, float, uint |
| Division | x / y | int, float, uint |
| Remainder | x % y | int, float, uint |
| Exponentiation | x \*\* y | float |
| Equal | x == y | int, float, uint |
| Not Equal | x != y | int, float, uint |
| Less Than | x < y | int, float, uint |
| Greater Than | x > y | int, float, uint |
| Less Than or Equal | x <= y | int, float, uint |
| Greater Than or Equal | x >= y | int, float, uint |
| Bitwise And | x & y | int, uint |
| Bitwise Or | x \| y | int, uint |
| Bitwise Xor | x ^ y | int, uint |
| Bitwise Right Shift | x >> y | uint |
| Bitwise Left Shift | x << y | uint |
Hexadecimal and binary floating-point literals are also supported.
In these representations, each digit after the point is divided according to the base’s power of the digit's position.
Specifically, for hexadecimal floating-point numbers, each place after the dot represents a fraction of 16 to the power of the digit's depth.
Similarly, for binary floating-point numbers, each place after the dot represents a fraction of 2 to the power of the digit's depth.
```python
0xA.A == 10.625
0b111.111 == 7.875
```
### Constructor Literals
Constructors are just functions.
A Constructor expression is equivalent to calling a Constructor function, they have 2 syntaxes:
```python
# Constructor syntax, requires all field names
Type/Ctr { field1: 4, field2: 8 }
# Function syntax
Type/Ctr(field1 = 4, field2 = 8)
Type/Ctr(4, field2 = 8)
Type/Ctr(4, 8)
Type/Ctr(4) # Can be partially applied if not using named arguments
```
### Character Literal
```python
'x'
```
A Character is surrounded with `'`. Accepts unicode characters, unicode escapes in the form '\u{hex value}' and is desugared to the unicode codepoint as an `u24`.
Only supports unicode codepoints up to `0xFFFFFF`.
### Symbol Literal
```python
# Becomes 2146 (33 << 6 + 34)
`hi`
```
A Symbol encodes a up to 4 base64 characters as a `u24` number. It is surrounded by `\``.
Empty characters are interpreted as `A` which has value 0, meaning that `B` is the same as `AAAB`.
### String Literal
```python
"Hello, World!"
```
A String literal is surrounded with `"`. Accepts the same values as characters literals.
It is desugared to constructor calls of the built-in type String, `String/cons(head, ~tail)` and `String/nil` .
### List Literal
```python
[1, 2, "three"]
```
A List literal is surrounded by `[` `]`. The elements must be separated by `,`.
It is desugared to constructor calls of the built-in type List, `List/cons(head, ~tail)` and `List/nil` .
### Tree Literals
```python
![![1, 2], ![3, 4]]
```
The Tree literals `![]` and `!` are used to create values of the built-in type `Tree`.
`![a b]` is equivalent to `Tree/Node(a, b)`.
`!x` is equivalent to `Tree/Leaf(x)`.
### Map Literals
```python
{ 0: 4, `hi`: "bye", 'c': 2 + 3 }
x[0] = 5 # Assigns the key 0 to the value 5
return x[0] # Gets the value of the key 0
```
Bend has a built-in binary tree map data structure where the key is a `u24`, meaning you can use numbers, characters, and symbols as keys.
### List Comprehension
```python
[x + 1 for x in list]
[x + 1 for x in list if x > 2]
```
A List Comprehension generates a new list, it can be extracted in 3 parts.
`[expression . iterator . condition]`
Expression: The expression to be performed in the iterator element.
Iterator: Binds a name to the list elements.
Condition: Optional, is used to filter the list elements.
It is desugared to a fold statement:
```python
fold list:
List/cons:
if condition:
List/cons(list.head, list.tail)
else:
list.tail
List/nil:
List/nil
```
<div id="fun-syntax"></div>
# Fun Syntax
## Top-level definitions
```rust
type Name
= (Ctr1 arg1 arg2)
| Ctr2
Name (Ctr1 sub_arg1 sub_arg2) arg3 = rule0_body
Name Ctr2 arg3 = rule1_body
```
A top-level name can be anything matching the regex `[A-Za-z0-9_.-/]+`, except it can't have `__` (used for generated names) or start with `//`.
### Function Definitions
A function definition is composed of a sequence of pattern matching equations.
Each rule is the name of the function, a sequence of patterns and then the body.
```rust
identity x = x
(Bool.neg True) = False
(Bool.neg False) = True
MapMaybe (Some val) f = (Some (f val))
MapMaybe None f = None
Pair.get (fst, snd) f = (f fst snd)
```
A rule pattern can be:
- A variable.
- A number.
- A constructor.
- A tuple.
- A superposition.
- A wildcard `*`.
And the builtin types that desugar to one of the above:
- A list (becomes a constructor).
- A string (becomes a constructor).
- A natural number (becomes a constructor).
- A character (becomes a number).
- A symbol (becomes a number);
Unscoped variables can't be defined in a rule pattern.
The rule body is a term, there are no statements in the Fun variant of Bend.
Read [pattern matching](./pattern-matching.md) to learn about what exactly the rules for pattern matching equations are.
### Type
Defines an Algebraic Data Type, it should have at least one constructor.
```rust
type Tree
= (Leaf value)
| (Node ~left ~right)
| Nil
```
`Tree` is the ADT name and it should be unique, except that it can be used once by a constructor name.
Each constructor is defined by a name followed by its fields. The `~` notation describes a recursive field.
The constructors inherit the name of their types and become functions (`Tree/Node` and `Tree/Leaf` in this case).
## Terms
### Variables
A variable can be anything matching the regex `[A-Za-z0-9_.-/]+` but with some restrictions:
- It can not start with `//`
- It can not contain `__`
A variable is a name for some immutable expression. It is possible to rebind variables with the same name.
```rust
let x = 1
let x = (+ x 1)
```
### Lambda
```rust
@x x
λx x
λ(fst, snd) snd
λ{x y} x
```
Lambdas represents anonymous inline functions, it can be written with `λ` or `@` followed by a pattern and a term.
A tuple or duplication pattern is equivalent to a lambda followed by a `let`.
```rust
λ(fst, snd) snd
λa let (fst, snd) = a; snd
λ{x y} (x y)
λa let {x y} = a; (x y)
```
### Unscoped Variables
```rust
λ$x $x
```
Like a normal scoped variable, but starts with a `$` sign. Every unscoped variable in a function must have a unique name and must be used exactly once.
They can be defined anywhere a scoped variable would be defined in a term, like in a lambda or a `let`.
Unscoped variables are not transformed and linearized like normal scoped variables.
Read [using scopeless lambdas](/docs/using-scopeless-lambdas.md) to know more about.
### Application
```rust
(fun arg_1 arg_2 ... arg_n)
```
An application is surrounded by `(` `)`, written in lisp style.
> Lambdas have a higher precedence, so `(@x x 1)` and `((@x x) 1)` means the same thing.
### Tuples
```rust
(1, 2, 3)
```
A tuple is surrounded by `(` `)`, with the difference that it's elements are separated by `,`.
### Superposition
```rust
{1 2 3}
```
A superposition of values is defined using `{` `}` with at least 2 terms inside.
Read [sups and dups](./dups-and-sups.md) to know more.
### Let-bindings
```rust
let x = (+ 1 2)
x
let (fst, snd, era) = (1, 2, *);
(+ fst snd)
let {f1 f2} = λx x;
(f1 f2)
let $x = (some_fn $x);
*
```
> `*` is an eraser term.
A let term uses a pattern, it can be:
- A variable / unscoped variable.
- A tuple.
- A superposition.
The let term will expects a binding value followed by a `next` term.
Using `;` is optional.
### Use
```rust
use x = (+ 2 3)
(+ x x)
```
Inline copies of the declared bind, it is equivalent to this code:
```rust
(+ (+ 2 3) (+ 2 3))
```
### Switch
```rust
switch n {
0: "zero"
1: "one"
_: "greater than 1"
}
switch x = (+ 1 1) {
0: 42;
_: x-1;
}
```
A switch for native numbers, it can hold a name binding if the matching term is not a variable.
The cases need to be typed from `0` to a wildcard `_` in sequence.
In the last case, the predecessor value is available with the name `bound_var-next_num`, where `bound_var` is the variable
set by the condition and `next_num` is the expected value of the next case. For example, the above example code returns
`1`, since `x-1` is bound to `(+ 1 1) - 1` and the value of `x` doesn't match any explicit case.
Using `;` is optional.
### Match
```rust
match opt = (Some "Bend") {
Some: opt.value;
None: "No name";
}
```
A pattern match expression, it can hold a name binding if the matching term is not a variable.
It is possible to use a _wildcard_, a named variable or `*` as default cases.
It is desugared according to the chosen encoding. Read [pattern matching](./pattern-matching.md) to know more.
Using `;` is optional.
### If
```rust
if condition {
...then
} else {
...else
}
```
A branching expression where `else` is mandatory.
The condition must return a `u24` number, where 0 will run the `else` branch and any other value will return the first one.
It is equivalent to this switch:
```rust
switch _ = condition {
0: else
_: then
}
```
It is possible to make if-chains using `elif`:
```rust
if condition1 {
0
} elif condition2 {
1
} elif condition3 {
2
} else {
3
}
```
### Bend
Bend can be used to create recursive data structures:
```rust
main =
bend x = 0 {
when (< x 3):
(Tree/Node (fork (+ x 1)) (fork (+ x 1)))
else:
(Tree/Leaf x)
}
```
Which binds a variable to the return of an inline recursive function.
The function `fork` is available inside the `when` arm of the `bend` and calls it recursively.
It is possible to pass multiple state variables, which can be initialized:
```rust
bend x = 0, y = 1 ... {
when (condition x y ...):
...
}
```
When calling `fork`, the function must receive the same number of arguments as the number of state variables.
It is equivalent to this inline recursive function:
```rust
bend x y ... =
if (condition x y ...) {
...
... (bend x y ...) ...
} else {
...
}
```
### Open
```rust
let x = (Pair 1 2);
open Pair x;
(+ x.fst x.snd)
```
Brings the inner fields of an object into scope. The original variable can still be accessed, but doing so will cause any used fields to be duplicated.
It's equivalent to pattern matching on the value, with the restriction that its type must have only one constructor.
```rust
let x = (Pair 1 2)
match x {
Pair: (+ x.fst x.snd)
}
```
### With block
```rust
Result/bind (Result/Ok val) nxt = ((undefer nxt) val)
Result/bind err _nxt = err
div a b = switch b {
0: (Result/Err "Div by 0")
_: (Result/Ok (/ a b))
}
rem a b = switch b {
0: (Result/Err "Mod by 0")
_: (Result/Ok (% a b))
}
Main = with Result {
ask y = (div 3 2);
ask x = (rem y 0);
x
}
```
Receives a type defined with `type` and expects `Result/bind` to be defined as a monadic bind function.
It should be of type `(Result a) -> (a -> Result b) -> Result b`, like in the example above.
However, the second argument, `nxt`, is actually a deferred call to the continuation, passing any free variables as arguments.
Therefore, all `bind` functions must call the builtin function `undefer` before using the value of `nxt`, as in the example above.
This is necessary to ensure that the continuation in recursive monadic functions stays lazy and doesn't expand infinitely.
This is an example of a recursive function that would loop if passing the variable `a` to the recursive call `Result/foo(a, b)` was not deferred:
```python
Result/foo x y = with Result {
ask a = (Result/Ok 1)
ask b = if b {
(Result/Err x)
} else {
(Result/Ok y)
}
(Result/foo a b)
}
```
Inside a `with` block, you can use `ask`, to access the continuation value of the monadic operation.
```rust
ask y = (div 3 2)
ask x = (rem y 0)
x
# Becomes
(Result/bind (div 3 2) λy (Result/bind (rem y 0) λx x))
```
It can be used to force a sequence of operations. Since the continuation receives the result through a lambda, it is only fully evaluated after something is applied to it.
The name `wrap` is bound inside a `with` block as a shorthand for `Type/wrap`,
the equivalent as a `pure` function in other functional languages:
```rust
Result/wrap x = (Result/Ok x)
with Result {
ask x = (some_operation ...)
ask y = (some_operation ...)
(wrap (* x y))
}
```
### Def
Creates a local function visible in the current block capturing variables:
```rust
main =
let base = 0
def aux [] = base
aux (List/Cons head tail) = (+ head (aux tail))
(aux [1, 2, 3])
```
### Numbers and operations
Currently, bend supports 3 types of numbers: floats, integers and unsigned integers. All of then are 24 bit sized.
```rust
f24 = +88.012
i24 = -42
u24 = 42
```
Currently, the 3 number types cannot be mixed.
| Addition | (+ x y) | int, float, uint |
| Subtraction | (- x y) | int, float, uint |
| Multiplication | (\* x y) | int, float, uint |
| Division | (/ x y) | int, float, uint |
| Remainder | (% x y) | int, float, uint |
| Exponentiation | (\*\* x y) | float |
| Equal | (== x y) | int, float, uint |
| Not Equal | (!= x y) | int, float, uint |
| Less Than | (< x y) | int, float, uint |
| Greater Than | (> x y) | int, float, uint |
| Less Than or Equal | (<= x y) | int, float, uint |
| Greater Than or Equal | (>= x y) | int, float, uint |
| Bitwise And | (& x y) | int, uint |
| Bitwise Or | (\| x y) | int, uint |
| Bitwise Xor | (^ x y) | int, uint |
| Bitwise Right Shift | (>> x y) | uint |
| Bitwise Left Shift | (<< x y) | uint |
Hexadecimal and binary floating-point literals are also supported.
In these representations, each digit after the point is divided according to the base’s power of the digit's position.
Specifically, for hexadecimal floating-point numbers, each place after the dot represents a fraction of 16 to the negative power of the digit's depth.
Similarly, for binary floating-point numbers, each place after the dot represents a fraction of 2 to the negative power of the digit's depth.
```python
(== 0xA.A 10.625)
(== 0b111.111 7.875)
```
### Character Literal
```rust
'a'
```
A Character is surrounded with `'`. Accepts unicode characters, unicode escapes in the form '\u{hex value}' and is desugared to the unicode codepoint as an `u24`.
Only supports unicode codepoints up to `0xFFFFFF`.
### Symbol Literal
```python
# Becomes 2146 (33 << 6 + 34)
`hi`
```
A Symbol encodes a up to 4 base64 characters as a `u24` number. It is surrounded by `\``.
Empty characters are interpreted as `A` which has value 0, meaning that `B` is the same as `AAAB`.
### String Literal
```rust
"Hello"
```
A String literal is surrounded with `"`. Accepts the same values as characters literals.
The syntax above is desugared to:
```
(String.cons 'H' (String.cons 'e' (String.cons 'l' (String.cons 'l' (String.cons 'o' String.nil)))))
```
### List Literal
```rust
[1, 2, 3 4]
```
The syntax above is desugared to:
```
(List.cons 1 (List.cons 2 (List.cons 3 (List.cons 4 List.nil))))
```
Using `,` is optional.
### Tree Literals
```python
![![1, 2], ![3, 4]]
```
The Tree literals `![]` and `!` are used to create values of the built-in type `Tree`.
`![a b]` is equivalent to `Tree/Node(a, b)`.
`!x` is equivalent to `Tree/Leaf(x)`.
### Nat Literal
```rust
#3
```
The syntax above is desugared to:
```
(Nat/succ (Nat/succ (Nat/succ Nat/zero)))
```
# Native HVM definitions
```py
# This function causes two ports to be linked and returns *.
# This can be used to interpret a lambda as an application and apply something to it for example.
# It can be used like this: `let * = (link_ports @x x y)`
hvm link_ports:
(a (b *))
& (c a) ~ (d e)
& (e b) ~ (d c)
# Casts a `u24` to itself.
# We can give type annotations to HVM definitions.
hvm u24_to_u24 -> (u24 -> u24):
($([u24] ret) ret)
```
It's also possible to define functions using HVM syntax. This can be
thought of as a way to write "HVM assembly" directly in a Bend program.
You can find the reference of this syntax in the [HVM paper](https://github.com/HigherOrderCO/HVM/blob/main/paper/PAPER.pdf).
This is meant for writing things that would otherwise be hard or
impossible to write in normal Bend syntax.
It will also ignore all term-level compiler passes and so can be
useful for writing programs with exact behaviour that won't ever be
changed or optimized by the compiler.
<div id="import-syntax"></div>
# Import Syntax
### Import Relative to the File
Paths starting with `./` or `../` are imported relative to the file.
### Import Relative to the Main Folder
Paths that do not start with `./` or `../` are relative to the folder of the main file.
## Syntax
### Import Specific Names from a File, or Files from a Folder
```py
from path import name
from path import (name1, name2)
import (path/name1, path/name2)
```
### Import All Names from a File, or All Files from a Folder
```py
from path import *
```
### Aliasing Imports
```py
from path import name as alias
from path import (name1 as Alias1, name2 as Alias2)
import path as alias
import (path/name1 as Alias1, path/name2 as Alias2)
```
<div id="comments"></div>
# Comments
## Syntax
### Single Line Comment
Use `#` to indicate a single line comment.
```py
# Single line comment
def main():
# return 0
```
### Multi Line Comment
Use `#{ ... #}` to indicate a multi-line comment.
Multi-line commenting should also be used to document code.
Documentation for functions is meant to be written as a multiline comment right above the function.
```py
#{
Expects two arguments to be passed.
This function always returns the second value that was used as argument.
#}
def second(x: A, y: B) -> B:
return y
```
<div id="imp-type-syntax"></div>
# Imp Type Syntax
## Variable
Any name represents a type variable.
Used in generic or polymorphic type definitions.
```python
# T is a type variable
type Option(T):
Some { value: T }
None
# A is a type variable
def id(x: A) -> A:
return x
```
## Constructor
`Ctr(...)` represents a constructor type.
Used for defining custom data types or algebraic data types.
Can contain other types as parameters.
```python
def head(list: List(T)) -> Option(T)
match list:
case List/Nil:
return Option/None
case List/Cons:
return Option/Some(list.head)
```
## Any
`Any` represents the untyped type.
It accepts values of alls type and will forcefully cast any type to `Any`.
Can be used for values that can't be statically typed, either because
they are unknown (like in raw IO calls), because they contain untypable
expressions (like unscoped variables), or because the expression cannot
be typed with the current type system (like the self application `lambda x: x(x)`).
```python
def main -> Any:
return 24
```
## None
`None` represents the eraser `*` or absence of a value.
Often used to indicate that a function doesn't return anything.
```python
def none -> None:
return *
```
## Hole
`_` represents a hole type.
This will let the type checker infer the most general type for an argument or return value.
```python
def increment(x: _) -> _:
return x + 1
```
## u24
`u24` represents an unsigned 24-bit integer.
```python
def zero -> u24:
return 0
```
## i24
`i24` represents a signed 24-bit integer.
```python
def random_integer -> i24:
return -42
```
## f24
`f24` represents a 24-bit floating-point number.
```python
def PI -> f24:
return 3.14
```
## Tuple
`(_, _, ...)` represents a tuple type.
Can contain two or more types separated by commas.
```python
def make_tuple(fst: A, snd: B) -> (A, B):
return (fst, snd)
```
## Function
`a -> b` represents a function type.
`a` is the input type, and `b` is the output type.
```python
def apply(f: A -> B, arg: A) -> B:
return f(arg)
```
<div id="fun-type-syntax"></div>
# Fun Type Syntax
## Variable
Any name represents a type variable.
Used in generic or polymorphic type definitions.
```python
# T is a type variable
type (Option T)
= (Some T)
| None
# A is a type variable
id : A -> A
id x = x
```
## Constructor
`(Ctr ...)` represents a constructor type.
Used for defining custom data types or algebraic data types.
Can contain other types as parameters.
```python
head : (List T) -> (Option T)
head [] = Option/None
head (List/Cons head _) = (Option/Some head)
```
## Any
`Any` represents the untyped type.
It accepts values of alls type and will forcefully cast any type to `Any`.
Can be used for values that can't be statically typed, either because
they are unknown (like in raw IO calls), because they contain untypable
expressions (like unscoped variables), or because the expression cannot
be typed with the current type system (like the self application `λx (x x)`).
```python
main : Any
main = @x x
```
## None
`None` represents the eraser `*` or absence of a value.
Often used to indicate that a function doesn't return anything.
```python
none : None
none = *
```
## Hole
`_` represents a hole type.
This will let the type checker infer the most general type for an argument or return value.
```python
increment : _ -> _
increment x = (+ x 1)
```
## u24
`u24` represents an unsigned 24-bit integer.
```python
zero : u24
zero = 0
```
## i24
`i24` represents a signed 24-bit integer.
```python
random_integer : i24
random_integer = -24
```
## f24
`f24` represents a 24-bit floating-point number.
```python
PI : f24
PI = 3.14
```
## Tuple
`(_, _, ...)` represents a tuple type.
Can contain two or more types separated by commas.
```python
make_tuple : A -> B -> (A, B)
make_tuple fst snd = (fst, snd)
```
## Function
`a -> b` represents a function type.
`a` is the input type, and `b` is the output type.
```python
apply : (A -> B) -> A -> B
apply f arg = (f arg)
```