mirror of
https://github.com/fluencelabs/aqua-book
synced 2024-12-04 15:20:19 +00:00
GitBook: [alpha] 26 pages modified
This commit is contained in:
parent
3d9fb4f754
commit
b7fcf54cdf
@ -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.
|
||||
|
||||
@ -63,5 +63,5 @@ func foo():
|
||||
MyService.noop()
|
||||
```
|
||||
|
||||
There's no way to call an external function in Aqua without defining all the data types and the service type. One of the most convinient ways to do it is to generate Aqua types from WASM code in Marine \[link to Marine docs\].
|
||||
There's no way to call an external function in Aqua without defining all the data types and the service type. One of the most convenient ways to do it is to generate Aqua types from Wasm code in Marine.
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
# CRDT Streams
|
||||
|
||||
In Aqua, an ordinary value is a name that points to a single result:
|
||||
In Aqua, ordinary value is a name that points to a single result:
|
||||
|
||||
```text
|
||||
value <- foo()
|
||||
```
|
||||
|
||||
A stream , on the other hand, is a name that points to zero or more results:
|
||||
Stream is a name that points to a number of results \(zero or more\):
|
||||
|
||||
```text
|
||||
value: *string
|
||||
@ -14,7 +14,7 @@ value <- foo()
|
||||
value <- foo()
|
||||
```
|
||||
|
||||
Stream is a kind of [collection](types.md#collection-types) and can be used in place of other collections:
|
||||
Stream is a kind of [collection](types.md#collection-types), and can be used where other collections are:
|
||||
|
||||
```text
|
||||
func foo(peer: string, relay: ?string):
|
||||
@ -34,15 +34,15 @@ func bar(peer: string, relay: string):
|
||||
foo(peer, relayMaybe)
|
||||
```
|
||||
|
||||
But the most powerful use of streams pertains to their use with parallel execution, which incurs non-determinism.
|
||||
But the most powerful uses of streams come along with parallelism, which incurs non-determinism.
|
||||
|
||||
### Streams: Lifecycle And Guarantees
|
||||
### Streams lifecycle and guarantees
|
||||
|
||||
A stream's lifecycle can be separated into three stages:
|
||||
Streams lifecycle can be divided into three stages:
|
||||
|
||||
* Source: \(Parallel\) Writes to a stream
|
||||
* Map: Handling the stream values
|
||||
* Sink: Converting the resulting stream into a scalar
|
||||
* Sink: Converting the resulting stream into scalar
|
||||
|
||||
Consider the following example:
|
||||
|
||||
@ -50,7 +50,7 @@ Consider the following example:
|
||||
func foo(peers: []string) -> string:
|
||||
resp: *string
|
||||
|
||||
-- Go to all peers in parallel
|
||||
-- Will go to all peers in parallel
|
||||
for p <- peers par:
|
||||
on p:
|
||||
-- Do something
|
||||
@ -71,15 +71,15 @@ func foo(peers: []string) -> string:
|
||||
|
||||
```
|
||||
|
||||
In this case, for each peer in peers, something is going to be written into `resp` stream.
|
||||
In this case, for each peer in peers, something is going to be written into resp stream.
|
||||
|
||||
Every peer `p` in peers does not know anything about how the other iterations proceed.
|
||||
Every peer p in peers does not know anything about how the other iterations proceed.
|
||||
|
||||
Once something is written to `resp` stream, the second for is triggered. This is the mapping stage.
|
||||
Once something is written to resp stream, the second for is triggered. It's the mapping stage.
|
||||
|
||||
And then the results are sent to the first peer, to call Op.identity there. This Op.identity waits until element number 5 is defined on `resp2` stream.
|
||||
And then the results are sent to the first peer, to call Op.identity there. This Op.identity waits until element number 5 is defined on resp2 stream.
|
||||
|
||||
When the join is complete, the stream is consumed by the concatenation service to produce a scalar value, which is returned.
|
||||
When it is, stream as a whole is consumed to produce a scalar value, which is returned.
|
||||
|
||||
During execution, involved peers have different views on the state of execution: each of the `for` parallel branches have no view or access to the other branches' data and eventually, the execution flows to the initial peer. The initial peer then merges writes to the `resp` stream and to the `resp2` stream, respectively. These writes are done in conflict-free fashion. Furthermore, the respective heads of the `resp`, `resp2` streams will not change from each peer's point of view as they are immutable and new values can only be appended. However, different peers may have a different order of the stream values depending on the order of receiving these values.
|
||||
During execution, involved peers have different views on the state of execution: parallel branches of for have no access to each other's data. Finally, execution flows to the initial peer. Initial peer merges writes to the resp stream, and merges writes to the resp2 stream. It's done in conflict-free fashion. More than that, head of resp, resp2 streams will not change from each peer's point of view: it's immutable, and new values are only appended. However, different peers may have different order of the stream values, depending on the order of receiving these values.
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Imports & exports
|
||||
# Imports And Exports
|
||||
|
||||
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.
|
||||
An Aqua source file has a head and a body. The body contains function definitions, services, types, constants. The header manages what is imported from other files and what is exported.
|
||||
|
||||
### Import expression
|
||||
### Import Expression
|
||||
|
||||
The main way to import a file is via `import` expression:
|
||||
|
||||
@ -17,9 +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.
|
||||
The `use` expression makes it possible to import a subset of a file, or to alias imports to avoid namespace collisions.
|
||||
|
||||
{% embed url="https://github.com/fluencelabs/aqua/issues/30" %}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user