GitBook: [master] 29 pages modified

This commit is contained in:
Dmitry Kurinskiy 2021-06-23 08:11:19 +00:00 committed by gitbook-bot
parent 743fe257ba
commit f6d737e844
No known key found for this signature in database
GPG Key ID: 07D2180C7B12D0FF
6 changed files with 379 additions and 1 deletions

View File

@ -11,6 +11,9 @@
* [Types](language/types.md)
* [Execution flow](language/operators/README.md)
* [Sequential](language/operators/sequential.md)
* [Conditional](language/operators/conditional.md)
* [Parallel](language/operators/parallel.md)
* [Iterative](language/operators/iterative.md)
* [Expressions](language/expressions/README.md)
* [Header](language/expressions/header.md)
* [Functions](language/expressions/functions.md)

View File

@ -1,2 +1,6 @@
# Execution flow
Aqua's main goal is to express how the execution flows: moves from peer to peer, forks to parallel flows and then joins back, uses data from one step in another.
As the foundation of Aqua is based on π-calculus, finally flow is decomposed into sequential \(seq, .\), conditional \(xor, ^\), parallel \(par, \|\) computations and iterations based on data \(!P\).

View File

@ -0,0 +1,126 @@
# Conditional
Aqua supports branching: you can return one value or another, recover from the error, or check a boolean expression.
### Contract
Second branch is executed iff the first branch failed.
Second branch has no access to the first branch's data.
Block is considered executed iff any branch was executed successfully.
Block is considered failed iff the second branch fails to execute.
### Conditional operations
#### try
Tries to perform operations, or swallows the error \(if there's no catch, otherwise after the try block\).
```text
try:
-- If foo fails with an error, execution will continue
-- You should write your logic in a non-blocking fashion:
-- If your code below depends on `x`, it may halt as `x` is not resolved.
-- See Conditional return below for workaround
x <- foo()
```
#### catch
Catches the standard error from `try` block.
```text
try:
foo()
catch e:
logError(e)
```
Type of `e` is:
```text
data LastError:
instruction: string -- What AIR instruction failed
msg: string -- Human-readable error message
peer_id: string -- On what peer the error happened
```
#### if
If corresponds to `match`, `mismatch` extension of π-calculus.
```text
x = true
if x:
-- always executed
foo()
if x == false:
-- never executed
bar()
if x != false:
-- executed
baz()
```
Currently, you may only use one `==`, `!=` operator in the `if` expression, or compare with true.
Both operators can be a variable. Variable types must intersect.
#### else
Just the second branch of `if`, in case the condition does not hold.
```text
if true:
foo()
else:
bar()
```
If you want to set a variable based on condition, see Conditional return.
#### otherwise
You may add `otherwise` to provide recovery for any block or expression:
```text
x <- foo()
otherwise:
-- if foo can't be executed, then do bar()
y <- bar()
```
### Conditional return
In Aqua, functions may have only one return expression, which is very last. And conditional expressions cannot define the same variable:
```text
try:
x <- foo()
otherwise:
x <- bar() -- Error: name x was already defined in scope, can't compile
```
So to get the value based on condition, we need to use a [writeable collection](../types.md#collection-types).
```text
-- result may have 0 or more values of type string, and is writeable
resultBox: *string
try:
resultBox <- foo()
otherwise:
resultBox <- bar()
-- now result contains only one value, let's extract it!
result = resultBox!
-- Type of result is string
-- Please note that if there were no writes to resultBox,
-- the first use of result will halt.
-- So you need to be careful about it and ensure that there's always a value.
```

View File

@ -0,0 +1,89 @@
# Iterative
π-calculus has a notion of repetitive process: `!P = P | !P`. That means, you can always fork a new `P` process if you need it.
In Aqua, two operations corresponds to it: you can call a service function \(it's just available when it's needed\), and you can use `for` loop to iterate on collections.
### For expression
In short, `for` looks like the following:
```text
xs: []string
for x <- xs:
y <- foo(x)
-- x and y are not accessible there, you can even redefine them
x <- bar()
y <- baz()
```
### Contract
Iterations of `for` loop are executed sequentially by default.
Variables defined inside for loop are not available outside.
For loop's code has access to all variables above.
For can be executed on a variable of any [Collection type](../types.md#collection-types).
### Conditional for
You can make several trials in a loop, and break once any trial succeeded.
```text
xs: []string
for x <- xs try:
-- Will stop trying once foo succeeds
foo(x)
```
Contract is changed as in [Parallel](parallel.md#contract) flow.
### Parallel for
Running many operations in parallel is the most commonly used pattern for `for`.
```text
xs: []string
for x <- xs par:
on x:
foo()
-- Once the fastest x succeeds, execution continues
-- If you want to make the subsequent execution independent from for,
-- mark it with par, e.g.:
par continueWithBaz()
```
Contract is changed as in [Conditional](conditional.md#contract) flow.
### Export data from for
The way to export data from `for` is the same as in [Conditional return](conditional.md#conditional-return) and [Race patterns](parallel.md#join-behavior).
```text
xs: []string
return: *string
-- can be par, try, or nothing
for x <- xs par:
on x:
return <- foo()
-- Wait for 6 fastest results -- see Join behavior
baz(return!5, return)
```
### For on streams
For on streams is one of the most complex parts of Aqua.
Stream forms CRDT.
Iterations are added when new values are added to a stream.

View File

@ -0,0 +1,96 @@
# Parallel
Parallel execution is where everything becomes shiny.
### Contract
Parallel branches have no access to each other's data. Sync points must be explicit \(see Join behavior\).
If any branch is executed successfully, the flow execution continues.
All the data defined in parallel branches is available in the subsequent code.
### Parallel operations
As of time of writing, there's only one parallel expression: `par`
Its syntax is derived from π-calculus notation of parallelism: `A | B`
```text
-- foo and bar will be executed in parallel, if possible
foo()
par bar()
```
Parallel execution has some implementation limitations:
* Parallel means independent execution on different peers
* No parallelism when executing a script on single peer \(fix planned\)
* No concurrency in services: one service instance does only one job in a time. Keep services small \(wasm limitation\)
### Join behavior
Join means that data was created by different parallel execution flows and then used on a single peer to perform computations.
In Aqua, you can refer to previously defined variables. In case of sequential computations, they are available, if execution not failed:
```text
-- Start execution somewhere
on peer1:
-- Go to peer1, execute foo, remember x
x <- foo()
-- x is available at this point
on peer2:
-- Go to peer2, execute bar, remember y
y <- bar()
-- Both x and y are available at this point
-- Use them in a function
baz(x, y)
```
Let's make this script parallel: execute `foo` and `bar` on different peers in parallel, then use both to compute `baz`.
```text
-- Start execution somewhere
on peer1:
-- Go to peer1, execute foo, remember x
x <- foo()
-- Notice par on the next line: it means, go to peer2 in parallel with peer1
par on peer2:
-- Go to peer2, execute bar, remember y
y <- bar()
-- Remember the contract: either x or y is available at this point
-- As it's enough to execute just one branch to advance further
baz(x, y)
```
What will happen when execution comes to `baz`?
Actually, the script will be executed twice: first time it will be sent from `peer1`, and second time from `peer2`. Or another way round: `peer2` then `peer1`, we don't know who is faster.
When execution will get to `baz` for the first time, [Aqua VM](../../aqua-vm.md) will realize that it lacks some data that is expected to be computed above in the parallel branch. And halt.
After the second branch executes, VM will be woken up again, reach the same piece of code and realize that now it has enough data to proceed.
This way you can express race \(see [Collection types](../types.md#collection-types) and [Conditional return](conditional.md#conditional-return) for other uses of this pattern\):
```text
-- Initiate a stream to write into it several times
results: *string
on peer1:
results <- foo()
par on peer2:
results <- bar()
-- When any result is returned, take the first (the fastest) to proceed
baz(results!0)
```

View File

@ -1,4 +1,64 @@
# Sequential
Sequential
By default, Aqua code is executed line by line, sequentially.
### Contract
Data from the first branch is available in the second branch.
Second branch is executed iff the first branch succeeded.
If any branch errored, then the whole sequence is errored.
If all branches executed successfully, then the whole seq is executed successfully.
### Sequential operations
#### call arrow
Any runnable piece of code in Aqua is an arrow from its domain to codomain.
```text
-- Call a function
foo()
-- Call a function that returns smth, assign results to a variable
x <- foo()
-- Call an ability function
y <- Peer.identify()
-- Pass an argument
z <- Op.identity(y)
```
When you write `<-`, this means not just "assign results of the function on the right to variable on the left". It means that all the effects are executed: [service](../abilities-and-services.md) may change state, [topology](../topology.md) may be shifted. But you end up in the same topological scope.
#### on
`on` denotes the peer where the code must be executed.
```text
func foo():
-- Will be executed where `foo` was executed
bar()
-- Move to another peer
on another_peer:
-- To call bar, we need to leave the peer where we were and get to another_peer
-- It's done automagically
bar()
on third_peer via relay:
-- This is executed on third_peer
-- But we denote that to get to third_peer and to leave third_peer
-- an additional hop is needed: get to relay, then to peer
bar()
-- Will be executed in the `foo` call site again
-- To get from the previous `bar`, compiler will add a hop to relay
bar()
```
See more in [Topology](../topology.md) section.