Skip to main content

Function currying that can be statically typed.

Project description

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

# 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.

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.

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

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.

@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.

@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.

@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.

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
"""

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytyped-curry-1.0.1.tar.gz (8.0 kB view details)

Uploaded Source

Built Distribution

pytyped_curry-1.0.1-py3-none-any.whl (14.6 kB view details)

Uploaded Python 3

File details

Details for the file pytyped-curry-1.0.1.tar.gz.

File metadata

  • Download URL: pytyped-curry-1.0.1.tar.gz
  • Upload date:
  • Size: 8.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.10.4

File hashes

Hashes for pytyped-curry-1.0.1.tar.gz
Algorithm Hash digest
SHA256 16ff31762101c93ba15f0cd36801d64ddaf33d961c22165ffd9a70290f4b33f9
MD5 108a356b4c5cd2cfdb00a39dadd56fa5
BLAKE2b-256 485d87744fae4924a423ea8a0a78daa18fc2d1bb19cb5f691087c8027fae77cf

See more details on using hashes here.

File details

Details for the file pytyped_curry-1.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for pytyped_curry-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 491d1563daf6c9b83049d40067589210182a6351127b998b06f0ef8158cd94ef
MD5 ab9f041e64887bc19d13b0f27b2f82e8
BLAKE2b-256 d77b44fab200a7e2949c86d66d2bd1a8c5662edbdad6cbd466ff218282845c0b

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page