ISTM the only time you come out a little ahead is if you know in advance that a function is going to be partialed (so you can decorate it), that you will need to partial arguments in other than a left to right fashion (otherwise a plain partial will suffice), that you won't use keyword arguments (the current partial works with keywords), and that you really dislike the lambda keyword (which neatly covers all cases from simple to complex without a new notation).
That said, this is a clever recipe. Kudos to the author for putting it together. I don't think it really rises to the level of "better" or "more intuitive", but it is an interesting experiment.
What if you have a function "f" that takes like 10 arguments and you need to pass it to a function "g" that expects as input a function takes 8 arguments.
I was gonna say cool for FP guys but alien to the Python world, but then i saw the f(..., x=1) syntax and decided you're a genius. Really nice idea.
In practice i worry about static checkers and IDE param hints though, how have you fared with these?
Not your fault at all, but mypy/pyright have very poor support for wrappers like this, and in IDEs it's common for their arg hints to devolve to a copout (args, kwargs) as well.
> Not your fault at all, but mypy/pyright have very poor support for wrappers like this, and in IDEs it's common for their arg hints to devolve to a copout (args, kwargs) as well.
You might be able to annotate it accurately with generics, ParamSpec and Concatenate[1].
High praise :-) I'm glad you like it! I don't know much about static checks / param hinting. TBH, I coded this up because I wanted to imagine how functions should work in Python as more of a fun exercise.
Accordingly there are a lot of things I have to do to make better_partial a strong tool for the community. I'll probably have time to work on the project again later in the week.
Hey, I use functools.partial a lot in my projects and I've always felt like it was pretty limited in the types of partial function application it could accomplish. I put together a modified version of partial over the weekend that I wanted to share / get feedback on. Let me know what you all think!
`functools.partial` has been bothering me for a long time and I've always thought it'd be nice to have something like the placeholder `·` that people use in math (i.e. `f(·, y, z)`). It didn't occur to me that a decorator might actually get us there without having to introduce new syntax. Nice project!
What specifically bothers you about functools partial? I think the decorator is nice in certain ways. However, then you have to decorate your code and guess where you’re going toy need the partials or create wrappers just to add the decorator which seems undesirable for a number of cases. I agree that partial has warts when it comes to inspection but at least they’re easy to make anywhere you want without having to apply any changes to the callee. Nice to have another option for partials though so that when you need these features there’s a way to get them.
This is a bit of a personal bugbear, but partial function application is not currying. I wrote a blog post on this a while ago because it's such a common misconception.
The link you posted is just using the word wrong. Currying is not syntactic sugar for partial application, it is the opposite. (ie currying builds functions which take multiple arguments from chains of functions which each take a single argument)
Yes! I almost made this mistake. Although I'm not sure @partial is the most apt name for the decorator, as it produces a function which can be partially applied. In retrospect, @partialize or something like it might be more appropriate.
It doesn't look natural to me nesting one arg functions in Python: lambda x: lambda y: lambda z: ..
What would the harm be to adapt the abstract notion (curry) for the specific language instead of trying to apply it literally (ugly)?
For example, I remember the notion of iteration is introduced in SICP using the syntax that were it to be translated literally into Python would look like recursive calls— obviously it would be unnatural to represent iterations this way in Python.
Can these be serialized? One of my main uses for partial is passing things to multiprocessing pool.map. People often reach for lambda but it cannot be pickled/serialized so it fails.
I'll look into this soon. I honestly wasn't expecting this much traffic on what essentially was a weekend project, so I'll need to spend a bit of time making the code less insane and testing for things like pickle-ability.
In my opinion, I'd love to see this baked in as part of the language. Getting buy in from my team to use this in production is just not going to happen. But cool package though!
Thanks! I would love to see it baked in as well. It'd be nice if, (1) all functions defaulted to behaving like better_partial.partial decorated functions and (2) similar to Ellipsis, there was some other special sentinel that we could use in place of _ (to avoid conflicting with the convention of using _ for unused variables).
I'm sure it's nice, but adding another dependency to save some keystrokes in an edge case --- no thanks. If I want lots of partial application I'll use Haskell.
As pointed out, you could import _ directly into your namespace, or map it to some other shorthand name that doesn't interfere with your naming conventions.
Also note that the _ placeholder isn't the only way that better_partial helps you. You can also use ... to indicate that you want to omit all arguments except ones you explicitly pass as kwargs. For instance:
This is not a new idea. siuba (a dplyr-like Pandas alternative) is one project that does it, and it also has a "pipeline operator": https://github.com/machow/siuba
Sure, I'm not claiming to have originated the idea of using _ or some other placeholder to return partially applied functions. Lots of languages have some kind of shorthand syntax for this. I just hadn't seen it done in Python before.
Coconut looks cool btw! I'll give it a closer look soon.
I'm glad you like it! I agree it's not optimal to have to wrap existing functions. One option would be to decorate the functions as they're defined, although you cant do this for external functions.
In my perfect world, python functions would default to behaving as they do when decorated with better_partial.partial, but it seems like a long-shot to try to get something like this added to the language itself.
While it does solve the problem with functools.partial() forcing you to use keyword arguments in some cases, I don't think currying is a common enough operation to warrant a whole lib, or even a new syntax.
I'd rather see partial() promoted to a universal static method attached to any callable, so it's easier to discover, and use.
I guess it depends what kind of stuff you're working on. I do a lot of work with jax for my research (https://github.com/google/jax) and use functools.partial a lot.
The work you've done is nice, but using a package for this issue gives off bad code smell. I'd much rather refine my functions and their relations rather than require an extra dependency that reduces readability for my coworkers.
Thanks for the feedback! This project was really me just imagining how I'd like functions to behave in Python. I didn't really expect there to be this much interest.
whats wrong with functools.partial? I don't see that anywhere in the arguments.. all you say is "I find functools.partial unintuitive".. why? its a simple wrapper.
That said, this is a clever recipe. Kudos to the author for putting it together. I don't think it really rises to the level of "better" or "more intuitive", but it is an interesting experiment.