Aliases - Manhunter07/MFL GitHub Wiki

Note: This feature is risky and should be avoided

Aliases are synonymes for identifiers used for both declaration and call names. The are stored in the objects lookup table and are used whenever an identifier is parsed. Aliases are supported for these identifiers:

  • Constant names in declaration headers and terms
  • Variable names in declaration headers and terms
  • Function and inline names in declaration headers and terms
  • Type names in declarations and terms, including constructors and converters)
  • Attribute names in declarations (attributes are not available elsewhere)
  • Record field names in notations (not in member calls)
  • Alias names and meanings

Please keep in mind that alias resolvings may cause an infinite loop if the alias resolves to another alias. No background check is done! Aliases can be given any meaning that is not their name. They can, however, resolve to an alias whose meaning is their name. Use this feature with caution, therefore.

Declaration

Aliases are declared using the alias keyword, followed by a name list (which may as well consist of just one identifier), an equal sign and a so-called meaning. This meaning can be any qualified or unqualified identifier. It could be an object that does not exist as well as another alias (the latter being an extremely risky operation, though). Operators, value (number, string, ...) notations and statements cannot be part of either a name or its meaning.

The following expression declares an alias named Sine for the Sin function:

alias Sine = Sin

If an explicite package name is needed to specify which object is meant, qualified identifiers are supported as well. Here, we define two aliases, Cos and Cosine that both explicitly point to System.Cos:

alias Cos, Cosine = System.Cos

Resolve process

Aliases are resolved using the object dictionary of the current program or package. Resolving an alias is done recursively, meaning that an alias may be resolved as another alias that is being resolved then. The following code will return "boo":

const c = "boo"
alias a = b
alias b = c
a

Problems

In many (actually, most) situations, the use of aliases is not very senseful, since there are many problems that can arise from it. This includes program crashes due to circular references between different aliases, but also other issues like the fact that once they are defined, they are permanent and cannot be undeclared or re-declared anymore. Aliases are package-dependent and cannot be imported, because they are no actual objects but rather additional identifiers for one object.

Because an alias is not a real macro as known from for example C, only identifiers are allowed as meanings. This includes both qualified and unqualified ones and also package names, but no direct values, operators or statements for example.

Substitutes

As previously mentioned, there are numerous reasons as for why aliases should be avoided in most situations. In fact, there is actually only one legithimate situation when you should consider using them, and that's backward or forward compatibility. There are however, dependent on what you want to achieve, different ways of substituting them, some of which will be explained below.

Constants

Instead of using an alias for a constant, you could simply declare a new constant with the same value. You can even use the first constant as value for the second one. Since constants cannot be re-declared, this should be a safe way to achieve avoiding aliases for constants.

If the desired target is a function, variable or type, you can instead reference it in the constant and later de-reference it when needed.

Inlines

Inlines are like functions, but have their syntax directly inserted into the term rather than being called on a stack. Like aliases, they do not support parameters, but unlike constants, they are not evaluated at the time of declaration but whenever they are used, similarly as aliases. In fact, their behaviour is very similar to that of aliases, but they can be handled better and are less error-prone due to them being an actual object. Infinite call loops are also handled by an exception, because them being stateless allows the compiler to do a background check (you cannot rely on this however).

Imports

See also: Commands#import

Instead of declaring an alias for an identifier in an external package, it is often better to just import the object to the current package or program. You can thereby avoid name conflicts without having to declare an alias for the qualified name of the target object.