Lazy predicates
Lazy predicates are a more advanced feature that allow composing predicates that reference themself, or even having predicates with mutual references.
There are 3 types of lazy predicates:
lazy_p: references another predicate by name.
this_p: refers to the predicate to which the this_p belongs
root_p: refers to the root predicate, i.e. the fully composed predicate
In the next chapters we will explain them in more detail
lazy_p
A lazy predicate is defined as:
from predicate import lazy_p
p = lazy_p("name_of_referenced_predicate")
To make this more clear, we will give to examples.
Example 1
In the next example we define a predicate, that tests if a given data structure is either a string, or a list of data that can again either be a string or a list of data. Ad infinitum.
from predicate import all_p, is_list_p, is_str_p, lazy_p
str_or_list_of_str = is_str_p | (is_list_p & all_p(lazy_p("str_or_list_of_str")))
Note that the name of the reference must be the same as the variable name of the predicate that you are referencing.
Applying this predicate gives the following results:
str_or_list_of_str("foo")
True
str_or_list_of_str(["foo"])
True
str_or_list_of_str(["foo", ["bar"]])
True
str_or_list_of_str(1)
False
Example 2
We can even model a predicate that checks if a given data structure is valid json:
valid_json_p = lazy_p("is_json_p")
json_list_p = is_list_p & lazy_p("valid_values")
json_keys_p = all_p(is_str_p)
valid_values = all_p(
is_str_p | is_int_p | is_float_p | json_list_p | valid_json_p | is_none_p
)
json_values_p = comp_p(lambda x: x.values(), valid_values)
is_json_p = (is_dict_p & json_keys_p & json_values_p) | json_list_p
As you can see in this example there are 2 mutual lazy references.
this_p
A this predicate (this_p) is defined as:
from predicate import this_p
p = this_p
Note that this example is not very useful, since it references itself, leading to an infinite recursion while trying to evaluate.
For a more realist example, lets write example 1 from lazy_p
using the this_p
:
Example 1
from predicate import is_list_of_p, is_str_p, this_p
str_or_list_of_str = is_str_p | is_list_of_p(this_p)
We made two changes to the original lazy_p example. Firstly, are using the is_list_of_p
predicate that combines the
is_list
and all_p
predicates. But most importantly, instead of having to reference the str_or_list_of_str
by name, we just use this_p
, leading to more concise code.
root_p
A this predicate (root_p) is defined as:
from predicate import root_p
p = root_p
Note that this example is not very useful, since it references itself, leading to an infinite recursion while trying to evaluate.
For a more realist example, lets write example 1 from lazy_p
using the root_p
:
Example 1
from predicate import is_list_of_p, is_str_p, root_p
str_or_list_of_str = is_str_p | is_list_of_p(root_p)
We made two changes to the original lazy_p example. Firstly, are using the is_list_of_p
predicate that combines the
is_list
and all_p
predicates. But most importantly, instead of having to reference the str_or_list_of_str
by name, we just use root_p
, leading to more concise code.
Also note that in this particular case there is not difference between the this_p
and the root_p