Fork me on GitHub

The Agoraplex Predicates Library

The predicates module provides a variety of predicates, predicate factories, and predicate partials.

“A predicate is a function that returns the truth value of some condition.”

—Andrew M. Kuchling, Python Functional Programming HOWTO

Predicate factories are functions which create new predicates based on their arguments (e.g., _and(), _nargs()). Predicate partials are functions created by partial application of a predicate’s arguments.

The Agoraplex Predicates Library is licensed under the BSD “3-clause” license. See LICENSE for details.

Motivation and applications

You could say that I hate Python’s lambda syntax, and that this is just a very indirect way of expressing it. While I do dislike the syntax, that’s really not it at all...

predicates is actually part of an extensible (work-in-progress) way to build matchers (or selectors, or whatever you call them). I.e., do something based on matching a value to a set of rules.

The original motivation was (is) a predicate dispatch library [1] for Python, but... something else came up.

>>> from predicates import (
...   _any,
...   _nis,
...   _contains,
...   isstring,
...   _and,
...   true_
... )

>>> def the_answer (*args, **kwargs):
...   return "forty-two"

>>> def the_question (*args, **kwargs):
...   return "what do you get when you multiply six by nine?"

>>> def other (*args, **kwargs):
...   return "yeah. I got nuthin'. sorry."

>>> def marvin (*args, **kwargs):
...   return "no, that's okay, i'll just sit here and rust."

>>> the_guide = (
...   (_any(_nis(exactly=42)), the_question),
...   (_any(_and(isstring, _contains("sad"))), marvin),
...   (_any(
            _and(isstring,
                 _contains("life", "the universe", "everything"))),
       the_answer),
...   (true_, other)
... )

>>> def dispatch (rules, *args, **kwargs):
...   for (rule, method) in rules:
...     if rule(*args, **kwargs):
...       return method(*args, **kwargs)
...   raise NotImplementedError

>>> dispatch(the_guide, 42)
'what do you get when you multiply six by nine?'

>>> dispatch(the_guide, 23, 64, "how sad is he?")
"no, that's okay, i'll just sit here and rust."

>>> dispatch(the_guide,
...   "he's pondering the question.",
...   "which question?",
...   "THE question!",
...   "life. the universe. everything!",
...   "oh. that one.")
'forty-two'

Of course, that’s a very ugly example, since it doesn’t use decorators, or mine annotations [2]. Worse, though, it uses that brutally naïve linear probe through the predicates. While that allows giving rules precedence, it it also needlessly repeats any shared tests, and, well, it’s just embarassing. So... future work on predicates will include a predicate compiler and an optimizer. The compiler might expand boolean predicates into Python statements, instead of requiring multiple nested function calls. The optimizer might, given a set of predicates, canonicalize them and build a tree, with each leaf being one of the original set’s results.

Tomorrow Man [3] is gonna get right on that.

[1]...which explains all of the argXXX predicates, I hope.
[2]Using anodi, for example...
[3]Not to be confused with The Tomorrow Man, about whom Google just told me.

Indices and tables