mirror of
https://github.com/fluencelabs/aqua-book
synced 2024-12-04 23:30:18 +00:00
GitBook: [alpha] 30 pages modified
This commit is contained in:
parent
bec7e16368
commit
5c46b605e8
@ -15,8 +15,9 @@
|
||||
* [Iterative](language/flow/iterative.md)
|
||||
* [Abilities & Services](language/abilities-and-services.md)
|
||||
* [CRDT Streams](language/crdt-streams.md)
|
||||
* [Imports And Exports](language/header/README.md)
|
||||
* [Control, Scope And Visibility](language/header/control-scope-and-visibility.md)
|
||||
* [Imports And Exports](language/header.md)
|
||||
* [Control, Scope And Visibility](language/control-scope-and-visibility/README.md)
|
||||
* [Control, Scope And Visibility](language/control-scope-and-visibility/control-scope-and-visibility.md)
|
||||
* [Expressions](language/expressions/README.md)
|
||||
* [Header](language/expressions/header.md)
|
||||
* [Functions](language/expressions/functions.md)
|
||||
|
@ -34,7 +34,7 @@ Aqua compiler's versioning scheme is the following: `0.BREAKING.ENHANCING.RELEAS
|
||||
|
||||
### [0.1.14](https://github.com/fluencelabs/aqua/releases/tag/0.1.14) – August 20, 2021
|
||||
|
||||
* Aqua file header changes: `module`, `declares`, `use`, `export` expressions \([\#245](https://github.com/fluencelabs/aqua/pull/245)\), see [Imports and Exports](language/header/) for the docs.
|
||||
* Aqua file header changes: `module`, `declares`, `use`, `export` expressions \([\#245](https://github.com/fluencelabs/aqua/pull/245)\), see [Imports and Exports](language/header.md) for the docs.
|
||||
* Experimental Scala.js build of the compiler \([\#247](https://github.com/fluencelabs/aqua/pull/247)\)
|
||||
|
||||
### [0.1.13](https://github.com/fluencelabs/aqua/releases/tag/0.1.13) – August 10, 2021
|
||||
|
@ -5,7 +5,7 @@ Both the Aqua compiler and support library can be installed natively with `npm`
|
||||
To install the compiler:
|
||||
|
||||
```bash
|
||||
npm -g install @fluencelabs/aqua-cli
|
||||
npm -g install @fluencelabs/aqua
|
||||
```
|
||||
|
||||
and to make the Aqua library available to Typescript applications:
|
||||
|
@ -42,12 +42,12 @@ func ts_getter(node: string) -> []u64:
|
||||
|
||||
The Aqua script essentially creates a workflow originating from our client peer to enumerate neighbor peers for our reference node, calls on the builtin timestamp service on each peer in parallel, joins the results stream after we collect ten timestamps and return our u64 array of timestamps back to the client peer.
|
||||
|
||||
See the [ts-oracle example](https://github.com/fluencelabs/examples/tree/main/ts-oracle) for the corresponding Aqua files in the `aqua-script` directory. Now that we have our script, let's compile it with the `aqua-cli` tool and find our AIR file in the `air-scripts` directory:
|
||||
See the [ts-oracle example](https://github.com/fluencelabs/examples/tree/d52f06dfc3d30799fe6bd8e3e602c8ea1d1b8e8a/aqua-examples/ts-oracle) for the corresponding Aqua files in the `aqua-script` directory. Now that we have our script, let's compile it with the `aqua-cli` tool and find our AIR file in the `air-scripts` directory:
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="Compile" %}
|
||||
```bash
|
||||
aqua-cli -i aqua-scripts -o air-scripts -a
|
||||
aqua -i aqua-scripts -o air-scripts -a
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
|
@ -48,7 +48,7 @@ Advanced parallelism:
|
||||
|
||||
Code management:
|
||||
|
||||
* [Imports & exports](header/)
|
||||
* [Imports & exports](header.md)
|
||||
|
||||
Reference:
|
||||
|
||||
|
353
language/control-scope-and-visibility/README.md
Normal file
353
language/control-scope-and-visibility/README.md
Normal file
@ -0,0 +1,353 @@
|
||||
# Control, Scope And Visibility
|
||||
|
||||
##
|
||||
|
||||
In Aqua, the default namespace of a module is the file name and all declarations, i.e., data, services and functions, are public.
|
||||
|
||||
For example, a `default.aqua` file:
|
||||
|
||||
```text
|
||||
-- default_foo.aqua
|
||||
|
||||
|
||||
func foo() -> string:
|
||||
<- "I am a visible foo func that compiles"
|
||||
```
|
||||
|
||||
Provides visibility to all members, i.e. `foo`.
|
||||
|
||||
Let's compile the file:
|
||||
|
||||
```text
|
||||
aqua -i aqua-scripts -o compiled-aqua
|
||||
```
|
||||
|
||||
To obtain Typescript wrapped AIR, `default_foo.ts` in the `compiled-aqua` directory:
|
||||
|
||||
```text
|
||||
import { FluenceClient, PeerIdB58 } from '@fluencelabs/fluence';
|
||||
import { RequestFlowBuilder } from '@fluencelabs/fluence/dist/api.unstable';
|
||||
import { RequestFlow } from '@fluencelabs/fluence/dist/internal/RequestFlow';
|
||||
|
||||
|
||||
// Services
|
||||
|
||||
|
||||
// Functions
|
||||
|
||||
export async function foo(client: FluenceClient, config?: {ttl?: number}): Promise<string> {
|
||||
let request: RequestFlow;
|
||||
const promise = new Promise<string>((resolve, reject) => {
|
||||
const r = new RequestFlowBuilder()
|
||||
.disableInjections()
|
||||
.withRawScript(
|
||||
`
|
||||
(xor
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(xor
|
||||
(call %init_peer_id% ("callbackSrv" "response") ["I am a visible foo func that compiles"])
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1])
|
||||
)
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2])
|
||||
)
|
||||
|
||||
`,
|
||||
)
|
||||
.configHandler((h) => {
|
||||
h.on('getDataSrv', '-relay-', () => {
|
||||
return client.relayPeerId!;
|
||||
});
|
||||
|
||||
h.onEvent('callbackSrv', 'response', (args) => {
|
||||
const [res] = args;
|
||||
resolve(res);
|
||||
});
|
||||
|
||||
h.onEvent('errorHandlingSrv', 'error', (args) => {
|
||||
// assuming error is the single argument
|
||||
const [err] = args;
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.handleScriptError(reject)
|
||||
.handleTimeout(() => {
|
||||
reject('Request timed out for foo');
|
||||
})
|
||||
if(config && config.ttl) {
|
||||
r.withTTL(config.ttl)
|
||||
}
|
||||
request = r.build();
|
||||
});
|
||||
await client.initiateFlow(request!);
|
||||
return promise;
|
||||
}
|
||||
```
|
||||
|
||||
Regardless of your output target, i.e. AIR or Typescript, the default module namespace is `default_foo` and `foo` is the compiled function. If you are following along, check your `compiled-aqua` directory for the output file.
|
||||
|
||||
While the default approach is handy for single file, single module development, it makes for inefficient dependency management. The remainder of this section introduces the scoping and visibility concepts available in Aqua to effectively manage dependencies.
|
||||
|
||||
### Visibility In Aqua Exports
|
||||
|
||||
By default, all declarations in a module, i.e., _data_, _service_ and _func_, are public. With the `module` declaration, Aqua allows developers to create named modules and define membership visibility where the default visibility of `module` is private and module members do not get compiled.
|
||||
|
||||
Let's create an `export.aqua` file to check things out:
|
||||
|
||||
```text
|
||||
-- export.aqua
|
||||
module Export
|
||||
|
||||
func foo() -> string:
|
||||
<- "I am Export foo"
|
||||
```
|
||||
|
||||
When we compile `export.aqua`
|
||||
|
||||
```text
|
||||
aqua -i aqua-scripts -o compiled-aqua
|
||||
```
|
||||
|
||||
nothing gets compiled as expected:
|
||||
|
||||
```text
|
||||
2021.09.02 11:31:41 [INFO] Aqua Compiler 0.2.1-219
|
||||
2021.09.02 11:31:42 [INFO] Source /Users/bebo/localdev/aqua-245/documentation-examples/aqua-scripts/export.aqua: compilation OK (nothing to emit)
|
||||
```
|
||||
|
||||
Again, you can check the output directory, `compiled-aqua` , for the lack of output files. By corollary, `foo` cannot be imported from another files. For example:
|
||||
|
||||
```text
|
||||
-- import.aqua
|
||||
|
||||
import "module_foo.aqua"
|
||||
|
||||
|
||||
func wrapped_foo() -> string:
|
||||
res <- foo()
|
||||
<- res
|
||||
```
|
||||
|
||||
Results in a compile failure since `foo` is not visible to `import.aqua`:
|
||||
|
||||
```text
|
||||
this is not working as expected. Issue filed.
|
||||
```
|
||||
|
||||
|
||||
|
||||
In order to make module members available for use by other modules, we can use `declares`. For example,
|
||||
|
||||
```text
|
||||
-- export.aqua
|
||||
module ExportModule declares foo
|
||||
|
||||
func bar() -> string:
|
||||
<- " I am MyFooBar bar"
|
||||
|
||||
func foo() -> string:
|
||||
res <- bar()
|
||||
<- res
|
||||
```
|
||||
|
||||
Keeps `bar` private but exposes `foo` as public for use by another module only. That is, compiling just `expert.aqua` does not create output if compiled directly:
|
||||
|
||||
```text
|
||||
2021.09.02 16:33:53 [INFO] Aqua Compiler 0.2.1-219
|
||||
2021.09.02 16:33:54 [INFO] Source /Users/bebo/localdev/aqua-245/documentation-examples/aqua-scripts/export.aqua: compilation OK (nothing to emit)
|
||||
```
|
||||
|
||||
Let's add a module consuming `foo`:
|
||||
|
||||
```text
|
||||
-- import.aqua
|
||||
import "export.aqua"
|
||||
|
||||
func foo_wrapper() -> string:
|
||||
res <- foo()
|
||||
<- res
|
||||
```
|
||||
|
||||
results in the compilation of `foo` and `foo_wrapper`:
|
||||
|
||||
```text
|
||||
2021.09.02 16:37:18 [INFO] Aqua Compiler 0.2.1-219
|
||||
2021.09.02 16:37:18 [INFO] Source /Users/bebo/localdev/aqua-245/documentation-examples/aqua-scripts/export.aqua: compilation OK (nothing to emit)
|
||||
2021.09.02 16:37:18 [INFO] Result /Users/bebo/localdev/aqua-245/documentation-examples/compiled-aqua/import.ts: compilation OK (1 functions)
|
||||
```
|
||||
|
||||
Check out the output file, `compiled-aqua/import.ts`:
|
||||
|
||||
|
||||
```text
|
||||
export async function foo_wrapper(client: FluenceClient, config?: {ttl?: number}): Promise<string> {
|
||||
let request: RequestFlow;
|
||||
const promise = new Promise<string>((resolve, reject) => {
|
||||
const r = new RequestFlowBuilder()
|
||||
.disableInjections()
|
||||
.withRawScript(
|
||||
`
|
||||
(xor
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(xor
|
||||
(call %init_peer_id% ("callbackSrv" "response") [" I am MyFooBar bar"])
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1])
|
||||
)
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2])
|
||||
)
|
||||
|
||||
`,
|
||||
)
|
||||
.configHandler((h) => {
|
||||
h.on('getDataSrv', '-relay-', () => {
|
||||
return client.relayPeerId!;
|
||||
});
|
||||
|
||||
h.onEvent('callbackSrv', 'response', (args) => {
|
||||
const [res] = args;
|
||||
resolve(res);
|
||||
});
|
||||
|
||||
h.onEvent('errorHandlingSrv', 'error', (args) => {
|
||||
// assuming error is the single argument
|
||||
const [err] = args;
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.handleScriptError(reject)
|
||||
.handleTimeout(() => {
|
||||
reject('Request timed out for foo_wrapper');
|
||||
})
|
||||
if(config && config.ttl) {
|
||||
r.withTTL(config.ttl)
|
||||
}
|
||||
request = r.build();
|
||||
});
|
||||
await client.initiateFlow(request!);
|
||||
return promise;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
Of course, if we change `import.aqua` to:
|
||||
|
||||
```text
|
||||
import bar from "export.aqua"
|
||||
|
||||
func bar_wrapper() -> string:
|
||||
res <- bar()
|
||||
<- res
|
||||
```
|
||||
|
||||
we get the expected error:
|
||||
|
||||
```text
|
||||
import bar from "export.aqua"
|
||||
^^^===================
|
||||
Imported file declares [foo], no bar declared. Try adding `declares *` to that file.
|
||||
```
|
||||
|
||||
since `bar` was not declared to be visible outside the module namespace.
|
||||
|
||||
As indicated in the error message, `declares *` makes all members of the namespace public.
|
||||
|
||||
Note: `exports` isn't working yet
|
||||
|
||||
### Scoping Aqua Imports
|
||||
|
||||
We already encountered the `import` statement earlier. Using `import` with the file name, e.g., `import "export.aqua"`, imports all namespace members visible for export. However, we can scope import with the `from` modifier, e.g., `import foo from "file.aqua"`, to limit our imports and subsequent compilation outputs.
|
||||
|
||||
Moreover, we can associate a namespace with an `import`, e.g., `import foo from "export.aqua" as ExportModule`, which then allows us to use `foo` as:
|
||||
|
||||
```text
|
||||
import foo from "export.aqua" as ExportModule
|
||||
|
||||
func demo_foo() -> string:
|
||||
res <- ExportModule.foo()
|
||||
<- res
|
||||
```
|
||||
|
||||
and we can rename our imports with the `as` modifier with or without the `module` modifier:
|
||||
|
||||
```text
|
||||
import foo as ex_foo from "export.aqua" as ExportModule
|
||||
|
||||
func demo_foo() -> string:
|
||||
res <- ExportModule.ex_foo()
|
||||
<- res
|
||||
```
|
||||
|
||||
In addition to `import`, we also have the `use` keyword available to link and scope. The difference between`use` and `import` is that `use` brings in module namespaces declared in the referenced file. For example:
|
||||
|
||||
```text
|
||||
-- export.aqua
|
||||
module ExportModule declares foo
|
||||
|
||||
func foo() -> string:
|
||||
<- "I am a foo fighter"
|
||||
```
|
||||
|
||||
declares the `ExportModule` namespace and makes `foo` visible. We can now bring `foo` into scope by means of its module namespace `ExportModule` in our import file without having to \(re-\) declare anything:
|
||||
|
||||
```text
|
||||
-- import.aqua
|
||||
use "export.aqua"
|
||||
|
||||
func foo_wrapper() -> string:
|
||||
res <- ExportModule.foo()
|
||||
<- res
|
||||
```
|
||||
|
||||
|
||||
|
||||
If, for validation purposes, we use:
|
||||
|
||||
```text
|
||||
-- import.aqua
|
||||
use "export.aqua"
|
||||
|
||||
func foo_wrapper() -> string:
|
||||
res <- foo()
|
||||
<- res
|
||||
```
|
||||
|
||||
We get the expected error:
|
||||
|
||||
```text
|
||||
3 func foo_wrapper() -> string:
|
||||
4 res <- foo()
|
||||
5 <- res
|
||||
^^^
|
||||
Undefined name, available: ExportModule.foo, host_peer_id, ExportModule.host_peer_id, ExportModule.%last_error%, ExportModule.nil, nil, %last_error%
|
||||
```
|
||||
|
||||
If we don't declare a module name in our reference file and use `use` in our consumer file, the compiler will throw an error:
|
||||
|
||||
```text
|
||||
-- export.aqua
|
||||
func foo() -> string:
|
||||
<- "I am a foo fighter"
|
||||
```
|
||||
|
||||
```text
|
||||
use "export.aqua"
|
||||
^^^^^^^^^^^^^
|
||||
Used module has no `module` header. Please add `module` header or use ... as ModuleName, or switch to import
|
||||
```
|
||||
|
||||
Following the compiler instructions as can declare a module name in the importing file:
|
||||
|
||||
```text
|
||||
use "export.aqua" as ModuleName
|
||||
|
||||
func foo_wrapper() -> string:
|
||||
res <- ModuleName.foo()
|
||||
<- res
|
||||
```
|
||||
|
||||
Note: also doesn't compile just yet
|
||||
|
@ -0,0 +1,316 @@
|
||||
# Control, Scope And Visibility
|
||||
|
||||
In Aqua, the default namespace of a module is the file name and all declarations, i.e., data, services and functions, are public.
|
||||
|
||||
For example, the default.aqua file:
|
||||
|
||||
```python
|
||||
-- default_foo.aqua
|
||||
|
||||
|
||||
func foo() -> string:
|
||||
<- "I am a visible foo func that compiles"
|
||||
```
|
||||
|
||||
Which we compile with
|
||||
|
||||
```bash
|
||||
aqua -i aqua-scripts -o compiled-aqua
|
||||
```
|
||||
|
||||
to obtain Typescript wrapped AIR, `default_foo.ts` in the `compiled-aqua` directory:
|
||||
|
||||
```typescript
|
||||
import { FluenceClient, PeerIdB58 } from '@fluencelabs/fluence';
|
||||
import { RequestFlowBuilder } from '@fluencelabs/fluence/dist/api.unstable';
|
||||
import { RequestFlow } from '@fluencelabs/fluence/dist/internal/RequestFlow';
|
||||
|
||||
|
||||
// Services
|
||||
|
||||
|
||||
// Functions
|
||||
|
||||
export async function foo(client: FluenceClient, config?: {ttl?: number}): Promise<string> {
|
||||
let request: RequestFlow;
|
||||
const promise = new Promise<string>((resolve, reject) => {
|
||||
const r = new RequestFlowBuilder()
|
||||
.disableInjections()
|
||||
.withRawScript(
|
||||
`
|
||||
(xor
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(xor
|
||||
(call %init_peer_id% ("callbackSrv" "response") ["I am a visible foo func that compiles"])
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1])
|
||||
)
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2])
|
||||
)
|
||||
|
||||
`,
|
||||
)
|
||||
.configHandler((h) => {
|
||||
h.on('getDataSrv', '-relay-', () => {
|
||||
return client.relayPeerId!;
|
||||
});
|
||||
|
||||
h.onEvent('callbackSrv', 'response', (args) => {
|
||||
const [res] = args;
|
||||
resolve(res);
|
||||
});
|
||||
|
||||
h.onEvent('errorHandlingSrv', 'error', (args) => {
|
||||
// assuming error is the single argument
|
||||
const [err] = args;
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.handleScriptError(reject)
|
||||
.handleTimeout(() => {
|
||||
reject('Request timed out for foo');
|
||||
})
|
||||
if(config && config.ttl) {
|
||||
r.withTTL(config.ttl)
|
||||
}
|
||||
request = r.build();
|
||||
});
|
||||
await client.initiateFlow(request!);
|
||||
return promise;
|
||||
}
|
||||
```
|
||||
|
||||
Regardless of your output target, i.e. raw AIR or Typescript wrapped AIR, the default module namespace is `default_foo` and `foo` is the compiled function.
|
||||
|
||||
While this default approach is handy for single file, single module development, it makes for inefficient dependency management and unnecessary compilations for multi-module projects. The remainder of this section introduces the scoping and visibility concepts available in Aqua to effectively manage dependencies.
|
||||
|
||||
### Managing Visibility With `module` and `declare`
|
||||
|
||||
By default, all declarations in a module, i.e., _data_, _service_ and _func_, are public. With the `module` declaration, Aqua allows developers to create named modules and define membership visibility where the default visibility of `module` is private. That is, with the `module` declaration all module members are private and do not get compiled.
|
||||
|
||||
Let's create an `export.aqua` file like so:
|
||||
|
||||
```python
|
||||
module Export
|
||||
|
||||
func foo() -> string:
|
||||
<- "I am Export foo"
|
||||
```
|
||||
|
||||
When we compile `export.aqua`
|
||||
|
||||
```bash
|
||||
aqua -i aqua-scripts -o compiled-aqua
|
||||
```
|
||||
|
||||
nothing gets compiled as expected:
|
||||
|
||||
```bash
|
||||
2021.09.02 11:31:41 [INFO] Aqua Compiler 0.2.1-219
|
||||
2021.09.02 11:31:42 [INFO] Source /Users/bebo/localdev/aqua-245/documentation-examples/aqua-scripts/export.aqua: compilation OK (nothing to emit)
|
||||
```
|
||||
|
||||
You can further check the output directory, `compiled-aqua`, in our case, for the lack of output files. By corollary, `foo` cannot be imported from another files. For example:
|
||||
|
||||
```python
|
||||
-- import.aqua
|
||||
|
||||
import "export.aqua"
|
||||
|
||||
|
||||
func wrapped_foo() -> string:
|
||||
res <- foo()
|
||||
<- res
|
||||
```
|
||||
|
||||
Results in compile failure since `foo` is not visible to `import.aqua`:
|
||||
|
||||
```python
|
||||
6 func wrapped_foo() -> string:
|
||||
7 res <- foo()
|
||||
^^^==
|
||||
Undefined arrow, available: HOST_PEER_ID, INIT_PEER_ID, nil, LAST_ERROR
|
||||
8 <- res
|
||||
```
|
||||
|
||||
We can use `declares` to create visibility for a `module` namespace for **consuming** modules. For example,
|
||||
|
||||
```python
|
||||
-- export.aqua
|
||||
module Export declares foo
|
||||
|
||||
func bar() -> string:
|
||||
<- " I am MyFooBar bar"
|
||||
|
||||
func foo() -> string:
|
||||
res <- bar()
|
||||
<- res
|
||||
```
|
||||
|
||||
in and by itself does not result in compiled Aqua:
|
||||
|
||||
```bash
|
||||
aqua -i aqua-scripts -o compiled-aqua -a
|
||||
Aqua JS: node /Users/bebo/.nvm/versions/node/v14.16.0/lib/node_modules/@fluencelabs/aqua/aqua.js -i aqua-scripts -o compiled-aqua -a
|
||||
Aqua JS:
|
||||
Aqua JS: 2021.09.08 13:36:17 [INFO] Aqua Compiler 0.3.0-222
|
||||
2021.09.08 13:36:21 [INFO] Source /Users/bebo/localdev/aqua-245/documentation-examples/aqua-scripts/export.aqua: compilation OK (nothing to emit)
|
||||
```
|
||||
|
||||
But once we link from another module, e.g.:
|
||||
|
||||
```python
|
||||
import foo from "export.aqua"
|
||||
|
||||
func foo_wrapper() -> string:
|
||||
res <- foo()
|
||||
<- res
|
||||
```
|
||||
|
||||
We get the appropriate result:
|
||||
|
||||
```bash
|
||||
2021.09.08 13:40:17 [INFO] Source /Users/bebo/localdev/aqua-245/documentation-examples/aqua-scripts/export.aqua: compilation OK (nothing to emit)
|
||||
2021.09.08 13:40:17 [INFO] Result /Users/bebo/localdev/aqua-245/documentation-examples/compiled-aqua/import.ts: compilation OK (1 functions)
|
||||
```
|
||||
|
||||
in form of `import.ts`:
|
||||
|
||||
```typescript
|
||||
// compiled-aqua/import.ts
|
||||
import { FluencePeer } from '@fluencelabs/fluence';
|
||||
import {
|
||||
ResultCodes,
|
||||
RequestFlow,
|
||||
RequestFlowBuilder,
|
||||
CallParams,
|
||||
} from '@fluencelabs/fluence/dist/internal/compilerSupport/v1';
|
||||
|
||||
|
||||
// Services
|
||||
|
||||
|
||||
// Functions
|
||||
|
||||
export function foo_wrapper(config?: {ttl?: number}) : Promise<string>;
|
||||
export function foo_wrapper(peer: FluencePeer, config?: {ttl?: number}) : Promise<string>;
|
||||
export function foo_wrapper(...args) {
|
||||
let peer: FluencePeer;
|
||||
|
||||
let config;
|
||||
if (args[0] instanceof FluencePeer) {
|
||||
peer = args[0];
|
||||
config = args[1];
|
||||
} else {
|
||||
peer = FluencePeer.default;
|
||||
config = args[0];
|
||||
}
|
||||
|
||||
let request: RequestFlow;
|
||||
const promise = new Promise<string>((resolve, reject) => {
|
||||
const r = new RequestFlowBuilder()
|
||||
.disableInjections()
|
||||
.withRawScript(
|
||||
`
|
||||
(xor
|
||||
(seq
|
||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
||||
(xor
|
||||
(call %init_peer_id% ("callbackSrv" "response") [" I am MyFooBar bar"])
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1])
|
||||
)
|
||||
)
|
||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2])
|
||||
)
|
||||
|
||||
`,
|
||||
)
|
||||
.configHandler((h) => {
|
||||
h.on('getDataSrv', '-relay-', () => {
|
||||
return peer.connectionInfo.connectedRelay ;
|
||||
});
|
||||
|
||||
h.onEvent('callbackSrv', 'response', (args) => {
|
||||
const [res] = args;
|
||||
resolve(res);
|
||||
});
|
||||
|
||||
h.onEvent('errorHandlingSrv', 'error', (args) => {
|
||||
const [err] = args;
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.handleScriptError(reject)
|
||||
.handleTimeout(() => {
|
||||
reject('Request timed out for foo_wrapper');
|
||||
})
|
||||
if(config && config.ttl) {
|
||||
r.withTTL(config.ttl)
|
||||
}
|
||||
request = r.build();
|
||||
});
|
||||
peer.internals.initiateFlow(request!);
|
||||
return promise;
|
||||
}
|
||||
```
|
||||
|
||||
Of course, if we change `import.aqua` to include the private `bar`:
|
||||
|
||||
```python
|
||||
import bar from "export.aqua"
|
||||
|
||||
func bar_wrapper() -> string:
|
||||
res <- bar()
|
||||
<- res
|
||||
```
|
||||
|
||||
We get the expected error:
|
||||
|
||||
```python
|
||||
import bar from "export.aqua"
|
||||
^^^===================
|
||||
Imported file declares [foo], no bar declared. Try adding `declares *` to that file.
|
||||
```
|
||||
|
||||
As indicated in the error message, `declares *` makes all members of the namespace public, although we can be quite fine-grained and use a comma separated list of members we want to be visible, such as `declares foo, bar`.
|
||||
|
||||
### Scoping Inclusion With `use` and `import`
|
||||
|
||||
We already encountered the `import` statement earlier. Using `import` with the file name, e.g., `import "export.aqua"`, imports all visible, i.e., public, members from the dependency. We can manage import granularity with the `from` modifier, e.g., `import foo from "file.aqua"`, to limit our imports and subsequent compilation outputs. Moreover, we can alias imported declarations with the `as` modifier, e.g.,`import foo as HighFoo, bar as LowBar from "export_file.aqua"`.
|
||||
|
||||
In addition to `import`, we also have the `use` keyword available to link and scope. The difference between`use` and `import` is that `use` brings in module namespaces declared in the referenced source file. For example:
|
||||
|
||||
```python
|
||||
-- export.aqua
|
||||
module ExportModule declares foo
|
||||
|
||||
func foo() -> string:
|
||||
<- "I am a foo fighter"
|
||||
```
|
||||
|
||||
declares the `ExportModule` namespace and makes `foo` visible. We can now bring `foo` into scope by means of its module namespace `ExportModule` in our import file without having to \(re-\) declare anything:
|
||||
|
||||
```python
|
||||
-- import.aqua
|
||||
use "export.aqua"
|
||||
|
||||
func foo -> string:
|
||||
res <- ExportModule.foo()
|
||||
<- res
|
||||
```
|
||||
|
||||
This example already illustrates the power of `use` as we now can declare a local `foo` function rather than the `foo_wrapper` we used earlier. `use` provides very clear namespace separation that is fully enforced at the compiler level allowing developers to build, update and extend complex code bases with clear namespace separation by default.
|
||||
|
||||
The default behavior for `use` is to use the dependent filename if no module declaration was provided. Moreover, we can use the `as` modifier to change the module namespace. Continuing with the above example:
|
||||
|
||||
```python
|
||||
-- import.aqua
|
||||
use "export.aqua" as RenamedExport
|
||||
|
||||
func foo() -> string:
|
||||
-- res <- ExportModule.foo() --< this fails
|
||||
res <- RenamedExport.foo()
|
||||
<- res
|
||||
```
|
||||
|
@ -12,5 +12,5 @@ import "path/to/file.aqua"
|
||||
|
||||
The to be imported file path is first resolved relative to the source file path followed by checking for an `-imports` directories.
|
||||
|
||||
See [Imports & Exports](../header/) for details.
|
||||
See [Imports & Exports](../header.md) for details.
|
||||
|
||||
|
@ -43,12 +43,16 @@ func foo():
|
||||
|
||||
Aqua compiler takes a source directory and a list of import directories \(usually with `node_modules` as a default\). You can use relative paths to `.aqua` files, relatively to the current file's path, and to import folders.
|
||||
|
||||
{% hint style="info" %}
|
||||
`.aqua` extension in `import` and `use` expressions can be ommited. So, `import "builtin.aqua"` does exactly the same as `import "builtin"`.
|
||||
{% endhint %}
|
||||
|
||||
Everything defined in the file is imported into the current namespace.
|
||||
|
||||
You can cherry-pick and rename imports using `import ... from` expression:
|
||||
|
||||
```python
|
||||
import Op as Noop from "@fluencelabs/aqua-lib/builtin.aqua"
|
||||
import Op as Noop from "@fluencelabs/aqua-lib/builtin"
|
||||
|
||||
func foo():
|
||||
Noop.noop()
|
||||
@ -59,7 +63,7 @@ func foo():
|
||||
The `use` expression makes it possible to import a file into a named scope.
|
||||
|
||||
```python
|
||||
use Op from "@fluencelabs/aqua-lib/builtin.aqua" as BuiltIn
|
||||
use Op from "@fluencelabs/aqua-lib/builtin" as BuiltIn
|
||||
|
||||
func foo():
|
||||
BuiltIn.Op.noop()
|
||||
@ -84,7 +88,7 @@ Another problem is libraries distribution. If a developer wants to deliver an `.
|
||||
`export` lets a developer decide what exactly is going to be exported, including imported functions.
|
||||
|
||||
```python
|
||||
import bar from "lib.aqua"
|
||||
import bar from "lib"
|
||||
|
||||
-- Exported functions and services will be compiled for the host language
|
||||
-- You can use several `export` expressions
|
||||
|
@ -166,7 +166,7 @@ data MyServiceType:
|
||||
flag: bool
|
||||
```
|
||||
|
||||
See [Imports and Exports](header/#module) for module declarations.
|
||||
See [Imports and Exports](header.md#module) for module declarations.
|
||||
|
||||
{% embed url="https://github.com/fluencelabs/aqua/blob/main/types/src/main/scala/aqua/types/Type.scala" caption="See the types system implementation" %}
|
||||
|
||||
|
@ -34,7 +34,7 @@ After running `npm i`, you will have `@fluencelabs/aqua-lib` in `node_modules`
|
||||
|
||||
### In Aqua
|
||||
|
||||
After the library is downloaded, you can import it in your `.aqua` script as documented in [Imports And Exports](../language/header/):
|
||||
After the library is downloaded, you can import it in your `.aqua` script as documented in [Imports And Exports](../language/header.md):
|
||||
|
||||
```javascript
|
||||
import "@fluencelabs/aqua-lib/builtin.aqua"
|
||||
|
Loading…
Reference in New Issue
Block a user