Skip to content

Instantly share code, notes, and snippets.

@qexat
Created November 30, 2023 21:32
Show Gist options
  • Select an option

  • Save qexat/0f23a14a3ad61572dd6cfa7c82ad104d to your computer and use it in GitHub Desktop.

Select an option

Save qexat/0f23a14a3ad61572dd6cfa7c82ad104d to your computer and use it in GitHub Desktop.
whatever this is
from __future__ import annotations
import abc
import collections.abc
import dataclasses
import functools
import typing
T = typing.TypeVar("T")
U = typing.TypeVar("U")
V = typing.TypeVar("V")
W = typing.TypeVar("W")
T_co = typing.TypeVar("T_co", covariant=True)
T_contra = typing.TypeVar("T_contra", contravariant=True)
U_co = typing.TypeVar("U_co", covariant=True)
U_contra = typing.TypeVar("U_contra", contravariant=True)
Ts = typing.TypeVarTuple("Ts")
P = typing.ParamSpec("P")
def compose(
func1: collections.abc.Callable[P, U],
func2: collections.abc.Callable[[U], V],
) -> collections.abc.Callable[P, V]:
"""
Compose two functions together into one.
"""
def inner(*args: P.args, **kwargs: P.kwargs) -> V:
return func2(func1(*args, **kwargs))
return inner
@typing.final
@dataclasses.dataclass(slots=True, frozen=True)
class Composable(typing.Generic[P, U]):
"""
Make a function composable with another.
Implements the sugar `>>` operator to bind two functions together.
>>> @Composable
>>> def foo(x: str) -> float:
... # do stuff
>>> @Composable
>>> def bar(x: float) -> int:
... # do stuff
>>> foobar = foo >> bar
>>> baz: int = foobar("hello")
"""
func: collections.abc.Callable[P, U]
def __rshift__(self, other: collections.abc.Callable[[U], V]) -> Composable[P, V]:
return Composable(compose(self.func, other))
# We make sure it is still callable as is
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> U:
return self.func(*args, **kwargs)
@Composable
def partmap(
func: collections.abc.Callable[[T], U],
) -> Composable[[collections.abc.Iterable[T]], collections.abc.Iterable[U]]:
"""
Create a partial application of the mapped version of `func`.
>>> ords = partmap(ord)
>>> ords("hello")
[104, 101, 108, 108, 111]
"""
@Composable
@functools.wraps(func)
def mapped_func(
iterable: collections.abc.Iterable[T]
) -> collections.abc.Iterable[U]:
return map(func, iterable)
return mapped_func
@Composable
def withboth(
func: collections.abc.Callable[[*tuple[T, ...]], U],
first_values: collections.abc.Iterable[T],
second_values: collections.abc.Iterable[T],
) -> tuple[U, U]:
"""
Call `func` on the two sets of values.
"""
return (func(*first_values), func(*second_values))
@Composable
def withall(
func: collections.abc.Callable[[*tuple[T, ...]], U],
*value_sets: collections.abc.Iterable[T],
) -> tuple[U, ...]:
"""
Call `func` on all the sets of values.
"""
return tuple(func(*value_set) for value_set in value_sets)
@Composable
def fork(
func1: collections.abc.Callable[[*tuple[T, ...]], U],
func2: collections.abc.Callable[[*tuple[T, ...]], V],
values: collections.abc.Iterable[T],
) -> tuple[U, V]:
"""
Call two different functions on the same set of values.
"""
return func1(*values), func2(*values)
@Composable
def bracket(
func1: collections.abc.Callable[[*tuple[T, ...]], U],
func2: collections.abc.Callable[[*tuple[V, ...]], W],
first_values: collections.abc.Iterable[T],
second_values: collections.abc.Iterable[V],
) -> tuple[U, W]:
"""
Call two functions on two distinct sets of values.
"""
return func1(*first_values), func2(*second_values)
@Composable
def packcall(func: collections.abc.Callable[[*Ts], T], args: tuple[*Ts]) -> T:
"""
Call `func` with unpacked `args`.
>>> # equivalent to max(-5, 12, 8)
>>> packcall(max, (-5, 12, 8))
12
"""
return func(*args)
@typing.runtime_checkable
class SupportsAdd(typing.Protocol[T_contra, T_co]):
"""
Trait of implementing the operator `+`.
"""
@abc.abstractmethod
def __add__(self, other: T_contra, /) -> T_co:
pass
@typing.overload
def add() -> (
collections.abc.Callable[
[SupportsAdd[typing.Any, T_co]],
collections.abc.Callable[[SupportsAdd[typing.Any, U_co]], T_co | U_co],
]
):
pass
@typing.overload
def add(
left: SupportsAdd[typing.Any, T_co], /
) -> collections.abc.Callable[[SupportsAdd[typing.Any, U_co]], T_co | U_co]:
pass
@typing.overload
def add(
left: SupportsAdd[typing.Any, T_co],
right: SupportsAdd[typing.Any, U_co],
/,
) -> T_co | U_co:
pass
def add(
left: SupportsAdd[T_contra, T_co] | None = None,
right: SupportsAdd[U_contra, U_co] | None = None,
/,
) -> (
T_co
| U_co
| collections.abc.Callable[[SupportsAdd[U_contra, U_co]], T_co | U_co]
| collections.abc.Callable[
[SupportsAdd[T_contra, T_co]],
collections.abc.Callable[[SupportsAdd[U_contra, U_co]], T_co | U_co],
]
):
"""
Add two values together now. Or later. Supports automatically partial
application.
"""
@functools.wraps(add)
def inner(
left: SupportsAdd[T_contra, T_co]
) -> collections.abc.Callable[[SupportsAdd[U_contra, U_co]], T_co | U_co]:
@functools.wraps(add)
def inner_inner(right: SupportsAdd[U_contra, U_co]) -> T_co | U_co:
return add(left, right)
return inner_inner
if right is None:
if left is None:
return inner
return inner(left)
if left is None:
raise ValueError("left cannot be None if right is not None")
return left + right # type: ignore
print((partmap(ord) >> partmap(hex) >> " ".join)("Hello World!"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment