What is forall
used for? Could you give me an example of where you’d want to use forall
?
DAML inherits a lot of its type system form Haskell and as such you can find a lot of quality explainers for questions like that. Eg https://wiki.haskell.org/Scoped_type_variables
In short, it allows you to explicitly tell the compiler about type variables. Take as an example the exerciseByKey
function:
-- | Exercise a choice on the contract associated with the given key.
--
-- You must pass the `t` using an explicit type application. For
-- instance, if you want to exercise a choice `Withdraw` on a contract of
-- template `Account` given by its key `k`, you must call
-- `exerciseByKey @Account k Withdraw`.
exerciseByKey : forall t k c r. (HasFetchByKey t k, HasExercise t c r) => k -> c -> Update r
exerciseByKey k c = do
(cid, _) <- fetchByKey @t k
exercise cid c
The template type t
doesn’t occur anywhere in the signature k -> c -> Update r
, but you have to use it in the type constraints (HasFetchByKey t k, HasExercise t c r)
. To be able to do so, you first have to explicitly tell the compiler about the type variables in use: forall t k c r
.
Usually when you need to do this, you’ll run into other problems like “Ambiguous Types”. But fortunately the DAML IDE will tell you how to fix that.
@bernhard described one of the reasons (the keyword if you want more information for that is ScopedTypeVariables
). That one is related to the internal implementation of a function.
There is a second one related to the API you expose to users of a definition: The order of type variables.
You might think this doesn’t matter at first. However, as soon as you have an explicit type application (that’s what the @
sign is used for) the order becomes relevant. You can see an example of this in @bernhard’s code above:
fetchByKey @t k
fetchByKey
has type TemplateKey t k => k -> Update (ContractId t, t)
. There are two possible orders of type variables, either the template type comes first or the key type comes first. Depending on how you order them fetchByKey @t k
will use t
as the template type or as the key type. The default order you get by the compiler if you do not use a forall
is somewhat arbitrary (it’s deterministic and there are rules but it’s non-obvious). If you use a forall
, you can control the order of type variables explicitly and make sure that t
comes before k
.
Makes sense, thanks @bernhard and @cocreature!