mirror of
https://github.com/fluencelabs/aqua-book
synced 2024-12-04 23:30:18 +00:00
GitBook: [bb-edit] 31 pages and one asset modified
This commit is contained in:
parent
d54b086ab4
commit
d6c3316ef4
BIN
.gitbook/assets/screen-shot-2021-06-29-at-1.06.39-pm.png
Normal file
BIN
.gitbook/assets/screen-shot-2021-06-29-at-1.06.39-pm.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 982 KiB |
@ -4,9 +4,9 @@ While Execution flow organizes the flow from peer to peer, Abilities & Services
|
||||
|
||||
Ability is a concept of "what is possible in this context": like a peer-specific trait or a typeclass. It will be better explained once abilities passing is implemented.
|
||||
|
||||
{% embed url="https://github.com/fluencelabs/aqua/issues/33" %}
|
||||
{% embed url="https://github.com/fluencelabs/aqua/issues/33" caption="" %}
|
||||
|
||||
### Services
|
||||
## Services
|
||||
|
||||
A Service interfaces functions \(often WASM ones\) executable on a peer. Example of service definition:
|
||||
|
||||
@ -19,7 +19,7 @@ service MyService:
|
||||
|
||||
Service functions in Aqua have no function body. Computations, of any complexity, are implemented with any programming language that fits, and then brought to the Aqua execution context. Aqua calls these functions but does not peak into what's going on inside.
|
||||
|
||||
#### Built-in services
|
||||
### Built-in services
|
||||
|
||||
Some services may be singletons available on all peers. Such services are called built-ins, and are always available in any scope.
|
||||
|
||||
@ -33,7 +33,7 @@ func foo():
|
||||
Op.noop()
|
||||
```
|
||||
|
||||
#### Service resolution
|
||||
### Service resolution
|
||||
|
||||
A peer may host many services of the same type. To distinguish services from each other, Aqua requires Service resolution to be done: that means, the developer must provide an ID of the service to be used on the peer.
|
||||
|
||||
|
@ -52,5 +52,3 @@ Reference:
|
||||
|
||||
* [Expressions](expressions/)
|
||||
|
||||
|
||||
|
||||
|
@ -36,7 +36,7 @@ func bar(peer: string, relay: string):
|
||||
|
||||
But the most powerful uses of streams come along with parallelism, which incurs non-determinism.
|
||||
|
||||
### Streams lifecycle and guarantees
|
||||
## Streams lifecycle and guarantees
|
||||
|
||||
Streams lifecycle can be divided into three stages:
|
||||
|
||||
@ -68,7 +68,6 @@ func foo(peers: []string) -> string:
|
||||
-- Once we have 5 responses, merge them
|
||||
r <- Srv.concat(resp2)
|
||||
<- r
|
||||
|
||||
```
|
||||
|
||||
In this case, for each peer in peers, something is going to be written into resp stream.
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
Aqua supports branching: you can return one value or another, recover from the error, or check a boolean expression.
|
||||
|
||||
### Contract
|
||||
## Contract
|
||||
|
||||
The second arm of the conditional operator is executed iff the first arm failed.
|
||||
|
||||
@ -12,9 +12,9 @@ A conditional block is considered executed iff any arm was executed successfully
|
||||
|
||||
A conditional block is considered failed iff the second \(recovery\) arm fails to execute.
|
||||
|
||||
### Conditional operations
|
||||
## Conditional operations
|
||||
|
||||
#### try
|
||||
### try
|
||||
|
||||
Tries to perform operations, or swallows the error \(if there's no catch, otherwise after the try block\).
|
||||
|
||||
@ -27,7 +27,7 @@ try:
|
||||
x <- foo()
|
||||
```
|
||||
|
||||
#### catch
|
||||
### catch
|
||||
|
||||
Catches the standard error from `try` block.
|
||||
|
||||
@ -47,7 +47,7 @@ data LastError:
|
||||
peer_id: string -- On what peer the error happened
|
||||
```
|
||||
|
||||
#### if
|
||||
### if
|
||||
|
||||
If corresponds to `match`, `mismatch` extension of π-calculus.
|
||||
|
||||
@ -70,7 +70,7 @@ Currently, you may only use one `==`, `!=` operator in the `if` expression, or c
|
||||
|
||||
Both operands can be variables.
|
||||
|
||||
#### else
|
||||
### else
|
||||
|
||||
Just the second branch of `if`, in case the condition does not hold.
|
||||
|
||||
@ -83,7 +83,7 @@ else:
|
||||
|
||||
If you want to set a variable based on condition, see Conditional return.
|
||||
|
||||
#### otherwise
|
||||
### otherwise
|
||||
|
||||
You may add `otherwise` to provide recovery for any block or expression:
|
||||
|
||||
@ -94,7 +94,7 @@ otherwise:
|
||||
y <- bar()
|
||||
```
|
||||
|
||||
### Conditional return
|
||||
## Conditional return
|
||||
|
||||
In Aqua, functions may have only one return expression, which is very last. And conditional expressions cannot define the same variable:
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
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
|
||||
## For expression
|
||||
|
||||
In short, `for` looks like the following:
|
||||
|
||||
@ -19,7 +19,7 @@ x <- bar()
|
||||
y <- baz()
|
||||
```
|
||||
|
||||
### Contract
|
||||
## Contract
|
||||
|
||||
Iterations of `for` loop are executed sequentially by default.
|
||||
|
||||
@ -29,7 +29,7 @@ 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
|
||||
## Conditional for
|
||||
|
||||
You can make several trials in a loop, and break once any trial succeeded.
|
||||
|
||||
@ -43,7 +43,7 @@ for x <- xs try:
|
||||
|
||||
Contract is changed as in [Parallel](parallel.md#contract) flow.
|
||||
|
||||
### Parallel for
|
||||
## Parallel for
|
||||
|
||||
Running many operations in parallel is the most commonly used pattern for `for`.
|
||||
|
||||
@ -62,7 +62,7 @@ par continueWithBaz()
|
||||
|
||||
Contract is changed as in [Conditional](conditional.md#contract) flow.
|
||||
|
||||
### Export data from for
|
||||
## 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).
|
||||
|
||||
@ -79,7 +79,7 @@ for x <- xs par:
|
||||
baz(return!5, return)
|
||||
```
|
||||
|
||||
### For on streams
|
||||
## For on streams
|
||||
|
||||
For on streams is one of the most complex and powerful parts of Aqua. See [CRDT streams](../crdt-streams.md) for details.
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
Parallel execution is where everything becomes shiny.
|
||||
|
||||
### Contract
|
||||
## Contract
|
||||
|
||||
Parallel arms have no access to each other's data. Sync points must be explicit \(see Join behavior\).
|
||||
|
||||
@ -10,7 +10,7 @@ If any arm is executed successfully, the flow execution continues.
|
||||
|
||||
All the data defined in parallel arms is available in the subsequent code.
|
||||
|
||||
### Implementation limitation
|
||||
## Implementation limitation
|
||||
|
||||
Parallel execution has some implementation limitations:
|
||||
|
||||
@ -20,9 +20,9 @@ Parallel execution has some implementation limitations:
|
||||
|
||||
We might overcome these limitations later, but for now, plan your application design having this in mind.
|
||||
|
||||
### Parallel operations
|
||||
## Parallel operations
|
||||
|
||||
### par
|
||||
## par
|
||||
|
||||
`par` syntax is derived from π-calculus notation of parallelism: `A | B`
|
||||
|
||||
@ -54,7 +54,7 @@ par hello(y)
|
||||
|
||||
`par` works in infix manner between the previously stated function and the next one.
|
||||
|
||||
#### co
|
||||
### co
|
||||
|
||||
`co` , short for `coroutine`, prefixes an operation to send it to background. From π-calculus perspective, it's the same as `A | null`, where `null`-process is the one that does nothing and completes instantly.
|
||||
|
||||
@ -80,7 +80,7 @@ bar()
|
||||
bax(x)
|
||||
```
|
||||
|
||||
### Join behavior
|
||||
## Join behavior
|
||||
|
||||
Join means that data was created by different parallel execution flows and then used on a single peer to perform computations. It works the same way for any parallel blocks, be it `par`, `co` or something else \(`for par`\).
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
By default, Aqua code is executed line by line, sequentially.
|
||||
|
||||
### Contract
|
||||
## Contract
|
||||
|
||||
Data from the first arm is available in the second branch.
|
||||
|
||||
@ -12,9 +12,9 @@ If any arm failed, then the whole sequence is failed.
|
||||
|
||||
If all arms executed successfully, then the whole sequence is executed successfully.
|
||||
|
||||
### Sequential operations
|
||||
## Sequential operations
|
||||
|
||||
#### call arrow
|
||||
### call arrow
|
||||
|
||||
Any runnable piece of code in Aqua is an arrow from its domain to codomain.
|
||||
|
||||
@ -34,7 +34,7 @@ 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 being \(semantically\) on the same peer where you have called the arrow.
|
||||
|
||||
#### on
|
||||
### on
|
||||
|
||||
`on` denotes the peer where the code must be executed. `on` is handled sequentially, and the code inside is executed line by line by default.
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
Aqua source file has head and body. The body contains function definitions, services, types, constants. Header manages what is imported from other files, and what is exported from this one.
|
||||
|
||||
### Import expression
|
||||
## Import expression
|
||||
|
||||
The main way to import a file is via `import` expression:
|
||||
|
||||
@ -17,11 +17,9 @@ Aqua compiler takes a source directory and a list of import directories \(usuall
|
||||
|
||||
Everything defined in the file is imported into the current namespace.
|
||||
|
||||
### `Use` expression
|
||||
## `Use` expression
|
||||
|
||||
Use expression makes it possible to import a subset of a file, or to alias the imports to avoid collisions.
|
||||
|
||||
{% embed url="https://github.com/fluencelabs/aqua/issues/30" %}
|
||||
|
||||
|
||||
{% embed url="https://github.com/fluencelabs/aqua/issues/30" caption="" %}
|
||||
|
||||
|
@ -4,12 +4,11 @@ description: Define where the code is to be executed and how to get there
|
||||
|
||||
# Topology
|
||||
|
||||
|
||||
Aqua lets developers to describe the whole distributed workflow in a single script, link data, recover from errors, implement complex patterns like backpressure, and more. Hence, topology is at the heart of Aqua.
|
||||
|
||||
Topology in Aqua is declarative: You just need to say where a piece of code must be executed, on what peer, and optionally how to get there. he Aqua compiler will add all the required network hops.
|
||||
|
||||
### On expression
|
||||
## On expression
|
||||
|
||||
`on` expression moves execution to the specified peer:
|
||||
|
||||
@ -28,7 +27,7 @@ on myPeer:
|
||||
baz()
|
||||
```
|
||||
|
||||
### `%init_peer_id%`
|
||||
## `%init_peer_id%`
|
||||
|
||||
There is one custom peer ID that is always in scope: `%init_peer_id%`. It points to the peer that initiated this request.
|
||||
|
||||
@ -36,7 +35,7 @@ There is one custom peer ID that is always in scope: `%init_peer_id%`. It points
|
||||
Using `on %init_peer_id%` is an anti-pattern: There is no way to ensure that init peer is accessible from the currently used part of the network.
|
||||
{% endhint %}
|
||||
|
||||
### More complex scenarios
|
||||
## More complex scenarios
|
||||
|
||||
Consider this example:
|
||||
|
||||
@ -70,7 +69,7 @@ Declarative topology definition always works the same way.
|
||||
* `bar(2)` is executed on `"peer baz"`, despite the fact that foo does topologic transition. `bar(2)` is in the scope of `on "peer baz"`, so it will be executed there
|
||||
* `bar(3)` is executed where `bar(1)` was: in the root scope of `baz`, wherever it was called from
|
||||
|
||||
### Accessing peers `via` other peers
|
||||
## Accessing peers `via` other peers
|
||||
|
||||
In a distributed network it is quite common that a peer is not directly accessible. For example, a browser has no public network interface and you cannot open a socket to a browser at will. Such constraints warrant a `relay` pattern: there should be a well-connected peer that relays requests from a peer to the network and vice versa.
|
||||
|
||||
@ -137,7 +136,7 @@ foo()
|
||||
|
||||
When the `on` scope is ended, it does not affect any further topology moves. Until you stop indentation, `on` affects the topology and may add additional topology moves, which means more roundtrips and unnecessary latency.
|
||||
|
||||
### Callbacks
|
||||
## Callbacks
|
||||
|
||||
What if you want to return something to the initial peer? For example, implement a request-response pattern. Or send a bunch of requests to different peers, and render responses as they come, in any order.
|
||||
|
||||
@ -192,7 +191,7 @@ func baz():
|
||||
Passing service function calls as arguments is very fragile as it does not track that a service is resolved in the scope of the call. Abilities variance may fix that.
|
||||
{% endhint %}
|
||||
|
||||
### Parallel execution and topology
|
||||
## Parallel execution and topology
|
||||
|
||||
When blocks are executed in parallel, it is not always necessary to resolve the topology to get to the next peer. The compiler will add topologic hops from the par branch only if data defined in that branch is used down the flow.
|
||||
|
||||
@ -200,5 +199,3 @@ When blocks are executed in parallel, it is not always necessary to resolve the
|
||||
What if all branches do not return? Execution will halt. Be careful, use `co` if you don't care about the returned data.
|
||||
{% endhint %}
|
||||
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Types
|
||||
|
||||
### Scalars
|
||||
## Scalars
|
||||
|
||||
Scalar types follow the Wasm IT notation.
|
||||
|
||||
@ -12,11 +12,11 @@ Scalar types follow the Wasm IT notation.
|
||||
* Records \(product type\): see below
|
||||
* Arrays: see Collection Types below
|
||||
|
||||
### Literals
|
||||
## Literals
|
||||
|
||||
You can pass booleans \(true, false\), numbers, double-quoted strings as literals.
|
||||
|
||||
### Products
|
||||
## Products
|
||||
|
||||
```python
|
||||
data ProductName:
|
||||
@ -29,7 +29,7 @@ data OtherProduct:
|
||||
|
||||
Fields are accessible with the dot operator `.` , e.g. `product.field`.
|
||||
|
||||
### Collection Types
|
||||
## Collection Types
|
||||
|
||||
Aqua has three different types with variable length, denoted by quantifiers `[]`, `*`, and `?`.
|
||||
|
||||
@ -41,7 +41,6 @@ Appendable collection with 0..N values: `*`
|
||||
|
||||
Any data type can be prepended with a quantifier, e.g. `*u32`, `[][]string`, `?ProductType` are all correct type specifications.
|
||||
|
||||
|
||||
You can access a distinct value of a collection with `!` operator, optionally followed by an index.
|
||||
|
||||
Examples:
|
||||
@ -60,7 +59,7 @@ maybe_value: ?string
|
||||
value = maybe_value!
|
||||
```
|
||||
|
||||
### Arrow Types
|
||||
## Arrow Types
|
||||
|
||||
Every function has an arrow type that maps a list of input types to an optional output type.
|
||||
|
||||
@ -82,7 +81,7 @@ arrow()
|
||||
x <- arrow()
|
||||
```
|
||||
|
||||
### Type Alias
|
||||
## Type Alias
|
||||
|
||||
For convenience, you can alias a type:
|
||||
|
||||
@ -90,13 +89,12 @@ For convenience, you can alias a type:
|
||||
alias MyAlias = ?string
|
||||
```
|
||||
|
||||
### Type Variance
|
||||
## Type Variance
|
||||
|
||||
Aqua is made for composing data on the open network. That means that you want to compose things if they do compose, even if you don't control its source code.
|
||||
|
||||
Therefore Aqua follows the structural typing paradigm: if a type contains all the expected data, then it fits. For example, you can pass `u8` in place of `u16` or `i16`. Or `?bool` in place of `[]bool`. Or `*string` instead of `?string` or `[]string`. The same holds for products.
|
||||
|
||||
|
||||
For arrow types, Aqua checks the variance on arguments and contravariance on the return type.
|
||||
|
||||
```text
|
||||
@ -130,7 +128,7 @@ bar(foo4)
|
||||
|
||||
Arrow type `A: D -> C` is a subtype of `A1: D1 -> C1`, if `D1` is a subtype of `D` and `C` is a subtype of `C1`.
|
||||
|
||||
### Type Of A Service And A File
|
||||
## Type Of A Service And A File
|
||||
|
||||
A service type is a product of arrows.
|
||||
|
||||
@ -161,5 +159,3 @@ data MyServiceType:
|
||||
|
||||
{% embed url="https://github.com/fluencelabs/aqua/blob/main/types/src/main/scala/aqua/types/Type.scala" caption="See the types system implementation" %}
|
||||
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@ on "peer 1":
|
||||
|
||||
More on that in the Security section. Now let's see how we can work with values inside the language.
|
||||
|
||||
### Arguments
|
||||
## Arguments
|
||||
|
||||
Function arguments are available within the whole function body.
|
||||
|
||||
@ -31,7 +31,7 @@ func foo(arg: i32, log: string -> ()):
|
||||
log("Wrote arg to responses")
|
||||
```
|
||||
|
||||
### Return values
|
||||
## Return values
|
||||
|
||||
You can assign results of an arrow call to a name, and use this returned value in the code below.
|
||||
|
||||
@ -55,7 +55,7 @@ func foo(arg: i32, log: *string):
|
||||
log <- bar(arg)
|
||||
```
|
||||
|
||||
### Literals
|
||||
## Literals
|
||||
|
||||
Aqua supports just a few literals: numbers, quoted strings, booleans. You [cannot init a structure](https://github.com/fluencelabs/aqua/issues/167) in Aqua, only obtain it as a result of a function call.
|
||||
|
||||
@ -79,7 +79,7 @@ bar(-1)
|
||||
bar(-0.2)
|
||||
```
|
||||
|
||||
### Getters
|
||||
## Getters
|
||||
|
||||
In Aqua, you can use a getter to peak into a field of a product or indexed element in an array.
|
||||
|
||||
@ -105,9 +105,8 @@ func foo(e: Example):
|
||||
|
||||
Note that the `!` operator may fail or halt:
|
||||
|
||||
* If it is called on an immutable collection, it will fail if the collection is shorter and has no given index; you can handle the error with [try](operators/conditional.md#try) or [otherwise](operators/conditional.md#otherwise).
|
||||
* If it is called on an appendable stream, it will wait for some parallel append operation to fulfill, see [Join behavior](operators/parallel.md#join-behavior).
|
||||
|
||||
* If it is called on an immutable collection, it will fail if the collection is shorter and has no given index; you can handle the error with [try](https://github.com/fluencelabs/aqua-book/tree/d54b086ab43f89c9f5622d26a22574a47d0cde19/language/operators/conditional.md#try) or [otherwise](https://github.com/fluencelabs/aqua-book/tree/d54b086ab43f89c9f5622d26a22574a47d0cde19/language/operators/conditional.md#otherwise).
|
||||
* If it is called on an appendable stream, it will wait for some parallel append operation to fulfill, see [Join behavior](https://github.com/fluencelabs/aqua-book/tree/d54b086ab43f89c9f5622d26a22574a47d0cde19/language/operators/parallel.md#join-behavior).
|
||||
|
||||
{% hint style="warning" %}
|
||||
The `!` operator can currently only be used with literal indices.
|
||||
@ -115,11 +114,10 @@ That is,`!2` is valid but`!x` is not valid.
|
||||
We expect to address this limitation soon.
|
||||
{% endhint %}
|
||||
|
||||
### Assignments
|
||||
## Assignments
|
||||
|
||||
Assignments, `=`, only give a name to a value with applied getter or to a literal.
|
||||
|
||||
|
||||
```text
|
||||
func foo(arg: bool, e: Example):
|
||||
-- Rename the argument
|
||||
@ -130,7 +128,7 @@ func foo(arg: bool, e: Example):
|
||||
c = "just string value"
|
||||
```
|
||||
|
||||
### Constants
|
||||
## Constants
|
||||
|
||||
Constants are like assignments but in the root scope. They can be used in all function bodies, textually below the place of const definition. Constant values must resolve to a literal.
|
||||
|
||||
@ -150,7 +148,7 @@ func bar():
|
||||
foo(setting)
|
||||
```
|
||||
|
||||
### Visibility scopes
|
||||
## Visibility scopes
|
||||
|
||||
Visibility scopes follow the contracts of execution flow.
|
||||
|
||||
@ -193,7 +191,7 @@ par y <- bar(x)
|
||||
baz(x, y)
|
||||
```
|
||||
|
||||
Recovery branches in [conditional flow](operators/conditional.md) have no access to the main branch as the main branch exports values, whereas the recovery branch does not:
|
||||
Recovery branches in [conditional flow](https://github.com/fluencelabs/aqua-book/tree/d54b086ab43f89c9f5622d26a22574a47d0cde19/language/operators/conditional.md) have no access to the main branch as the main branch exports values, whereas the recovery branch does not:
|
||||
|
||||
```text
|
||||
try:
|
||||
@ -205,10 +203,9 @@ otherwise:
|
||||
|
||||
-- y is not available below
|
||||
willFail(y)
|
||||
|
||||
```
|
||||
|
||||
### Streams as literals
|
||||
## Streams as literals
|
||||
|
||||
Stream is a special data structure that allows many writes. It has [a dedicated article](crdt-streams.md).
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user