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.
aqua
aqua AquaFile declares foo-- builtin.aqua declares `Op`use "@fluencelabs/aqua-lib/builtin.aqua"func foo():BuiltIn.Op.noop()
aqua
aqua 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:
aqua
aqua AquaFile declares foo-- builtin.aqua declares `Op`use "@fluencelabs/aqua-lib/builtin" as Renamedfunc foo():Renamed.Op.noop()
aqua
aqua 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 ...
:
aqua
aqua 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()
aqua
aqua 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.
aqua
aqua AquaFile declares foo-- builtin.aqua declares `Op`import "@fluencelabs/aqua-lib/builtin.aqua"func foo():Op.noop()
aqua
aqua 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 ...
:
aqua
aqua 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()
aqua
aqua 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 export
s in a file and they are all merged into one.
aqua
aqua 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
aqua
aqua 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 ...
:
aqua
aqua Libexport foo as foo_barfunc foo() -> bool:<- true
aqua
aqua 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
.