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@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@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@yahoo.com>:
On Monday, May 11, 2015 9:15 AM, Gregory Salvan <apieum@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@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@python.org
>>https://mail.python.org/mailman/listinfo/python-ideas
>>Code of Conduct: http://python.org/psf/codeofconduct/
>>
>
>
>_______________________________________________
>Python-ideas mailing list
>Python-ideas@python.org
>https://mail.python.org/mailman/listinfo/python-ideas
>Code of Conduct: http://python.org/psf/codeofconduct/
>
>