Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support (un)curry up to 20 arguments #3555

Merged
merged 2 commits into from
Oct 20, 2023
Merged

Conversation

MangelMaxime
Copy link
Member

#3514

@dbrattli Can you please have a look at the fable-library-py/Util.py.

I don't understand why Pylance generates errors when the function as more that 10 arguments.

CleanShot 2023-10-19 at 22 15 42@2x

I tried to check for syntax errors, but could not find any.

@dbrattli
Copy link
Collaborator

@MangelMaxime the thing here is that Python is a bit different with typing than e.g. F#. A generic type argument in Python must be used more than one time in a function signature to make sense (which makes sense for F# as well when you think about it). So, if a function has e.g a type _T11 that is used only once, you can just as well replace it with Any.

So, for the outer function signature you can see that the type arguments make sense because it locks the type of the input arguments to the output arguments. But for the inner functions e.g:

def f2(a1: _T1, a2: _T2, a3: _T3, a4: _T4, a5: _T5) -> _TResult:
        return f(a1)(a2)(a3)(a4)(a5)

You can see that the type arguments are all different. Thus, they do not provide any additional value. No input is locked to any output. They do not inherit the outer type arguments, so they are not locked to the outer function declaration either. E.g a function like:

def id(value: _T1) -> _T2:
   return value

is no different than

def id(value: Any) -> Any:
   return value

Thus, the inner functions should be written like this instead to avoid the type errors you see:

def f2(a1: Any, a2: Any, a3: Any, a4: Any, a5: Any) -> Any:
        return f(a1)(a2)(a3)(a4)(a5)

@MangelMaxime
Copy link
Member Author

Thanks for the explanation but why does it work for uncurry2 to uncurry10?

And doesn't anymore starting from uncurry11?

If I understand correctly the function below should have the errors too on f2 definition but that's not the case.

def uncurry2(
    f: Callable[[_T1], Callable[[_T2], _TResult]]
) -> Callable[[_T1, _T2], _TResult]:
    def f2(a1: _T1, a2: _T2) -> _TResult:
        return f(a1)(a2)

    _curried[f2] = f
    return f2

Another interrogation is why would the error only be on _T11, _T12, _T13, etc. and not all the types starting from _T1 ?

CleanShot.2023-10-20.at.14.39.40.mp4

Replacing _TX with Any for uncurry11 and plus, makes the error goes away. If you are fine with that I can make the change.

@dbrattli
Copy link
Collaborator

Yes, it's fine. We have no clue about whatTX is and it doesn't really matter. It's just important that the signature of the outer function locks input types to output types.

@MangelMaxime MangelMaxime merged commit b1ab3e6 into main Oct 20, 2023
11 checks passed
@MangelMaxime MangelMaxime deleted the curry_and_uncurry_20 branch March 1, 2024 08:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants