mirror of
https://github.com/fluencelabs/aqua-book
synced 2025-04-24 23:22:13 +00:00
GitBook: [alpha] 27 pages modified
This commit is contained in:
parent
4177e00f93
commit
04a6872697
21
README.md
21
README.md
@ -1,8 +1,23 @@
|
||||
# Aqua
|
||||
# Introduction
|
||||
|
||||
Fluence is an open protocol and a framework for internet or private cloud applications. Fluence provides a peer-to-peer development stack so that you can create applications free of proprietary cloud platforms, centralized APIs, and untrustworthy third-parties. The Fluence stack is open source and is maintained and governed by a community of developers.
|
||||
|
||||
At the core of Fluence is the open-source language **Aqua** that allows for the programming of peer-to-peer scenarios separately from the computations on peers. Applications are turned into hostless workflows over distributed function calls, which enables various levels of decentralization: from handling by a limited set of servers to complete peer-to-peer architecture by connecting user devices directly.
|
||||
|
||||
{% embed url="https://youtu.be/EcS0jT8a\_dk" %}
|
||||
|
||||
This book is dedicated to all things Aqua and currently in its **alpha** version and we expect to expand both Aqua's breadth and depth coverage over the coming weeks.
|
||||
|
||||
You can stay in touch or contact us via the following channels:
|
||||
|
||||
* [Discord](https://discord.gg/)
|
||||
* [Telegram](https://t.me/fluence_project)
|
||||
* [Aqua Github](https://github.com/fluencelabs/aqua)
|
||||
* [Youtube](https://www.youtube.com/channel/UC3b5eFyKRFlEMwSJ1BTjpbw)
|
||||
|
||||
|
||||
|
||||
[Aqua](https://github.com/fluencelabs/aqua) is a purpose-designed language to compose distributed services hosted on peer-to-peer nodes into applications and backends and part of Fluence Labs' Aquamarine stack and peer-to-peer programming model.
|
||||
|
||||
In addition to the language specification, Aqua provides a compiler, intermediate representation \(AIR\) and an execution stack, Aqua VM.
|
||||
|
||||
|
||||
|
||||
|
11
SUMMARY.md
11
SUMMARY.md
@ -1,11 +1,9 @@
|
||||
# Table of contents
|
||||
|
||||
* [Aqua](README.md)
|
||||
* [Tour De Aqua](tour-de-aqua.md)
|
||||
* [Introduction](README.md)
|
||||
* [Getting Started](getting-started/README.md)
|
||||
* [Installation](getting-started/installation.md)
|
||||
* [Debugging And Testing](getting-started/debugging-and-testing.md)
|
||||
* [Tools](getting-started/tools.md)
|
||||
* [Quick Start](getting-started/quick-start.md)
|
||||
* [Language](language/README.md)
|
||||
* [Basics](language/basics.md)
|
||||
* [Types](language/types.md)
|
||||
@ -18,7 +16,7 @@
|
||||
* [Iterative](language/flow/iterative.md)
|
||||
* [Abilities & Services](language/abilities-and-services.md)
|
||||
* [CRDT Streams](language/crdt-streams.md)
|
||||
* [Imports & exports](language/statements-1.md)
|
||||
* [Imports And Exports](language/statements-1.md)
|
||||
* [Expressions](language/expressions/README.md)
|
||||
* [Header](language/expressions/header.md)
|
||||
* [Functions](language/expressions/functions.md)
|
||||
@ -27,8 +25,5 @@
|
||||
* [Overrideable constants](language/expressions/overrideable-constants.md)
|
||||
* [Runtimes](runtimes/README.md)
|
||||
* [Aqua VM](runtimes/aqua-vm.md)
|
||||
* [Aqua Patterns](aqua-patterns.md)
|
||||
* [Library \(BuiltIns\)](library-builtins.md)
|
||||
* [Appendix](appendix.md)
|
||||
* [Foundations: π-calculus](foundations-p-calculus.md)
|
||||
|
||||
|
@ -1,10 +1,27 @@
|
||||
# Getting Started
|
||||
|
||||
## Installation
|
||||
[Aqua](https://github.com/fluencelabs/aqua), part of Fluence Lab's Aquamarine Web3 stack, is a purpose-built language to program peer-to-peer networks and compose distributed services hosted on peer-to-peer nodes into applications and backends.
|
||||
|
||||
* native
|
||||
* container
|
||||
* VSCode extension\(s\)
|
||||
In addition to the language specification, Aqua provides a compiler, which produces Aqua Intermediary Representation \(AIR\) and an execution stack, Aqua VM, that is part of every Fluence node implementation to execute AIR. Moreover, Aqua VM it a Wasm module that runs of ... and
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### Help And Support
|
||||
|
||||
* [Discord](https://discord.gg/)
|
||||
* [Telegram](https://t.me/fluence_project)
|
||||
* [https://github.com/fluencelabs/aqua](https://github.com/fluencelabs/aqua)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
* [Aqua Repo](https://github.com/fluencelabs/aqua)
|
||||
* [Aqua Playground](https://github.com/fluencelabs/aqua-playground)
|
||||
* [Aqua VM](https://github.com/fluencelabs/aquavm)
|
||||
* [VSCode Extension](https://github.com/fluencelabs/aqua-vscode)
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,28 @@
|
||||
# Installation
|
||||
|
||||
* native tools
|
||||
* devcontianer ?
|
||||
Both the Aqua compiler and support library can be installed natively with `npm`
|
||||
|
||||
To install the compiler:
|
||||
|
||||
```bash
|
||||
npm -g install @fluencelabs/aqua-cli
|
||||
```
|
||||
|
||||
and to make the Aqua library available to Typescript applications:
|
||||
|
||||
```bash
|
||||
npm -g install @fluencelabs/aqua-lib
|
||||
```
|
||||
|
||||
Moreover, a VSCode syntax-highlighting extension is available. In VSCode, click on the Extensions button, search for `aqua`and install the extension.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
120
getting-started/quick-start.md
Normal file
120
getting-started/quick-start.md
Normal file
@ -0,0 +1,120 @@
|
||||
# Quick Start
|
||||
|
||||
Every Fluence reference node comes with a set of builtin services which are accessible to Aqua programs. Let's use those readily available services to get the timestamp of a few of our peer-to-peer neighborhood nodes with Aqua.
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="Peer Timestamps With Aqua" %}
|
||||
```text
|
||||
-- timestamp_getter.aqua
|
||||
-- bring the builtin services into scope
|
||||
import "builtin.aqua"
|
||||
|
||||
-- create and identity service to join our results
|
||||
service Op2("op"):
|
||||
identity(s: u64)
|
||||
array(a: string, b: u64) -> string
|
||||
|
||||
-- function to get ten timestamps from our Kademlia
|
||||
-- neighborhood peers and return as an array of u64 timestamps
|
||||
-- the function arguement node is our peer id
|
||||
func ts_getter(node: string) -> []u64:
|
||||
-- create a streaming variable
|
||||
res: *[]u64
|
||||
-- execute on the pecified peer
|
||||
on node:
|
||||
-- get the base58 representation of the peer id
|
||||
k <- Op.string_to_b58(node)
|
||||
-- find all (default 20) neighborhood peers from k
|
||||
nodes <- Kademlia.neighborhood(k, false)
|
||||
-- for each peer in our neighborhood and in parallel
|
||||
for n <- nodes par:
|
||||
on n:
|
||||
-- try and get the peer's timestamp
|
||||
try:
|
||||
res <- Peer.timestamp_ms()
|
||||
-- flatten nine of our joined results
|
||||
Op2.identity(res!9)
|
||||
-- return an array of ten timestamps
|
||||
<- res
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% tab title="Compiling Aqua Script" %}
|
||||
```
|
||||
aqua-cli -i aqua-scripts -o air-scripts -a
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% tab title="Result" %}
|
||||
```
|
||||
fldist run_air -p air-scripts/timestamp_getter.ts_getter.air -d '{"node":"12D3KooWHLxVhUQyAuZe6AHMB29P7wkvTNMn7eDMcsqimJYLKREf"}' --generated
|
||||
[
|
||||
[
|
||||
1624928596292,
|
||||
1624928596291,
|
||||
1624928596291,
|
||||
1624928596299,
|
||||
1624928596295,
|
||||
1624928596286,
|
||||
1624928596295,
|
||||
1624928596284,
|
||||
1624928596293,
|
||||
1624928596289
|
||||
]
|
||||
]
|
||||
```
|
||||
{% endtab %}
|
||||
{% endtabs %}
|
||||
|
||||
In the Aqua code, we essentially create a workflow originating from our client peer to enumerate our neighbor peers from the Kademlia neighborhood based on our reference node specification, call on the builtin timestamp service on each peer and in parallel, join after we collect ten timestamps and return our u64 array of timestamps back to the client peer.
|
||||
|
||||
Once we have our file, lets copy it to a directory called aqua-scripts and create an empty directory. air-scripts. Now we can compile our aqua script with the aqua-cli tool and find our AIR file in the air-scripts directory:
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="Compile" %}
|
||||
```text
|
||||
aqua-cli -i aqua-scripts -o air-scripts -a
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% tab title="Result" %}
|
||||
```
|
||||
# in the air-script dir you should have the following file
|
||||
timestamp_getter.ts_getter.air
|
||||
```
|
||||
{% endtab %}
|
||||
{% endtabs %}
|
||||
|
||||
Once we have our AIR file we can either use Typescript or command line client. Let's use the command line client `flidst`:
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="Run Air scripts" %}
|
||||
```text
|
||||
# execute the AIR script from our compile phase with a peer id
|
||||
fldist run_air -p air-scripts/timestamp_getter.ts_getter.air -d '{"node":"12D3KooWHLxVhUQyAuZe6AHMB29P7wkvTNMn7eDMcsqimJYLKREf"}' --generated
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% tab title="Output" %}
|
||||
```
|
||||
# here we go: ten timestamps in micro seconds obtained in parallel
|
||||
[
|
||||
[
|
||||
1624928596292,
|
||||
1624928596291,
|
||||
1624928596291,
|
||||
1624928596299,
|
||||
1624928596295,
|
||||
1624928596286,
|
||||
1624928596295,
|
||||
1624928596284,
|
||||
1624928596293,
|
||||
1624928596289
|
||||
]
|
||||
]
|
||||
```
|
||||
{% endtab %}
|
||||
{% endtabs %}
|
||||
|
||||
And that's it. We now have ten reasonable timestamps from our Kademlia neighbors. See the [ts-oracle example](https://github.com/fluencelabs/examples/tree/main/ts-oracle) for the corresponding Aqua and Air files, respectively.
|
||||
|
@ -52,5 +52,3 @@ Reference:
|
||||
|
||||
* [Expressions](expressions/)
|
||||
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Header
|
||||
|
||||
### Header expressions
|
||||
## Header expressions
|
||||
|
||||
`import`
|
||||
|
||||
|
@ -4,8 +4,6 @@ description: Static configuration pieces that affect compilation
|
||||
|
||||
# Overrideable constants
|
||||
|
||||
|
||||
|
||||
`const`
|
||||
|
||||
Constant definition.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# 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.
|
||||
|
||||
|
@ -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/4177e00f9313f0e1eb0a60015e1c19a956c065bd/language/operators/conditional.md#try) or [otherwise](https://github.com/fluencelabs/aqua-book/tree/4177e00f9313f0e1eb0a60015e1c19a956c065bd/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/4177e00f9313f0e1eb0a60015e1c19a956c065bd/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/4177e00f9313f0e1eb0a60015e1c19a956c065bd/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…
x
Reference in New Issue
Block a user