Source

intensional / README.rst

Full commit

Intensional (rule-defined) sets for Python.

Overview

There are two ways of defining a set: intensional and extensional. Extensional sets like set([1,3,5,'daisy']) enumerate every member of the set explicitly.

Intensional sets, in contrast, are defined by rules. For example "the set of all prime numbers" or "every word beginning with 'a' and ending with 't'. Intensional sets are often infinite. It may be possible to generate a list of their members, but it's not as simple as a "give me everything you've got!" for loop.

Once you know what you're looking for, intensional sets are everywhere. Python doesn't represent them directly, but regular expressions, many list comprehensions, and all manner of testing and filtering operations are really faces of the intensional set concept. Many functions test whether something 'qualifies'. os.path.isdir(d) for example, tests whether d is in the set of legitimate directories, and isinstance(s, str) tests whether s is a member of the set of str objects. Even the core if conditional can be construed as testing for membership in an intensional set--the set of all items that pass the test.

Many such tests have a temporal aspect--they determine whether a value is a member right now. The answer may change in the future, if conditions change. Others tests are invariant over time. %%734 will never be a valid Python identifier, no matter how many times it's tested--unless the rules of the overall Python universe change, that is.

Intensional sets are part and parcel of all programming, even if they're not explicitly represented or called by that name.``intensional`` helps Python programs represent intensional sets directly. This provides several interesting improvements:

  • A nicer way to use regular expressions
  • A switch statement (actually, function) for Python

Usage

Instead of:

import re

match = re.search(pattern, some_string)
if match:
    print match.group(1)

You can do an en passant test:

from intensional import Re

if some_string in Re(pattern):
    print _[1]

There are two things to note here: First, this turns the sense of the matching around, asking "is a given string in the set of items this pattern describes?" The Re pattern is an intensionally defined set (namely "all strings matching the pattern"). This order often makes excellent sense whey you have a clear intent for the test. For example, "is the given string within the set of all legitimate commands?"

Second, the in test had the side effect of setting the underscore name _ to the result. Python doesn't support en passant assignment, so you can't both test and collect results in the same motion, even though that's sometimes exactly appropriate. Re use introspection to get around this difficulty and provide neat results.

If you prefer the more traditional re calls, you can still use them with the convenient en passant style.:

if Re(pattern).search(some_string):
    print _[1]

Re works even better with named pattern components, such as:

person = 'John Smith 48'
if person in Re(r'(?P<name>[\w\s]*)\s+(?P<age>\d+)'):
    print _.name, "is", _.age, "years old"
else:
    print "don't understand '{}'".format(person)

### need go back meahcnism

It's possible also to loop over the results, as in:

for found in Re(r'(pattern\w*)').findall('pattern is as pattern does'):
    print found

Re objects are memoized for efficiency, so they are only compiled once, regardless of how many times they're mentioned in the program.

Notes

It's entirely possible to implement an intensional set like idea with other Python mechanisms, such as lambda expressions, test functions, and list comprehensions. Many (most?) Python programs already de facto use this implicit intensional set approach. Making intensional sets explicit, however, has the benefit of making what's being done more clear. It provides a mechanism for cleaning up several sort of ucky parts of Python:

  • Messy, guts-exposed regular expression tests
  • Overly verbose lambda expressions
  • Long, non-DRY if/elif/else constructions (with switch)
  • A mess of mechanisms for collection filtering

Installation

pip install intensional

(You may need to prefix this with "sudo " to authorize installation.)