[Python-ideas] Partial operator (and 'third-party methods' and 'piping') [was Re: Function composition (was no subject)]
Terry Reedy
tjreedy at udel.edu
Mon May 11 19:54:50 CEST 2015
On 5/11/2015 12:13 PM, Gregory Salvan wrote:
> I don't want to insist and I respect your point of view, I just want to
> give a simplified real life example to show that function composition
> can be less painful than another syntax.
>
> When validating a lot of data you may want to reuse parts of already
> writen validators. It can also be a mess to test complex data validation.
> You can reduce this mess and reuse parts of your code by writing atomic
> validators and compose them.
>
> # sorry for using my own lib, but if I make no mistakes this code
> functions, so...
>
> import re
> from lawvere import curry # curry is an arrow without type checking,
> inherits composition, mutiple dispatch
>
> user_match =
> re.compile("^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*$").match
> domain_match =
> re.compile("^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$").match
> strict_user_match =
> re.compile("^[a-z0-9][a-z0-9_-]+(?:\.[a-z0-9_-]+)*$").match
>
> @curry
> def is_string(value):
> assert isinstance(value, str), '%s is not a string' %value
> return value
>
> @curry
> def apply_until_char(func, char, value):
> func(value[:value.index(char)])
> return value
>
> @curry
> def apply_from_char(func, char, value):
> func(value[value.index(char) + 1:])
> return value
>
> @curry
> def has_char(char, value):
> assert value.count(char) == 1
> return value
>
> @curry
> def assert_ends_with(text, value):
> assert value.endswith(text), '%s do not ends with %s' % (value, text)
> return value
>
> @curry
> def assert_user(user):
> assert user_match(user) is not None, '%s is not a valid user name'
> % value
> return user
>
> @curry
> def assert_strict_user(user):
> assert strict_user_match(user) is not None, '%s is not a valid
> strict user' % value
> return user
>
> @curry
> def assert_domain(domain):
> assert domain_match(domain) is not None, '%s is not a valid domain
> name' % value
> return domain
>
> # currying (be made with partial)
> has_user = apply_until_char(assert_user, '@')
> has_strict_user = apply_until_char(assert_strict_user, '@')
> has_domain = apply_from_char(assert_domain, '@')
>
> # composition:
> is_email_address = is_string >> has_char('@') >> has_user >> has_domain
> is_strict_email_address = is_string >> has_char('@') >> has_strict_user
> >> has_domain
>
> # we just want org adresses ?
> is_org_addess = is_email_address >> assert_ends_with('.org')
>
>
> I found a lot of interest in this syntax, mainly for testing purpose,
> readability and maintenability of code.
> No matters if I'm a fish out of python waters. :)
You could do much the same with standard syntax by writing an str
subclass with multiple methods that return self, and then chain together
the method calls.
class VString: # verifiable string
def has_char_once(self, char):
assert self.count(char) == 1
return self
...
def is_email_address(self): # or make standalone
return self.has_char_once('@').has_user().has_domain()
data = VString(input())
data.is_email()
--
Terry Jan Reedy
More information about the Python-ideas
mailing list