Modularity
Source File Structure
An Aqua source file has a header and a body. The body contains function definitions, services, types, constants. The header is dedicated to code management: it specifies the name of the module, what is declared by the module, what is exported from the module and what is imported into the module.
Giving a name to an Aqua module with aqua
Every Aqua source file should begin with aqua keyword followed by the the name of the aqua module presented by the file.
aqua-- `aqua` expression may only be on the very first line of the fileaqua AquaFile
aqua-- `aqua` expression may only be on the very first line of the fileaqua AquaFile
Module name can contain dots, e.g. aqua Aqua.File.
This name is used when the module is imported with use, see Importing other modules with use.
Specifying what is declared by the module with declares
The aqua AquaFile expression may optionally include a declares section. This section enumerates the elements that the module will make available for other modules that import it. If the declares section is omitted, the module does not declare anything for other modules to use.
aqua-- This module declares `CONST_NAME`, `ServiceName`, `MyType` and `fn`aqua AquaFile declares CONST_NAME, ServiceName, MyType, fnconst CONST_NAME = "something"service ServiceName:do_something()data MyType:result: i32func fn() -> string:<- CONST_NAME
aqua-- This module declares `CONST_NAME`, `ServiceName`, `MyType` and `fn`aqua AquaFile declares CONST_NAME, ServiceName, MyType, fnconst CONST_NAME = "something"service ServiceName:do_something()data MyType:result: i32func fn() -> string:<- CONST_NAME
To declare everything contained in the file, use declares *:
aqua-- This module declares `CONST_NAME`, `ServiceName`, `MyType` and `fn`aqua AquaFile declares *const CONST_NAME = "something"service ServiceName:do_something()data MyType:result: i32func fn() -> string:<- CONST_NAME
aqua-- This module declares `CONST_NAME`, `ServiceName`, `MyType` and `fn`aqua AquaFile declares *const CONST_NAME = "something"service ServiceName:do_something()data MyType:result: i32func fn() -> string:<- CONST_NAME
Note that symbols declared with declares are not exported to the host language. To export symbols to the host language, use export.
Importing other modules
Aqua modules can import other modules to use their declarations. There are two ways to import a module: with import and use.
With use
The use expression makes it possible to import a module as a named scope. The name of the scope is taken from aqua header of the imported module. Everything declared in the imported module is available in the current namespace as a member of the scope.
aquaaqua AquaFile declares foo-- builtin.aqua declares `Op`use "@fluencelabs/aqua-lib/builtin.aqua"func foo():BuiltIn.Op.noop()
aquaaqua AquaFile declares foo-- builtin.aqua declares `Op`use "@fluencelabs/aqua-lib/builtin.aqua"func foo():BuiltIn.Op.noop()
It is possible to rename the imported module with use ... as ... expression:
aquaaqua AquaFile declares foo-- builtin.aqua declares `Op`use "@fluencelabs/aqua-lib/builtin" as Renamedfunc foo():Renamed.Op.noop()
aquaaqua AquaFile declares foo-- builtin.aqua declares `Op`use "@fluencelabs/aqua-lib/builtin" as Renamedfunc foo():Renamed.Op.noop()
It is also possible to cherry-pick and rename imports using use ... as ... from ... as ...:
aquaaqua AquaFile declares foo-- builtin.aqua declares `Op`use Op as Noop from "@fluencelabs/aqua-lib/builtin" as Renamed-- multiple imports are allowed-- dependency.aqua declares functions `foo`, `baz` and `bar`import foo as f, baz, bar as b from "dependency.aqua" as Depfunc foo():Dep.f()Dep.baz()Dep.b()Renamed.Noop.noop()
aquaaqua AquaFile declares foo-- builtin.aqua declares `Op`use Op as Noop from "@fluencelabs/aqua-lib/builtin" as Renamed-- multiple imports are allowed-- dependency.aqua declares functions `foo`, `baz` and `bar`import foo as f, baz, bar as b from "dependency.aqua" as Depfunc foo():Dep.f()Dep.baz()Dep.b()Renamed.Noop.noop()
Creation of a scope with use makes it easier to avoid name clashes and to understand where the symbol comes from. Thus it is recommended to prefer use instead of import when possible.
With import
Another way to import a module is via import. In this case, everything declared in the imported module comes into the current namespace directly.
aquaaqua AquaFile declares foo-- builtin.aqua declares `Op`import "@fluencelabs/aqua-lib/builtin.aqua"func foo():Op.noop()
aquaaqua AquaFile declares foo-- builtin.aqua declares `Op`import "@fluencelabs/aqua-lib/builtin.aqua"func foo():Op.noop()
It is possible to cherry-pick and rename imports using import ... as ... from ...:
aquaaqua AquaFile declares foo-- builtin.aqua declares `Op`import Op as Noop from "@fluencelabs/aqua-lib/builtin"-- multiple imports are allowed-- dependency.aqua declares functions `foo`, `baz` and `bar`import foo as f, baz, bar as b from "dependency.aqua"func foo():f()baz()b()Noop.noop()
aquaaqua AquaFile declares foo-- builtin.aqua declares `Op`import Op as Noop from "@fluencelabs/aqua-lib/builtin"-- multiple imports are allowed-- dependency.aqua declares functions `foo`, `baz` and `bar`import foo as f, baz, bar as b from "dependency.aqua"func foo():f()baz()b()Noop.noop()
Imports resolution
To learn how compiler resolves the import path, see JS Aqua API.
.aqua extension in import and use expressions can be omitted. So, import "builtin.aqua" does exactly the same as import "builtin".
Exporting to the host language with export
Inside Aqua language code modularity is achieved with declares, import and use on module level (see also Abilities as more fine grained method of code organization). However, what should be exported to the host language depends on the particular use case of aqua code and has nothing to do with code management inside Aqua. This is why exporting to the host language is a separate concept inside Aqua.
It is possible to specify what should be exported to the host language with export. Exporting symbols that were imported from other modules is allowed. There could be several exports in a file and they are all merged into one.
aquaaqua Libimport bar from "lib"-- Exported functions and services will be compiled-- into the host languageexport fooexport bar, MySrvservice MySrv:call_something()func foo() -> bool:<- true
aquaaqua Libimport bar from "lib"-- Exported functions and services will be compiled-- into the host languageexport fooexport bar, MySrvservice MySrv:call_something()func foo() -> bool:<- true
To export a symbol under a different name, use export ... as ...:
aquaaqua Libexport foo as foo_barfunc foo() -> bool:<- true
aquaaqua Libexport foo as foo_barfunc foo() -> bool:<- true
Note that export does not make the symbol available for other modules that import the current module. To make a symbol available for other modules, use declares.