Syntactic sugar to declare partial functions

Hello, I would like to propose a new method to create a partial function. At the moment we have to load the *partial* function from the *functool* library, and apply it to an existing function, e.g. from functools import partial def add(x: int, y: int) -> int: return x + y add_2 = partial(add, 2) While partial expose the mechanism excellently its instantiation method is, at times, not very friendly, I would like to propose a syntactic sugar to create partial functions, in the case you create a partial function using *curly braces*: def add(x: int, y: int) -> int: return x + y add_2 = add{2} At the moment this causes SyntaxError so the change is retro-compatible. In the case of key word arguments we could have: sort_by_x = sort{key=lambda element: element.x} That could be good as it would be an easy way to pre-load functions without having to eagerly compute it, but without needing to pass the entire function parameters to to other scopes. # prepare the function get_sorted_users: Callable[[], Iterator[User]] = sort{users, key=lambda user : user.creation_date} # continue with job at hand ... # some where else, maybe another process sorted_users = list(get_sorted_users()) Even create a factory method on the fly: @dataclass class Product: name: str category: Category price: Decimal smartphone_factory = Product{category=smartphone_category} Now all this can already be done with partial, but adding this syntactic sugar would reduce the perception of `partial` as an advanced feature, alleviating the use of closures created only for the sake of avoiding an explicit partial. In my opinion this syntactic sugar has a lot of potential adoption seen the general interest in functional programming.

The funcoperators lib on pypi does exactly that: from funcoperators import partially @partially def add(x: int, y: int) -> int: return x + y add_2 = add[2] @partiallymulti def stuff(x,y,z): return x - y + 2*z sort = partially(sorted) sort_by_x = sort.key(key=lambda element: element.x) The ".key" means "give a keyword argument". The ".val" or [] gives a positional argument. The ".part" accept positional and keyword arguments. Le sam. 4 août 2018 à 18:03, Fabrizio Messina <zauddelig@gmail.com> a écrit :

Here's my opinion, in a nutshell. A diet with a small amount of sugar is healthy. But too much sugar is not. https://www.nhs.uk/live-well/eat-well/how-does-sugar-in-our-diet-affect-our-... I have a similar attitude to syntactic sugar. Sometimes helpful. https://news.ycombinator.com/item?id=16216994 Sometimes, perhaps not helpful. https://github.com/benthor/dictator https://github.com/czheo/syntax_sugar_python Every programming construct has semantics. Here's a suggestion. If you're proposing a syntax change, give a pure Python implementation of the semantics. For example (not tested). The construction
And if your proposal works with current Python, write and publish a pure Python implementation. And help others use it, listening to their feedback. So do it on github or the like, with an issue tracker. In short, if you have a proposal, publish some working code that implements the proposal. -- Jonathan

You can read the other functionalities on the project page : https://pypi.org/project/funcoperators/ And if you want to solve the "can't partial from right that allows to use the Ellipsis '...' : # the built-in "pow" doesn't take keyword arguments, so partial can't be used. from funcoperators import elipartial, bracket square = elipartial (pow, ..., 2) # = pow(something, 2) square(3) # 9 @bracket def f(x,y,z): return x - y + 2 * z r = f(1,2,3) g = f[1, ..., 3] # g = a function with one argument: y r = g(2) bracket merges the concept of partiallymulti, and elipartial. Partially Multi allowing to write f[1, 2] as a sugar for f[1][2] (which is different than partial(f, (1,2)) ). Le dim. 5 août 2018 à 00:18, Daniel. <danielhilst@gmail.com> a écrit :

On Sat, Aug 04, 2018 at 09:03:50AM -0700, Fabrizio Messina wrote:
While partial expose the mechanism excellently its instantiation method is, at times, not very friendly,
I disagree with *both* of those statements. As an implementation of partial function application, partial() has a couple of weaknesses: - no support for applying arguments from the right, except by keyword; - no automatic or easy way to inherit docstrings from the original function; - even if you manually set the partial function object's docstring, help() ignores it. (That's possibly not partial's fault.) On the other hand, I think that partial(func, arg, kw=value) is plenty friendly.
add_2 = add{2}
Too obscure. What do curly braces have to do with partial function application? It seems pretty arbitrary. I know that ultimately ALL conventions are arbitrary, but we already have a couple of very strong conventions for curly braces (dicts and sets) and mathematicians have a few strong conventions for partial function application and currying, neither of which uses curly brackets. Besides, if we ever did support some sort of function-call-like syntax using braces spam{...} I would hope it would be for something more important than partial function application. You should consider prior art: - Scala uses a special value, which when passed to a function, returns a new function: # using Python syntax def add(a, b): return a+b add1 = add(1, _) # like partial(add, 1) - Perl6 gives all functions an "assuming" method: add1 = add.assuming(1) - ML and Haskell define all functions as single-argument functions. Multiple arguments are actually just syntactic sugar: func(a, b, c) # syntactic sugar for func(a)(b)(c) so partial application from the left is trivial: add1 = add(1) - but most other languages that I know of use a function or keyword "partial" (or sometimes, and inaccurately, "curry").
Now all this can already be done with partial,
Indeed it can.
I don't think that is the case. I think that partial, and currying, are both relatively advanced features. Many programmers never quite grasp, or become comfortable with, functions as values. If we were to push partial application as a mainstream technique, and I'm not saying we should, but if we did, my vote would be to give function (and method) objects a partial method: add1 = add.partial(1) although possibly a less jargon name would be nicer: add1 = add.given(1) -- Steve

The funcoperators lib on pypi does exactly that: from funcoperators import partially @partially def add(x: int, y: int) -> int: return x + y add_2 = add[2] @partiallymulti def stuff(x,y,z): return x - y + 2*z sort = partially(sorted) sort_by_x = sort.key(key=lambda element: element.x) The ".key" means "give a keyword argument". The ".val" or [] gives a positional argument. The ".part" accept positional and keyword arguments. Le sam. 4 août 2018 à 18:03, Fabrizio Messina <zauddelig@gmail.com> a écrit :

Here's my opinion, in a nutshell. A diet with a small amount of sugar is healthy. But too much sugar is not. https://www.nhs.uk/live-well/eat-well/how-does-sugar-in-our-diet-affect-our-... I have a similar attitude to syntactic sugar. Sometimes helpful. https://news.ycombinator.com/item?id=16216994 Sometimes, perhaps not helpful. https://github.com/benthor/dictator https://github.com/czheo/syntax_sugar_python Every programming construct has semantics. Here's a suggestion. If you're proposing a syntax change, give a pure Python implementation of the semantics. For example (not tested). The construction
And if your proposal works with current Python, write and publish a pure Python implementation. And help others use it, listening to their feedback. So do it on github or the like, with an issue tracker. In short, if you have a proposal, publish some working code that implements the proposal. -- Jonathan

You can read the other functionalities on the project page : https://pypi.org/project/funcoperators/ And if you want to solve the "can't partial from right that allows to use the Ellipsis '...' : # the built-in "pow" doesn't take keyword arguments, so partial can't be used. from funcoperators import elipartial, bracket square = elipartial (pow, ..., 2) # = pow(something, 2) square(3) # 9 @bracket def f(x,y,z): return x - y + 2 * z r = f(1,2,3) g = f[1, ..., 3] # g = a function with one argument: y r = g(2) bracket merges the concept of partiallymulti, and elipartial. Partially Multi allowing to write f[1, 2] as a sugar for f[1][2] (which is different than partial(f, (1,2)) ). Le dim. 5 août 2018 à 00:18, Daniel. <danielhilst@gmail.com> a écrit :

On Sat, Aug 04, 2018 at 09:03:50AM -0700, Fabrizio Messina wrote:
While partial expose the mechanism excellently its instantiation method is, at times, not very friendly,
I disagree with *both* of those statements. As an implementation of partial function application, partial() has a couple of weaknesses: - no support for applying arguments from the right, except by keyword; - no automatic or easy way to inherit docstrings from the original function; - even if you manually set the partial function object's docstring, help() ignores it. (That's possibly not partial's fault.) On the other hand, I think that partial(func, arg, kw=value) is plenty friendly.
add_2 = add{2}
Too obscure. What do curly braces have to do with partial function application? It seems pretty arbitrary. I know that ultimately ALL conventions are arbitrary, but we already have a couple of very strong conventions for curly braces (dicts and sets) and mathematicians have a few strong conventions for partial function application and currying, neither of which uses curly brackets. Besides, if we ever did support some sort of function-call-like syntax using braces spam{...} I would hope it would be for something more important than partial function application. You should consider prior art: - Scala uses a special value, which when passed to a function, returns a new function: # using Python syntax def add(a, b): return a+b add1 = add(1, _) # like partial(add, 1) - Perl6 gives all functions an "assuming" method: add1 = add.assuming(1) - ML and Haskell define all functions as single-argument functions. Multiple arguments are actually just syntactic sugar: func(a, b, c) # syntactic sugar for func(a)(b)(c) so partial application from the left is trivial: add1 = add(1) - but most other languages that I know of use a function or keyword "partial" (or sometimes, and inaccurately, "curry").
Now all this can already be done with partial,
Indeed it can.
I don't think that is the case. I think that partial, and currying, are both relatively advanced features. Many programmers never quite grasp, or become comfortable with, functions as values. If we were to push partial application as a mainstream technique, and I'm not saying we should, but if we did, my vote would be to give function (and method) objects a partial method: add1 = add.partial(1) although possibly a less jargon name would be nicer: add1 = add.given(1) -- Steve
participants (6)
-
Daniel.
-
Fabrizio Messina
-
Jonathan Fine
-
Neil Girdhar
-
Robert Vanden Eynde
-
Steven D'Aprano