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

Gregory Salvan apieum at gmail.com
Tue May 12 00:12:21 CEST 2015


Sorry the fun part: the more you write code the less you have to write
tests.

# what's a strict email address:
def test_it_is_an_email_address_with_a_strict_user_name(self):
    assert is_email_address.replace(has_user, has_strict_user) ==
is_strict_email_address

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

> 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/20150512/b00c70da/attachment-0001.html>


More information about the Python-ideas mailing list