Universal Function Call Syntax

You will get to learn about universal function call syntax (UFCS) in this lesson.

Universal function call syntax (UFCS)

UFCS is a feature that is applied by the compiler automatically. It enables the member function syntax even for regular functions. It can be explained simply by comparing two expressions:

variable.foo(arguments)

When the compiler encounters an expression like the one above, if there is no member function named foo that can be called on a variable with the provided arguments, the compiler also tries to compile the following expression:

foo(variable, arguments)

If this new expression can be compiled, the compiler simply accepts that one. As a result, foo() gets accepted to be used by the member function syntax even though it evidently has been a regular function.

Note: UFCS considers only functions that are defined at module scope; for example, nested functions cannot be called with the UFCS syntax.

We know that functions that are closely related to a type are defined as member functions of that type. This is especially important for encapsulation because only the member functions of a type (and that type’s module) can access its private members. Let’s consider a Car class which maintains the amount of fuel:

class Car {
    enum economy = 12.5; // kilometers per liter (average) 
    private double fuelAmount; // liters
    
    this(double fuelAmount) {
        this.fuelAmount = fuelAmount; 
    }
    
    double fuel() const {
        return fuelAmount;
    }
    // ...
}

Although member functions are very useful and sometimes necessary, not every function that operates on a type should be a member function. Some operations on a type are too specific to a certain application to be member functions. For example, a function that determines whether a car can travel a specific distance may more appropriately be defined as a regular function:

bool canTravel(Car car, double distance) {
    return (car.fuel() * car.economy) >= distance; 
}

This naturally creates a discrepancy when calling functions that are related to a type; objects appear at different places in these two syntaxes:

Get hands-on with 1000+ tech skills courses.