Hello everyone, Several weeks ago I have presented you typed `partial` function as a part of `dry-python/returns` project. But, why should we stop there? Now, I am happy to present fully typed `@curry` decorator for your functions. However, there are several issues with instance and class methods I would like to be helped with. Implementation: https://github.com/orsinium-forks/returns/blob/curry/returns/curry.py#L38 Plugin: https://github.com/orsinium-forks/returns/blob/0b3d0ab3204e6732b993aae76920b... Tests: https://github.com/orsinium-forks/returns/blob/curry/tests/test_curry/test_c... Typetests: https://github.com/orsinium-forks/returns/tree/curry/typesafety/test_curry/t... Here's how it works: from returns.curry import curry @curry def func(a: int, b: str, c: float) -> bool: ... reveal_type(func) reveal_type(func(1)) reveal_type(func(1)('a')) reveal_type(func(1, 'a')) Output: ex.py:1: note: Revealed type is ' Overload( def (a: builtins.int) -> Overload( def (b: builtins.str, c: builtins.float) -> builtins.bool, def (b: builtins.str) -> def (c: builtins.float) -> builtins.bool ), def (a: builtins.int, b: builtins.str) -> def (c: builtins.float) -> builtins.bool, def (a: builtins.int, b: builtins.str, c: builtins.float) -> builtins.bool )' ex.py:2: note: Revealed type is ' Overload( def (b: builtins.str, c: builtins.float) -> builtins.bool, def (b: builtins.str) -> def (c: builtins.float) -> builtins.bool )' ex.py:3: note: Revealed type is 'def (c: builtins.float) -> builtins.bool' ex.py:4: note: Revealed type is 'def (c: builtins.float) -> builtins.bool' Basically, we generate a deeply nested tree of all possible argument paths and then build Overloaded cases for each of them. Here's the interesting part: function `func` can be called with `a` as the first and the only argument. In this case we will choose this overload: def (a: builtins.int) -> Overload( def (b: builtins.str, c: builtins.float) -> builtins.bool, def (b: builtins.str) -> def (c: builtins.float) -> builtins.bool ) Which is cool! It works with: - regular functions - generic functions - unbound methods - staticmethods You can check out our typetests here: https://github.com/orsinium-forks/returns/tree/curry/typesafety/test_curry/t... But there are known issues at the moment with bound methods. Let me show you the problem. For example, we can take this piece of code: class Test(object): @curry def some(self, a: int, b: str) -> float: ... Let's think of what type should `Test().some` have, it should be an overload of: - (a: int) -> (b:str) -> float - (a:int, b: str) -> float That's what we have instead. Our plugin gives an incorrect result (because we cannot know if this is a function or method at the moment): Overload( def (self: ex.Test) -> Overload( def (a: builtins.int, b: builtins.str) -> builtins.float, def (a: builtins.int) -> def (b: builtins.str) -> builtins.float ), def (self: ex.Test, a: builtins.int) -> def (b: builtins.str) -> builtins.float, def (self: ex.Test, a: builtins.int, b: builtins.str) -> builtins.float ) Notice that it has an extra `self` argument. But, mypy reveals a completely different type: reveal_type(Test().some) ex.py:10: note: Revealed type is ' Overload( def () -> Overload( def (a: builtins.int, b: builtins.str) -> builtins.float, def (a: builtins.int) -> def (b: builtins.str) -> builtins.float ), def (a: builtins.int) -> def (b: builtins.str) -> builtins.float, def (a: builtins.int, b: builtins.str) -> builtins.float )' Looks like it removes `self` argument from all cases to be similar to what Python does. So, my questions are: 1. How can I know if this is a method or function from `CallableType` or `FunctionContext`? 2. Is removing the first `self` / `cls` argument with `mypy.typeops.bind_self` good enough for this usecase? Or just dropping it from `arg_types` / `arg_names` is better? 3. Are there any useful APIs to help me with this problem? I would really appreciate your help! Failing CI with real tests cases and error logs: https://github.com/dry-python/returns/runs/653833953?check_suite_focus=true#... Upstream: https://github.com/dry-python/returns Thanks a lot for your work on mypy project! It is awesome! Best, Nikita Sobolev https://github.com/sobolevn
participants (1)
-
Никита Соболев