Understanding the typing of `query` function

How does the query function used like query @BankAccount bank fit to the type (Template t, HasAgreement t, IsParties p) => p -> Script [(ContractId t, t)] (as defined in the docs) ? I don’t understand how this type maps to the usage.

First, a couple important pieces of documentation that you should take a look at if you have not yet had a chance to:

  1. Basics on constraints (worth reading the whole 101 page if you have not seen it before)
  2. more about what the part before => means

With that background, you’ll be well-equipped to understand the following: query @BankAccount bank sets the type argument t to BankAccount, and the first function value argument to bank.

2 Likes

The underlying concept is called Visible type application and allows to provide explicit type arguments to a function. This was discussed earlier in this forum here: I've just discovered what the `@<template_name>` notation in functions like `fetchByKey` et al means

In this particular case, the function query comes with two type parameters t and p, which can be specified with the @-notion and correspond to the order defined by the function. You could technically write query @BankAccount @Party bank, resulting in the same, although that is not necessary since only t is ambiguous when not defined.

3 Likes

So, in the function call query @BankAccount bank, the type constraint (Template t, HasAgreement t, IsParties p) applies to the whole function (not just a parameter), and bank is an instance of IsParties, which satisfies the last type constraint. Because we need to satisfy the first two constraints, we add @BankAccount in front of the argument bank which points to the BankAccount template and that is of type Template BankAccount and an instance of HasAgreement BankAccount. Does that sound right?

Rather, bank’s type has an instance of IsParties. Constraint resolution is 100% static. Types are members of typeclasses; expressions and values are not, but they are related to types in other ways.

While we need to satisfy the first two constraints, the real reason is that t is usually difficult to infer here. For example, if later in the function you pass the payload to a function that is declared explicitly to take a BankAccount, or you destructure the payload using the BankAccount{..} form, then that’s enough information to let you remove the @BankAccount argument.

But typically you are merely accessing properties with .prop syntax or exercising choices, and these aren’t enough to disambiguate what type your code could be referring to.

By contrast, consider that the type variable p is very easy to infer from context.

1 Like