Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Contracts

Contracts help describe the method signature, which includes:

  • The method name.
  • Interaction with stdin and stdout.
  • Description of optional and required arguments.
  • Description of optional and required flags.

Contracts help ensure a correct and expected call. If a contract exists and is violated, Irnix simply will not invoke the method and will output an error message.

All contracts are described in a .self file directly inside the object’s directory. This naming convention is linked to the fact that method names cannot contain .—as we discussed in the chapter "Methods".

Let us consider an example from the chapter "Quick Start" and add a new .self file:

~/.local/share/irnix
└── object
    ├── .self
    └── method

With contents:

method: message! stdout!

In this case method is the name of the method, message is simply a placeholder for an argument that can be any name, and ! indicates that the argument is required. stdout! means that output to stdout is expected in every execution.

After adding the contract, Irnix will prevent us from calling it incorrectly. If we try to pass stdin:

echo text | irnix e object.method

Then Irnix will not run the method and will output an error (the contract “method” does not imply functionality for stdin, but stdin was passed.):

The contract "method" does not imply functionality for stdin, but stdin was passed.

If we do not provide arguments:

irnix e object.method
The arguments provided are fewer than required by the contract. The contract requires 1 arguments and 0 optional ones.

If we pass too many arguments:

irnix e object.method -- message message
Too many arguments. The contract requires 1 arguments and 0 optional ones.

You can use any number of contracts in .self files.

method: arg! arg! arg?
method: arg!
method:

Thus Irnix protects the method from incorrect invocation, reducing the likelihood of bugs.

Signature Components

  • Method name
  • stdin
  • Required arguments
  • Optional arguments
  • Required flags
  • Optional flags
  • stdout

Method Name

To describe a signature you must specify the method name, which should match the actual method name. For example, if we want to describe the signature for the method start, the contract should begin with start::

start:

It is important that immediately after the method name there is a colon : without a space.

Stdin

stdin can have three states:

  • Not specified: means the method does not accept stdin; if non‑empty stdin is provided, it may cause an error or be ignored. If stdin is passed in this case, Irnix will not run the method and will return an error.
  • stdin!: indicates that the method requires non‑empty stdin. If nothing is sent to stdin during invocation, Irnix will output an error.
  • stdin?: means stdin is optional and may be empty or non‑empty.

For example, consider object logger with method log, which expects stdin as input:

log: stdin!
echo message | irnix e logger.log

Stdout

stdout can have three states:

  • Not specified: means the method never outputs anything. If it is used in a pipeline, Irnix will output an error stating that you are attempting to redirect output from a method that does not produce any output.
  • stdout!: indicates that the method always produces some output.
  • stdout?: means the method sometimes produces output and sometimes does not.

For example, consider object say with method hello, which simply outputs the string Hello!:

hello: stdout!
irnix e say.hello

Arguments

You can specify arguments in any order. What matters is only the number of required and optional arguments. The name of an argument also does not matter, and Irnix ignores it. Therefore you may invent any name for an argument to improve the readability of the contract.

  • arg! — a mandatory argument.
  • arg? — an optional argument.

When checking the contract’s arguments at call time, Irnix pays attention only to the count of arguments. For example, if you add three required and two optional arguments to the contract, Irnix will consider a call valid with a number from three (inclusive) up to five (inclusive). If you provide only two, or for instance six, Irnix will output an error message and will not run the method.

Let’s take an example from Quick Start and describe its contract:

method: message! stdout!

This is a method named method that requires one mandatory argument and always outputs something. When calling the method, Irnix checks the contract and ensures the call is correct. If we use this method in a pipeline, Irnix will check whether output is guaranteed:

irnix e object.method -- World! | logm

Flags

Flags, like arguments, can be either required or optional. Irnix treats everything that starts with - as a flag in contracts. For example:

method: arg! --flag! -s? stdout!

In this example --flag is mandatory, and -s is optional.

Flag values

You can also indicate whether a flag should contain a value by adding = before the ! or ? symbol:

method: arg! --flag=! -f?

In that case Irnix will check that after the flag there is an argument, which will be interpreted as the flag’s value. At the same time you can also specify values using =, for example --flag=value:

irnix e object.method -- -f --flag value arg
irnix e object.method -- -f --flag=value arg

You can also use helper symbols to make the contract more readable, such as (, ), ,, ->.

The following examples are absolutely equivalent:

method_name: stdin! -> (arg1!, arg2?, -f?, --flag=?) -> stdout!
method_name: stdin! -> arg1!, arg2?, -f?, --flag=? -> stdout!
method_name: stdin! -> arg1! arg2? -f? --flag=? -> stdout!
method_name: stdin! arg1! arg2? -f? --flag=? stdout!

In Irnix contract overriding works. This means that if you write many contracts for the same method, only the last specified one will be used. In this case the contract method_name: stdin! arg1! arg2? -f? --flag? stdout! will be used, and the others will be ignored.

Error codes (experimental feature)

You can also specify error codes in a contract by simply writing numbers, for example in the form 2 42 50, as 2, 42, 50, or [2, 42, 50]. However, at the moment this is an experimental feature, and support for error codes may be discontinued in future versions.

At present there are no checks for error codes, and when comparing interface contracts with object contracts the sets of error codes are compared directly, as well as their order. Therefore using error codes is not recommended at the moment, but such a possibility still exists.