pytyped-curry


Namepytyped-curry JSON
Version 1.0.2 PyPI version JSON
download
home_page
SummaryFunction currying that can be statically typed.
upload_time2023-02-07 13:52:10
maintainer
docs_urlNone
author
requires_python>=3.5
licensemit
keywords pretty print format indent dataclass
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # pytyped-curry
 Function currying that can be statically typed.

Functional tool for currying a function. Currying a function makes
the function callable multiple times before the function is actually
ran. Use `curry(n)(func)` or `@curry(n)` to transform a function into
a curried function which takes `n` calls before running.

## Example

```python
# Transform the function into a curried function that takes
# two function calls before running.
@curry(2)
def add(x: int, y: int) -> int:
    return x + y

# Add needs to be called twice to be ran.
add(2)(3)  # 5

# Partial evaluation is easy.
increment = add(1)

increment(5)  # 6

# The two arguments accept multiple forms.
add(x=2)(y=3)
add(y=3)(x=2)
add(2, 3)()
add()(2, 3)
```

## For Decorators

Often times writing decorators requires writing several nested functions.
This is often a hassle, and in many cases completely unnecessary due to
currying.

Note: `reveal_type` is ran using `mypy`.

```python
from typing import Callable, TypeVar

T = typing.TypeVar("T")
RT = typing.TypeVar("RT")

@curry(2, ...)
def decorator(func: Callable[[T], RT], x: T) -> RT:
    print("Start")
    y = func(x)
    print("Finished")
    return y

reveal_type(decorator)
"""
def (def (T`-1) -> RT`-2) -> def (T`-1) -> RT`-2
"""

@decorator
def increment(x: int) -> int:
    return x + 1

reveal_type(increment)
"""
def (builtins.int) -> builtins.int
"""

@curry(3, ...)
def rate_limit(timeout: float, func: Callable[[T], RT], x: T) -> RT:
    time.sleep(timeout)
    return func(x)

reveal_type(rate_limit)
"""
def (builtins.float) -> (def (T`-1) -> RT`-2) -> def (T`-1) -> RT`-2
"""

@rate_limit(5)
def request_data(name: str) -> int:
    return len(name)

reveal_type(request_data)
"""
def (builtins.str) -> builtins.int
"""
```

## Documentation

*New in Python 3.9*

Doc-strings can be applied to arbitrary objects at runtime for runtime use
with the `help(...)` function. A few additional pieces of metadata are also
accessible at runtime to provide clearer documentation, such as the name of
the result.

```python
@curry(3)
def add(x: int, y: int, z: int) -> int:
    """Returns add(x)(y)(z) = x + y + z."""
    return x + y + z

help(add)
"""
Help on Curried in module __main__:

add = curry(3)(add(x: int, y: int, z: int) -> int)
    Returns add(x)(y)(z) = x + y + z.

""""

help(add(1))
"""
Help on Curried in module __main__:

add(1) = curry(2)(add(x: int, y: int, z: int) -> int, 1)
    Returns add(x)(y)(z) -> x + y + z.

"""

help(add(1)(2))
"""
Help on Curried in module __main__:

add(1, 2) = curry(1)(add(x: int, y: int, z: int) -> int, 1, 2)
    Returns add(x)(y)(z) -> x + y + z.

"""

add(1)(2)(3)  # 6
```

## Type-Hinting

*New in Python 3.8*

Type-hints for curried functions are nigh impossible in the general case, as
can be seen by the last example. However, this doesn't stop us from enabling
typing in many common use-cases. Curried functions are hinted as functions
which take any arguments but take `n` calls, up to `n = 3` for Python <
(3, 11) and up to `n = 4` otherwise. Although the arguments are not
preserved, the final return type is.

Note: `reveal_type` is ran using `mypy`.

```python
@curry(2)
def add(x: int, y: int) -> int:
    return x + y

reveal_type(add)
"""
def (*Any, **Any) -> def (*Any, **Any) -> builtins.int
"""
```

For Python < (3, 11), one can also use `curry(n, ...)` to hint the curried
function as taking exactly `1` positional argument per call, up to `n = 3`.

```python
@curry(2, ...)
def add(x: int, y: int) -> int:
    return x + y

reveal_type(add)
"""
def (builtins.int) -> def (builtins.int) -> builtins.int
"""
```

For Python >= (3, 11), one can also use `curry(n, ...)` to hint the curried
function as taking exactly `1` positional argument per call, up to `n = 3`,
except for the last call. Notice that the `y` parameter is preserved as a
positional-or-keyword parameter.

```python
@curry(2, ...)
def add(x: int, y: int) -> int:
    return x + y

reveal_type(add)
"""
def (builtins.int) -> def (y: builtins.int) -> builtins.int
"""
```

For more precise hinting, one must use `typing.cast` around the currying
function.

```python
from typing import Protocol, overload


class Add(Protocol):

    @typing.overload
    def __call__(self, x: int, y: int) -> AddEmpty:
        ...

    @typing.overload
    def __call__(self, x: int) -> AddY:
        ...

    @typing.overload
    def __call__(self, *, y: int) -> AddX:
        ...

    def __call__(self, x, y):
        ...


class AddEmpty(Protocol):

    def __call__(self) -> int:
        ...


class AddX(Protocol):

    def __call__(self, x: int) -> int:
        ...


class AddY(Protocol):

    def __call__(self, y: int) -> int:
        ...


@typing.cast(Add, curry(2))
def add(x: int, y: int) -> int:
    return x + y

reveal_type(add)
"""
__main__.Add
"""
```

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "pytyped-curry",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.5",
    "maintainer_email": "",
    "keywords": "pretty,print,format,indent,dataclass",
    "author": "",
    "author_email": "",
    "download_url": "https://files.pythonhosted.org/packages/8d/00/afec7bd46375944ac6bb951502e7d9565dba75be9a7c9a559916d4c5cd56/pytyped-curry-1.0.2.tar.gz",
    "platform": null,
    "description": "# pytyped-curry\r\n Function currying that can be statically typed.\r\n\r\nFunctional tool for currying a function. Currying a function makes\r\nthe function callable multiple times before the function is actually\r\nran. Use `curry(n)(func)` or `@curry(n)` to transform a function into\r\na curried function which takes `n` calls before running.\r\n\r\n## Example\r\n\r\n```python\r\n# Transform the function into a curried function that takes\r\n# two function calls before running.\r\n@curry(2)\r\ndef add(x: int, y: int) -> int:\r\n    return x + y\r\n\r\n# Add needs to be called twice to be ran.\r\nadd(2)(3)  # 5\r\n\r\n# Partial evaluation is easy.\r\nincrement = add(1)\r\n\r\nincrement(5)  # 6\r\n\r\n# The two arguments accept multiple forms.\r\nadd(x=2)(y=3)\r\nadd(y=3)(x=2)\r\nadd(2, 3)()\r\nadd()(2, 3)\r\n```\r\n\r\n## For Decorators\r\n\r\nOften times writing decorators requires writing several nested functions.\r\nThis is often a hassle, and in many cases completely unnecessary due to\r\ncurrying.\r\n\r\nNote: `reveal_type` is ran using `mypy`.\r\n\r\n```python\r\nfrom typing import Callable, TypeVar\r\n\r\nT = typing.TypeVar(\"T\")\r\nRT = typing.TypeVar(\"RT\")\r\n\r\n@curry(2, ...)\r\ndef decorator(func: Callable[[T], RT], x: T) -> RT:\r\n    print(\"Start\")\r\n    y = func(x)\r\n    print(\"Finished\")\r\n    return y\r\n\r\nreveal_type(decorator)\r\n\"\"\"\r\ndef (def (T`-1) -> RT`-2) -> def (T`-1) -> RT`-2\r\n\"\"\"\r\n\r\n@decorator\r\ndef increment(x: int) -> int:\r\n    return x + 1\r\n\r\nreveal_type(increment)\r\n\"\"\"\r\ndef (builtins.int) -> builtins.int\r\n\"\"\"\r\n\r\n@curry(3, ...)\r\ndef rate_limit(timeout: float, func: Callable[[T], RT], x: T) -> RT:\r\n    time.sleep(timeout)\r\n    return func(x)\r\n\r\nreveal_type(rate_limit)\r\n\"\"\"\r\ndef (builtins.float) -> (def (T`-1) -> RT`-2) -> def (T`-1) -> RT`-2\r\n\"\"\"\r\n\r\n@rate_limit(5)\r\ndef request_data(name: str) -> int:\r\n    return len(name)\r\n\r\nreveal_type(request_data)\r\n\"\"\"\r\ndef (builtins.str) -> builtins.int\r\n\"\"\"\r\n```\r\n\r\n## Documentation\r\n\r\n*New in Python 3.9*\r\n\r\nDoc-strings can be applied to arbitrary objects at runtime for runtime use\r\nwith the `help(...)` function. A few additional pieces of metadata are also\r\naccessible at runtime to provide clearer documentation, such as the name of\r\nthe result.\r\n\r\n```python\r\n@curry(3)\r\ndef add(x: int, y: int, z: int) -> int:\r\n    \"\"\"Returns add(x)(y)(z) = x + y + z.\"\"\"\r\n    return x + y + z\r\n\r\nhelp(add)\r\n\"\"\"\r\nHelp on Curried in module __main__:\r\n\r\nadd = curry(3)(add(x: int, y: int, z: int) -> int)\r\n    Returns add(x)(y)(z) = x + y + z.\r\n\r\n\"\"\"\"\r\n\r\nhelp(add(1))\r\n\"\"\"\r\nHelp on Curried in module __main__:\r\n\r\nadd(1) = curry(2)(add(x: int, y: int, z: int) -> int, 1)\r\n    Returns add(x)(y)(z) -> x + y + z.\r\n\r\n\"\"\"\r\n\r\nhelp(add(1)(2))\r\n\"\"\"\r\nHelp on Curried in module __main__:\r\n\r\nadd(1, 2) = curry(1)(add(x: int, y: int, z: int) -> int, 1, 2)\r\n    Returns add(x)(y)(z) -> x + y + z.\r\n\r\n\"\"\"\r\n\r\nadd(1)(2)(3)  # 6\r\n```\r\n\r\n## Type-Hinting\r\n\r\n*New in Python 3.8*\r\n\r\nType-hints for curried functions are nigh impossible in the general case, as\r\ncan be seen by the last example. However, this doesn't stop us from enabling\r\ntyping in many common use-cases. Curried functions are hinted as functions\r\nwhich take any arguments but take `n` calls, up to `n = 3` for Python <\r\n(3, 11) and up to `n = 4` otherwise. Although the arguments are not\r\npreserved, the final return type is.\r\n\r\nNote: `reveal_type` is ran using `mypy`.\r\n\r\n```python\r\n@curry(2)\r\ndef add(x: int, y: int) -> int:\r\n    return x + y\r\n\r\nreveal_type(add)\r\n\"\"\"\r\ndef (*Any, **Any) -> def (*Any, **Any) -> builtins.int\r\n\"\"\"\r\n```\r\n\r\nFor Python < (3, 11), one can also use `curry(n, ...)` to hint the curried\r\nfunction as taking exactly `1` positional argument per call, up to `n = 3`.\r\n\r\n```python\r\n@curry(2, ...)\r\ndef add(x: int, y: int) -> int:\r\n    return x + y\r\n\r\nreveal_type(add)\r\n\"\"\"\r\ndef (builtins.int) -> def (builtins.int) -> builtins.int\r\n\"\"\"\r\n```\r\n\r\nFor Python >= (3, 11), one can also use `curry(n, ...)` to hint the curried\r\nfunction as taking exactly `1` positional argument per call, up to `n = 3`,\r\nexcept for the last call. Notice that the `y` parameter is preserved as a\r\npositional-or-keyword parameter.\r\n\r\n```python\r\n@curry(2, ...)\r\ndef add(x: int, y: int) -> int:\r\n    return x + y\r\n\r\nreveal_type(add)\r\n\"\"\"\r\ndef (builtins.int) -> def (y: builtins.int) -> builtins.int\r\n\"\"\"\r\n```\r\n\r\nFor more precise hinting, one must use `typing.cast` around the currying\r\nfunction.\r\n\r\n```python\r\nfrom typing import Protocol, overload\r\n\r\n\r\nclass Add(Protocol):\r\n\r\n    @typing.overload\r\n    def __call__(self, x: int, y: int) -> AddEmpty:\r\n        ...\r\n\r\n    @typing.overload\r\n    def __call__(self, x: int) -> AddY:\r\n        ...\r\n\r\n    @typing.overload\r\n    def __call__(self, *, y: int) -> AddX:\r\n        ...\r\n\r\n    def __call__(self, x, y):\r\n        ...\r\n\r\n\r\nclass AddEmpty(Protocol):\r\n\r\n    def __call__(self) -> int:\r\n        ...\r\n\r\n\r\nclass AddX(Protocol):\r\n\r\n    def __call__(self, x: int) -> int:\r\n        ...\r\n\r\n\r\nclass AddY(Protocol):\r\n\r\n    def __call__(self, y: int) -> int:\r\n        ...\r\n\r\n\r\n@typing.cast(Add, curry(2))\r\ndef add(x: int, y: int) -> int:\r\n    return x + y\r\n\r\nreveal_type(add)\r\n\"\"\"\r\n__main__.Add\r\n\"\"\"\r\n```\r\n",
    "bugtrack_url": null,
    "license": "mit",
    "summary": "Function currying that can be statically typed.",
    "version": "1.0.2",
    "split_keywords": [
        "pretty",
        "print",
        "format",
        "indent",
        "dataclass"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "64341da2b4f01afc131f518fb3a2ef34047e9667bcf19aece657321eb7c71e3e",
                "md5": "37c88dc7c0e6632449c1b2d2dd8d2367",
                "sha256": "fba88a96ca74df3149bc77290fba2f825572e4bccd6ed2bd65372a6fad8b79fc"
            },
            "downloads": -1,
            "filename": "pytyped_curry-1.0.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "37c88dc7c0e6632449c1b2d2dd8d2367",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.5",
            "size": 14628,
            "upload_time": "2023-02-07T13:52:08",
            "upload_time_iso_8601": "2023-02-07T13:52:08.979399Z",
            "url": "https://files.pythonhosted.org/packages/64/34/1da2b4f01afc131f518fb3a2ef34047e9667bcf19aece657321eb7c71e3e/pytyped_curry-1.0.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8d00afec7bd46375944ac6bb951502e7d9565dba75be9a7c9a559916d4c5cd56",
                "md5": "1961b45c1038c16d13356514e2e579d2",
                "sha256": "2e5529b560b54aacbe824310d7f11fa14f2989d89b48bfef8a0b57f17279b850"
            },
            "downloads": -1,
            "filename": "pytyped-curry-1.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "1961b45c1038c16d13356514e2e579d2",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.5",
            "size": 8022,
            "upload_time": "2023-02-07T13:52:10",
            "upload_time_iso_8601": "2023-02-07T13:52:10.761432Z",
            "url": "https://files.pythonhosted.org/packages/8d/00/afec7bd46375944ac6bb951502e7d9565dba75be9a7c9a559916d4c5cd56/pytyped-curry-1.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-02-07 13:52:10",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "lcname": "pytyped-curry"
}
        
Elapsed time: 0.36544s