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

Interfaces

Interfaces are a powerful mechanism that allows an object to be described abstractly and concrete implementations to be swapped in easily.

Irnix considers any object whose name starts with __ and ends with __, e.g. __interface_object__, to be an interface. For such objects there is a special rule: the directory of that object must contain only two files: a contract in a .self file, and a link to a concrete implementation.

For example, let us create a hypothetical __logger__ that has a method log, which takes stdin, formats the message, and always outputs it:

~/.local/share/irnix
└── __logger__
    └── .self

Contents of .self:

log: stdin! stdout!

If you try to call the method now, Irnix will output an error message stating that the interface directory must contain exactly two files (.self and a link to the concrete implementation).

Let us create a concrete implementation and name it logm:

~/.local/share/irnix
├── __logger__
│   └── .self
└── logm
    ├── .self
	├── log_level -> /bin/logm
    └── log -> /bin/logm

And in .self add two methods:

log: stdin! stdout!
log_level: stdin! level! stdout!

log takes stdin, formats it, and always outputs something to stdout. The method log_level works the same as log, but also takes one argument – the level.

It is important to note that in the implementation logm all methods are links to the same binary file logm, which simply formats the message and outputs it. We will discuss in more detail how links can be used in Irnix and how to achieve high flexibility in the chapter "Working with Links".

Now just create a hard link from the logm directory into the interface directory:

~/.local/share/irnix
├── __logger__
│   ├── .self
│   └── logm -> ../logm
└── logm
    ├── .self
	├── log_level -> /bin/logm
    └── log -> /bin/logm

After that the method can be called safely as a method on a regular object:

echo message | irnix e __logger__.log

During startup Irnix compares both contracts in __logger__ and in the implementation logm for the method log, and if they match, it means the implementation is suitable. Then everything proceeds as usual: Irnix verifies the call and invokes the specific implementation of the method log.

You can also avoid creating links and create the object’s implementation directly inside the interface directory:

~/.local/share/irnix
└── __logger__
    ├── .self
	└── logm
	    ├── .self
		├── log_level -> /bin/logm
	    └── log -> /bin/logm

But then you lose flexibility, because the implementation will no longer be as easy to replace with another one. The main purpose of using such interfaces is that you can easily swap out the implementation without changing how the command is invoked.


If at this point you try to call the method __logger__.log_level, Irnix will output the following message:

The called method "log_level" is not specified in the interface contract. However, it is specified in the object's contract: "/home/illia/.local/share/irnix/logm"

Irnix understands that such a method actually exists and could invoke it (and this would be something like a type cast), but this does not happen and it is intentional. This behavior helps avoid bugs, because if you replace the implementation with a new one, its methods may not fully match those of the old implementation.