6.3 Annotations from Predicates
Binding and Annotation Macros shows matching for turning a binding pattern into an annotation. The satisfying annotation constructor wraps an arbitrary predicate function, and it converts it to an annotation that is satisfied when the predicate returns a true value.
> f(10)
5
> f(9)
f: argument does not satisfy annotation
argument: 9
annotation: Int && satisfying(is_even)
The use of && in this example means that x has static information from Int. The combination of && and satisfying can be especially useful in defining a new annotation with annot.macro (see Binding and Annotation Macros).
To recognize specific values via an implicit == predicate, use Any.of:
n, ...):
> add(~from: #'left, 0.1, 0.9, 9.007199254740998e15)
9007199254741000.0
> add(~from: #'right, 0.1, 0.9, 9.007199254740998e15)
9007199254740998.0
The argument to satisfying or Any.of is an arbitrary expression, which raises the question of when the expression is evaluated. The expression is evaluated when the annotation is encountered, which is not necessarily the same as each time it is applied. If a binding with the annotation is repeated via ..., for example, the annotation may be checked multiple times against different values, but the expression to construct the annotation will be evaluated just once. Similarly, an annotation check that is repeated via List.of or delayed by Array.later_of can involve a definition evaluated once when the enclosing annotation is reached, not each time the repeated or delayed annotation is checked. Still, expressions in an annotation associated with a function argument must be evaluated each time the function is called, because because bindings for earlier arguments are available to the expression.
> add_more(10, 2, 3)
getting predicate
15
> add_more(1, 2, 3)
getting predicate
add_more: argument does not satisfy annotation
argument: [2, 3]
annotation:
matching(List((_ :: satisfying(block:
println("getting predicate")
fun (b): b < a)), ...))