[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