[Python-ideas] Partial operator (and 'third-party methods' and 'piping') [was Re: Function composition (was no subject)]

Douglas La Rocca larocca at abiresearch.com
Mon May 11 19:08:54 CEST 2015


Operator overloading (>>) has intuitive readability but in my experience it's better have functions remain "ordinary" functions, not class instances so you know what to expect regarding the type and so on. The other downside is that with (>>) only the functions you wrap can play together.

Leaving aside the readability concern, the really major problem is that your tracebacks are so badly mangled. And if your implementation of the composition function uses recursion it gets even worse.


You also lose the benefits of reflection/inspection--for example, with the code below, what happens if I call help ?? in ipython on `is_email_address`?


________________________________
From: Python-ideas <python-ideas-bounces+larocca=abiresearch.com at python.org> on behalf of Gregory Salvan <apieum at gmail.com>
Sent: Monday, May 11, 2015 12:13 PM
To: Guido van Rossum
Cc: python-ideas at python.org
Subject: Re: [Python-ideas] Partial operator (and 'third-party methods' and 'piping') [was Re: Function composition (was no subject)]

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. :)




2015-05-11 16:41 GMT+02:00 Guido van Rossum <guido at python.org<mailto:guido at python.org>>:
As long as I'm "in charge" the chances of this (or anything like it) being accepted into Python are zero. I get a headache when I try to understand code that uses function composition, and I end up having to laboriously rewrite it using more traditional call notation before I move on to understanding what it actually does. Python is not Haskell, and perhaps more importantly, Python users are not like Haskel users. Either way, what may work out beautifully in Haskell will be like a fish out of water in Python.

I understand that it's fun to try to sole this puzzle, but evolving Python is more than solving puzzles. Enjoy debating the puzzle, but in the end Python will survive without the solution.

--
--Guido van Rossum (python.org/~guido<http://python.org/~guido>)

_______________________________________________
Python-ideas mailing list
Python-ideas at python.org<mailto:Python-ideas at python.org>
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20150511/ebd5c858/attachment-0001.html>


More information about the Python-ideas mailing list