We sometimes run into a situation where we would like to use a logic predicate, but not to exhaustively enumerate all of its tuples.
For example, we can image a situation where we want a predicate
PrimeInRange(from, to, prime) which holds if
prime is a prime number in the
[from; to]. While we can imagine such a predicate, it is not feasible to
compute with. Instead, what we often want, is that we want to treat
PrimeInRange as a functional, i.e. a function that given
as input produces a set of primes as output. To make matters concrete, we
might want to write the rule:
R(p) :- P(x), Q(y), PrimeInRange(x, y, p).
but without having to evaluate
PrimeInRange for every
We can achieve this as follows. We write a function:
def primesInRange(b: Int32, e: Int32): Vector[Int32] = Vector.range(b, e) |> Vector.filter(isPrime)
The key is that
primesInRange is a function which returns a vector of tuples
(in this case single elements), given a begin
b and end index
primesInRange can efficiently compute the tuples we are interested in. To use
it in our rule, we write:
R(p) :- P(b), Q(e), let p = primesInRange(b, e).
e are clearly identified as the input of
as its output. Specifically, Flix requires that
e are positively bound
(i.e. bound by a non-negative body atom-- in this case
Q.) In this
primesInRanges returns a vector of
Int32s, but in general a functional
may return a vector of tuples.
Note: An important limitation in the current implementation is that the variables in the LHS of a functional predicate must not be rebound. That is, if the functional predicate is of the form
let (a, b) = f(x, y)then
bmust not be rebound in the rule.