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

Gregory Salvan apieum at gmail.com
Mon May 11 23:59:56 CEST 2015


In case you've not seen how it divides the volume of code you'll need to
write, here are tests of "is_email_address":

# What's an email address ?
def test_it_is_a_string(self):
    assert is_string in is_email_address

def test_it_has_a_user_name(self):
    assert has_user in is_email_address

def test_it_contains_at(self):
    assert has_char('@') in is_email_address

def test_it_has_a_domain_name(self):
    assert has_domain in is_email_address

# answer: an email address is a string with a user name, char '@' and a
domain name.

@Teddy Reedy with a class you'll have to write more tests and abuse of
inheritance.


2015-05-11 21:44 GMT+02:00 Gregory Salvan <apieum at gmail.com>:

> Andrew Barnet we disagree.
> In your example you have no information about if error comes from domain,
> user name or domain extension...
> Writing a big regexp with group... really ? it is easy to maintain, test
> and reuse ? and for a novice ? muliply this by thousands of validators and
> their respectives tests.
> I call that a mess and inside a project I lead, I will not accept it.
>
> Even in Haskell people rarelly use arrows, I don't criticize this choice
> as arrows comes from category theory and we are used to think inside ZF set
> theory.
> Somes prefer a syntax over another, there is not a good answer, but this
> also mean there is no irrelevant answer.
> In fact both exists and choosing within the case is never easy. Thinking
> the same way for each problem is also wrong, so I will never pretend to
> resolve every problem with a single lib.
>
> Now I understand this idea is not a priority, I've seen more and more
> threads about functional tools, I regret we can't find a solution but
> effectively this absence of solution now can't convince me to stop digging
> other paths. This is not irrespectuous.
>
>
>
> 2015-05-11 20:25 GMT+02:00 Andrew Barnert <abarnert at yahoo.com>:
>
>> On Monday, May 11, 2015 9:15 AM, Gregory Salvan <apieum at gmail.com> 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.
>>
>> OK, let's compare your example to a Pythonic implementation of the same
>> thing.
>>
>> import re
>>
>> ruser =
>> re.compile("^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*$")
>> rdomain =
>> re.compile("^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$")
>> rstrict_user = re.compile("^[a-z0-9][a-z0-9_-]+(?:\.[a-z0-9_-]+)*$")
>>
>>
>> def is_email_address(addr):
>>     user, domain = addr.split('@', 1)
>>     return ruser.match(user) and rdomain.match(domain)
>>
>> def is_strict_email_address(addr):
>>     user, domain = addr.split('@', 1)
>>     return rstrictuser.match(user) and rdomain.match(domain)
>>
>>
>> def is_org_address(addr):
>>     return is_email_address(addr) and addr.ends_with('.org')
>>
>> (An even better solution, given that you're already using regexps, might
>> be to just use a single regexp with named groups for the user or
>> strict-user, full domain, and TLD… but I've left yours alone.)
>>
>> Far from being more painful, the Pythonic version is easier to write,
>> easier to read, easier to debug, shorter, and understandable to even a
>> novice, without having to rewrite anything in your head. It also handles
>> invalid input by returning failure values and/or raising appropriate
>> exceptions rather than asserting and exiting. And it's almost certainly
>> going to be significantly more efficient. And it works with any string-like
>> type (that is, any type that has a .split method and works with re.match).
>> And if you have to debug something, you will have, e.g., values named user
>> and domain, rather than both being named value at different levels on the
>> call stack.
>>
>> If you really want to come up with a convincing example for your idea,
>> I'd take an example out of Learn You a Haskell or another book or tutorial
>> and translate that to Python with your library. I suspect it would still
>> have some of the same problems, but this example wouldn't even really be
>> good in Haskell, so it's just making it harder to see why anyone would want
>> anything like it. And by offering this as the response to Guido's "You're
>> never going to convince me," well, if he _was_ still reading this thread
>> with an open mind, he probably isn't anymore (although, to be honest, he
>> probably wasn't reading it anyway).
>>
>> >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>:
>> >
>> >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)
>> >>_______________________________________________
>> >>Python-ideas mailing list
>> >>Python-ideas at python.org
>> >>https://mail.python.org/mailman/listinfo/python-ideas
>> >>Code of Conduct: http://python.org/psf/codeofconduct/
>> >>
>> >
>> >
>> >_______________________________________________
>> >Python-ideas mailing list
>> >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/896d7d30/attachment.html>


More information about the Python-ideas mailing list