Niriel / yummycurry

Automatic currying, uncurrying and application of functions and methods in Python
MIT License
2 stars 0 forks source link

Friendly ping from dry-python/returns #4

Open sobolevn opened 4 years ago

sobolevn commented 4 years ago

Hi @Niriel! Thanks a lot for building this project. It looks amazing.

Currently we are working on better typed FP stack in Python. It has some great results already, but also some limitation.

During this release I am focused on working with callables, recently I did fully typed partial function: https://returns.readthedocs.io/en/latest/pages/curry.html And now I researching @curry decorator. I would ideally love to see something very similar to your solution.

So, there are several ways for us to continue:

  1. We can collaborate on this together: in this case we will just recommend your library in our docs and treat as a separate but complimentary package. I can also help with some other features we need for these projects to be compatible: like mypy plugin and python[3.6, 3.7] support
  2. We can tests all cases we need and port everything directly into returns project and I will add you as a collaborator
  3. I can continue from here alone if you have no interest or time to maintain this library. In this case I can probably use some of the code you wrote (which is allowed under MIT license)

What suits you best?

Cheers, Nikita Sobolev

Niriel commented 4 years ago

Hello!

I have some free time these days so I'd be happy to work on it.

I like the idea of keeping yummycurry its own package, in the tradition of doing one thing and doing it well. I'm curious to see it used in the real world (so far I just made it for myself); there are definitely things I've not thought about when I wrote it.

About Python 3.7: the main "problem" is the introduction of the positional-only arguments introduced in 3.8, which yummycurry expects. This feature needs to be switched off for earlier Python versions. Not sure how to do that nicely. I could at least setup some tox magic to automate the testing on various versions; that would be a first step.

Give me a couple of days to get familiar with my own package again; I've been busy with GUI stuff lately, which is as far as FP as you can get, so I need to refresh my memory :D.

sobolevn commented 4 years ago

Thanks a lot, @Niriel! I would love to help you with the mypy stuff.

Let me better explain why I push currying so much for returns.

Currently, all types we provide are not actually monads. Because they are not applicative. This was intentional, because without @curry the only possible way to achieve working .apply method with several arguments is overusing lambda functions. We will end up with something like this:

Some(3).apply(Some(2).apply(Some(1).map(lambda a: lambda b: lambda c: a + b + c)))

But, when we would have typed curry, we will be allowed to use:

@curry
def some_three(a: int, b: int, c: int) -> int:
     return a + b + c

Some(3).apply(Some(2).apply(Some(1).map(some_three)))

Which is times better!

So, our goal for the next release (not the one we are working on right now) to add .apply and @curry. And while .apply is our own stuff, @curry can be imported from whatever package user prefers as long as it is properly typed, that's 100% fine by me.

Related: https://github.com/dry-python/returns/issues/342

Niriel commented 4 years ago

I have no idea how to write a MyPy plugin; any help or suggestion would be lovely indeed.

What is Some here? Looks like the Identity functor. Is the name taken from a language I don't know? And yes, lambdas are a nightmare to write, read and debug :D.

How "properly typed" are we talking about, though? What signature should I give my curry function? Is f:Callable[..., A] good enough for its first parameter? It's quite hard to be more explicit. Actually it's even worse, as yummycurry.curry also takes non-callable objects (it just returns them). I guess Union can take care of that but we really cannot go very far.

I'm curious about your monads. apply is usually not a problem, it's pure/return/wrap that's difficult. The lack of return-value polymorphism makes it hard to write a function a -> f a where f is an applicative or a monad that we don't know because it's not passed as a parameter.

sobolevn commented 4 years ago

@Niriel we have a lot of hacks to fight HKT in python 😆 I have even tried to bring this into a separate mypy plugin.

As I said, I can take the mypy part. I have done quite a lot of work with mypy. For example, here's our plugin for the similar feature, we type partial function: https://github.com/dry-python/returns/blob/master/returns/contrib/mypy/_curry.py And here are our type-tests to showcase real-life examples: https://github.com/dry-python/returns/tree/master/typesafety/test_curry

And I want something similar for @curry. Let me show you some examples:

@curry
def x(a: int, b: int) -> int:
    return a + b

reveal_type(x)  # Would be Overloaded([(def a:int -> def x: int -> int), (def a: int, b: int -> int])

And the same for all possible argument types and values, including default values, kw and pos only, args and kwargs. And including Generics and @overloads.