5.4.2 Modules
On this page:
5.4.2.1 Statements
5.4.2.1.1 export
export
5.4.2.1.2 import
5.4.2.1.2.1 Local imports
import
from
5.4.2.1.2.2 Package imports
5.4.2.2 Expressions
5.4.2.2.1 Reach.App
Reach
App
5.4.2.2.1.1 Deprecated long-form
0.1.3
5.4.2 Modules

A Reach source file is a textual file which specifies a Reach module. It is traditionally given the file extension rsh, e.g. "dao.rsh".

A module starts with 'reach 0.1'; followed by a sequence of imports and identifier definitions.

See the guide section on versions to understand how Reach uses version numbers like this.

5.4.2.1 Statements

Any statements valid for a computation are valid for a module. However, some additional statements are allowed.

5.4.2.1.1 export

Module-level identifier definitions may be exported by writing export in front of them. For example,

export const x = 1;
export const [a, b, ...more] = [ 0, 1, 2, 3, 4 ];
export function add1(x) { return x + 1; };
are valid exports.

Module-level identifiers may also be exported after the fact, and may be renamed during export. For example:

const w = 2;
const z = 0;
export {w, z as zero};

Identifiers from other modules may be re-exported (and renamed), even if they are not imported in the current module. For example:

export {u, x as other_x} from './other-module.rsh';

An exported identifier in a given module may be imported by other modules.

Exports are also exposed to the frontend via getExports. Functions are only exposed if they are typed, that is, if they are constructed with is.

5.4.2.1.2 import

Reach supports two types of module imports: local imports, which refer to modules that exist within your project, and package imports, which refer to remote libraries that may be fetched from external sources such as GitHub.

Read the guide section on packages for more details.

Package imports are easily distinguished from local imports by a mandatory @ character at the beginning of the path string.

5.4.2.1.2.1 Local imports

import 'games-of-chance.rsh';

When a module, X, contains a local import, written import "LIB.rsh";, then the path "LIB.rsh" must resolve to another Reach source file. The exports from the module defined by "LIB.rsh" are included in the set of bound identifiers in X.

import {flipCoin, rollDice as d6} from 'games-of-chance.rsh';

Import statements may limit or rename the imported identifiers.

import * as gamesOfChance from 'games-of-chance.rsh';

Imports may instead bind the entire module to a single identifier, which is an object with fields corresponding to that module’s exports.

Import cycles are invalid.

The path given to an import may not include .. to specify files outside the current directory nor may it be an absolute path.

It must be a relative path, which is resolved relative to the parent directory of the source file in which they appear.

5.4.2.1.2.2 Package imports

import * as func from
  '@reach-sh/reach-example-package';
import * as func from
  '@reach-sh/reach-example-package:src/func.rsh';
import * as func from
  '@github.com:reach-sh/reach-example-package#main:src/func.rsh';

Package imports obey the same rules as local imports but support an extended path syntax which allows Reach programmers to seamlessly plug into third-party libraries hosted on the internet.

All package imports begin with the @ character.

Package import paths are comprised of the following components:
  • (Optional): The git server where the package is hosted.

    This component must be followed by a : character.

    This component defaults to GitHub (i.e. github.com) if no site is specified.

    Examples: github.com:, bitbucket.org:.

  • The account registered with the host site.

    This component must be followed by a / character.

    Examples: reach-sh/, jeapostrophe/.

  • A repository associated with the account.

    Examples: reach-example-package, nfts.

  • (Optional): A git ref or git commit used to represent the package version.

    If no ref is specified, Reach first tries to find the requested module on the repository’s master branch, and if that fails then on the main branch once more.

    git refs are discussed in further detail here.

    It is highly advisable that package authors use git tags to denote version "releases", e.g. v0.2.1, and that consuming code target the desired git tag rather than a branch name.

    Read this guide to learn more about how git tags work.

    This component must be preceded by a # character.

    Example: #v3.0.6.

  • (Optional): The directory in which the module may be found.

    This component must be preceded by a : character and must end with a /.

    Example: :src/lib/.

  • (Optional): The filename of the requested module.

    Defaults to index.rsh.

    If the module exists within a subdirectory it must be preceded by a / character.

    Example: @reach-sh/example#v1.01:parent/child/pkg.rsh.

    However, if the module is stored in the root of the repository, it must instead be preceded by a : character.

    Example: @reach-sh/example#v1.02:pkg.rsh.

The following forms are all syntactically valid package import expressions:

@account/repo

@account/repo:

@account/repo:a/b/file.rsh

@account/repo:a/b/

@account/repo:file.rsh

@account/repo#

@account/repo#:

@account/repo#:a/b/file.rsh

@account/repo#:a/b/

@account/repo#:file.rsh

@account/repo#ref

@account/repo#ref:

@account/repo#ref:a/b/file.rsh

@account/repo#ref:a/b/

@account/repo#ref:file.rsh

 

@server:account/repo

@server:account/repo:

@server:account/repo:a/b/file.rsh

@server:account/repo:a/b/

@server:account/repo:file.rsh

@server:account/repo#

@server:account/repo#:

@server:account/repo#:a/b/file.rsh

@server:account/repo#:a/b/

@server:account/repo#:file.rsh

@server:account/repo#ref

@server:account/repo#ref:

@server:account/repo#ref:a/b/file.rsh

@server:account/repo#ref:a/b/

@server:account/repo#ref:file.rsh

Since git repositories evolve and change over time, Reach takes extra steps in order to pin a given module import’s version to the specific SHA hash of the specified revision at the time the package is first installed. These pins are stored in a lockfile, which should be included in your source control system.

5.4.2.2 Expressions

Any expressions valid for a computation are valid for a module. However, some additional expressions are allowed.

5.4.2.2.1 Reach.App

export const main = Reach.App(() => {
 const A = Participant("A", {
  displayResult: Fun(Int, Null),
 });
 deploy();

 const result = 0;
 A.only(() => { interact.displayResult(result); });

 exit();
});

Reach.App accepts a no-argument function that specifies a DApp. This function is applied during compilation as an application initialization. It specifies the entire DApp in its body.

If the result of Reach.App is eventually bound to an identifier that is exported, then that identifier may be a target given to the compiler, as discussed in the section on usage.

5.4.2.2.1.1 Deprecated long-form

export const main =
  Reach.App({}, [Participant("A", {displayResult: Fun(Int, Null)})], (A) => {
    const result = 0;
    A.only(() => { interact.displayResult(result); })
    exit();
  });

Previous versions of Reach only allowed a form of Reach.App which accepted three arguments: an options object, an applicationArgs tuple, and a program arrow of the form (applicationIds) => body.

This form was equivalent to

Reach.App(() => {
 setOptions(options);
 [ applicationIds ] = applicationArgs;
 deploy();
 body
});

The current version of Reach will automatically transform these "ternary" Reach.App instances into the above form.

Future versions of Reach will deprecate this transform and such programs will be invalid.