Skip to content

Identifier

New feature (added in 2023.2.67)

Type

Identifiers define opaque, typed identifiers.

Rationale

When dealing with collections, it is often useful to be able to identify an element inside that collection by something other than its position.

Identifiers allow you to create strongly typed identifiers to support this use case.

A typical example is a collection of structs, where individual elements have no natural identity. You can create an identifier to store these structs inside a map.

struct Payment {
    amount: Number,
    paymentDate: LocalDate,
    processed: Boolean
}

identifier PaymentId;

protocol[issuer, payee] Iou() {
    var payments: Map<PaymentId, Payment> = mapOf<PaymentId, Payment>();

    permission[issuer] pay(amount: Number) returns PaymentId {
        var p = Payment(amount, now().toLocalDate(), processed = false);
        var id = PaymentId();
        payments = payments.with(id, p);
        return id;
    }

    permission[payee] markProcessed(id: PaymentId) {
        var processedPayment = payments.getOrNone(id).getOrFail().copy(processed = true);
        payments = payments.with(id, processedPayment);
    }
}

Declaration

An identifier type is declared by the identifier keyword, followed by the type name, followed by a semicolon,;. Declaration of an identifier type is a top-level construct.

identifier MyId;

Instantiation

An identifier instantiation starts with the identifier's type name, followed by parentheses, similar to a function call. It takes no arguments.

When an identifier value is instantiated within NPL, it is assigned a random, unique value.

For example, given an identifier defined as:

identifier MyId;

creating a new typed identifier value looks like so:

var myId: MyId = MyId();
var yourId = MyId(); // type annotation is not required

Usages

An identifier parameter, field or variable is declared like any other variables with the user-defined identifier type name. Values of an identifier type can be used as a key or value in a Map. You can also return values of a user-defined identifier type.

An identifier type can be imported from other packages using the use import statement.

An identifier type can be annotated with @experimental and @deprecated annotations.

Operators

Comparison

Values of a user-defined identifier type can be compared for equality, == and inequality, !=.

var myId: MyId = MyId();
var yourId: YourId = YourId();

function validComparison(myId: MyId, anotherMyId: MyId) returns Boolean -> {
    return myId == anotherMyId;
};

Note that you cannot compare values of different identifier types.

// compile ERROR, myId and yourId have different types
function invalidComparison(myId: MyId, yourId: YourId) returns Boolean -> {
    return myId == yourId;
}

Methods

While .toText() standard library method is not supported for the values of a user-defined identifier type, logging identifier values is still possible.