[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 21:44:19 CEST 2015


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/4e0bb57a/attachment-0001.html>


More information about the Python-ideas mailing list