Skip to content

Commit

Permalink
Improve partials
Browse files Browse the repository at this point in the history
Resolves   #797.
  • Loading branch information
evhub committed Nov 4, 2023
1 parent a99a76d commit c3b0273
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 26 deletions.
8 changes: 7 additions & 1 deletion DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,8 @@ def new_f(x, *args, **kwargs):
return f(*args, **kwargs)
```

Unlike `functools.partial`, Coconut's partial application will preserve the `__name__` of the wrapped function.

##### Rationale

Partial application, or currying, is a mainstay of functional programming, and for good reason: it allows the dynamic customization of functions to fit the needs of where they are being used. Partial application allows a new function to be created out of an old function with some of its arguments pre-specified.
Expand Down Expand Up @@ -4262,7 +4264,11 @@ If _map_using_ is passed, calculates `key_func` and `value_func` by mapping them

##### **mapreduce.using_processes**(_key\_value\_func_, _iterable_, \*, _reduce\_func_=`None`, _collect\_in_=`None`, _ordered_=`False`, _chunksize_=`1`, _max\_workers_=`None`)

These shortcut methods call `collectby`/`mapreduce` with `map_using` set to [`process_map`](#process_map)/[`thread_map`](#thread_map), properly managed using the `.multiple_sequential_calls` method and the `stream=True` argument of [`process_map`](#process_map)/[`thread_map`](#thread_map). `reduce_func` will be called as soon as results arrive, and by default in whatever order they arrive in (to enforce the original order, pass _ordered_=`True`). Note that, for very long iterables, it is highly recommended to pass a value other than the default `1` for _chunksize_.
These shortcut methods call `collectby`/`mapreduce` with `map_using` set to [`process_map`](#process_map)/[`thread_map`](#thread_map), properly managed using the `.multiple_sequential_calls` method and the `stream=True` argument of [`process_map`](#process_map)/[`thread_map`](#thread_map). `reduce_func` will be called as soon as results arrive, and by default in whatever order they arrive in (to enforce the original order, pass _ordered_=`True`).

To make multiple sequential calls to `collectby.using_threads()`/`mapreduce.using_threads()`, manage them using `thread_map.multiple_sequential_calls()`. Similarly, use `process_map.multiple_sequential_calls()` to manage `.using_processes()`.

Note that, for very long iterables, it is highly recommended to pass a value other than the default `1` for _chunksize_.

As an example, `mapreduce.using_processes` is effectively equivalent to:
```coconut
Expand Down
5 changes: 4 additions & 1 deletion __coconut__/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ tee = _coconut.itertools.tee
starmap = _coconut.itertools.starmap
cartesian_product = _coconut.itertools.product

_coconut_partial = _coconut.functools.partial
_coconut_tee = tee
_coconut_starmap = starmap
_coconut_cartesian_product = cartesian_product
Expand Down Expand Up @@ -644,7 +645,8 @@ def _coconut_mark_as_match(func: _Tfunc) -> _Tfunc:
return func


class _coconut_partial(_t.Generic[_T]):
class _coconut_complex_partial(_t.Generic[_T]):
func: _t.Callable[..., _T] = ...
args: _Tuple = ...
required_nargs: int = ...
keywords: _t.Dict[_t.Text, _t.Any] = ...
Expand All @@ -658,6 +660,7 @@ class _coconut_partial(_t.Generic[_T]):
**kwargs: _t.Any,
) -> None: ...
def __call__(self, *args: _t.Any, **kwargs: _t.Any) -> _T: ...
__name__: str | None = ...


@_t.overload
Expand Down
2 changes: 1 addition & 1 deletion coconut/__coconut__.pyi
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from __coconut__ import *
from __coconut__ import _coconut_tail_call, _coconut_tco, _coconut_call_set_names, _coconut_handle_cls_kwargs, _coconut_handle_cls_stargs, _namedtuple_of, _coconut, _coconut_Expected, _coconut_MatchError, _coconut_SupportsAdd, _coconut_SupportsMinus, _coconut_SupportsMul, _coconut_SupportsPow, _coconut_SupportsTruediv, _coconut_SupportsFloordiv, _coconut_SupportsMod, _coconut_SupportsAnd, _coconut_SupportsXor, _coconut_SupportsOr, _coconut_SupportsLshift, _coconut_SupportsRshift, _coconut_SupportsMatmul, _coconut_SupportsInv, _coconut_Expected, _coconut_MatchError, _coconut_iter_getitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_star_pipe, _coconut_dubstar_pipe, _coconut_back_pipe, _coconut_back_star_pipe, _coconut_back_dubstar_pipe, _coconut_none_pipe, _coconut_none_star_pipe, _coconut_none_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_raise, _coconut_mark_as_match, _coconut_reiterable, _coconut_self_match_types, _coconut_dict_merge, _coconut_exec, _coconut_comma_op, _coconut_multi_dim_arr, _coconut_mk_anon_namedtuple, _coconut_matmul, _coconut_py_str, _coconut_flatten, _coconut_multiset, _coconut_back_none_pipe, _coconut_back_none_star_pipe, _coconut_back_none_dubstar_pipe, _coconut_forward_none_compose, _coconut_back_none_compose, _coconut_forward_none_star_compose, _coconut_back_none_star_compose, _coconut_forward_none_dubstar_compose, _coconut_back_none_dubstar_compose, _coconut_call_or_coefficient, _coconut_in, _coconut_not_in, _coconut_attritemgetter
from __coconut__ import _coconut_tail_call, _coconut_tco, _coconut_call_set_names, _coconut_handle_cls_kwargs, _coconut_handle_cls_stargs, _namedtuple_of, _coconut, _coconut_Expected, _coconut_MatchError, _coconut_SupportsAdd, _coconut_SupportsMinus, _coconut_SupportsMul, _coconut_SupportsPow, _coconut_SupportsTruediv, _coconut_SupportsFloordiv, _coconut_SupportsMod, _coconut_SupportsAnd, _coconut_SupportsXor, _coconut_SupportsOr, _coconut_SupportsLshift, _coconut_SupportsRshift, _coconut_SupportsMatmul, _coconut_SupportsInv, _coconut_Expected, _coconut_MatchError, _coconut_iter_getitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_star_pipe, _coconut_dubstar_pipe, _coconut_back_pipe, _coconut_back_star_pipe, _coconut_back_dubstar_pipe, _coconut_none_pipe, _coconut_none_star_pipe, _coconut_none_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_complex_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_raise, _coconut_mark_as_match, _coconut_reiterable, _coconut_self_match_types, _coconut_dict_merge, _coconut_exec, _coconut_comma_op, _coconut_multi_dim_arr, _coconut_mk_anon_namedtuple, _coconut_matmul, _coconut_py_str, _coconut_flatten, _coconut_multiset, _coconut_back_none_pipe, _coconut_back_none_star_pipe, _coconut_back_none_dubstar_pipe, _coconut_forward_none_compose, _coconut_back_none_compose, _coconut_forward_none_star_compose, _coconut_back_none_star_compose, _coconut_forward_none_dubstar_compose, _coconut_back_none_dubstar_compose, _coconut_call_or_coefficient, _coconut_in, _coconut_not_in, _coconut_attritemgetter
14 changes: 7 additions & 7 deletions coconut/compiler/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2797,7 +2797,7 @@ def pipe_handle(self, original, loc, tokens, **kwargs):
return expr
elif name == "partial":
self.internal_assert(len(split_item) == 3, original, loc)
return "_coconut.functools.partial(" + join_args(split_item) + ")"
return "_coconut_partial(" + join_args(split_item) + ")"
elif name == "attrgetter":
return attrgetter_atom_handle(loc, item)
elif name == "itemgetter":
Expand Down Expand Up @@ -2891,14 +2891,14 @@ def item_handle(self, original, loc, tokens):
out += trailer
elif len(trailer) == 1:
if trailer[0] == "$[]":
out = "_coconut.functools.partial(_coconut_iter_getitem, " + out + ")"
out = "_coconut_partial(_coconut_iter_getitem, " + out + ")"
elif trailer[0] == "$":
out = "_coconut.functools.partial(_coconut.functools.partial, " + out + ")"
out = "_coconut_partial(_coconut_partial, " + out + ")"
elif trailer[0] == "[]":
out = "_coconut.functools.partial(_coconut.operator.getitem, " + out + ")"
out = "_coconut_partial(_coconut.operator.getitem, " + out + ")"
elif trailer[0] == ".":
self.strict_err_or_warn("'obj.' as a shorthand for 'getattr$(obj)' is deprecated (just use the getattr partial)", original, loc)
out = "_coconut.functools.partial(_coconut.getattr, " + out + ")"
out = "_coconut_partial(_coconut.getattr, " + out + ")"
elif trailer[0] == "type:[]":
out = "_coconut.typing.Sequence[" + out + "]"
elif trailer[0] == "type:$[]":
Expand Down Expand Up @@ -2931,7 +2931,7 @@ def item_handle(self, original, loc, tokens):
args = trailer[1][1:-1]
if not args:
raise CoconutDeferredSyntaxError("a partial application argument is required", loc)
out = "_coconut.functools.partial(" + out + ", " + args + ")"
out = "_coconut_partial(" + out + ", " + args + ")"
elif trailer[0] == "$[":
out = "_coconut_iter_getitem(" + out + ", " + trailer[1] + ")"
elif trailer[0] == "$(?":
Expand Down Expand Up @@ -2959,7 +2959,7 @@ def item_handle(self, original, loc, tokens):
raise CoconutInternalException("no question mark in question mark partial", trailer[1])
elif argdict_pairs or pos_kwargs or extra_args_str:
out = (
"_coconut_partial("
"_coconut_complex_partial("
+ out
+ ", {" + ", ".join(argdict_pairs) + "}"
+ ", " + str(len(pos_args))
Expand Down
8 changes: 4 additions & 4 deletions coconut/compiler/grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ def itemgetter_handle(tokens):
if op == "[":
return "_coconut.operator.itemgetter((" + args + "))"
elif op == "$[":
return "_coconut.functools.partial(_coconut_iter_getitem, index=(" + args + "))"
return "_coconut_partial(_coconut_iter_getitem, index=(" + args + "))"
else:
raise CoconutInternalException("invalid implicit itemgetter type", op)
else:
Expand Down Expand Up @@ -540,10 +540,10 @@ def partial_op_item_handle(tokens):
tok_grp, = tokens
if "left partial" in tok_grp:
arg, op = tok_grp
return "_coconut.functools.partial(" + op + ", " + arg + ")"
return "_coconut_partial(" + op + ", " + arg + ")"
elif "right partial" in tok_grp:
op, arg = tok_grp
return "_coconut_partial(" + op + ", {1: " + arg + "}, 2, ())"
return "_coconut_complex_partial(" + op + ", {1: " + arg + "}, 2, ())"
else:
raise CoconutInternalException("invalid operator function implicit partial token group", tok_grp)

Expand Down Expand Up @@ -1013,7 +1013,7 @@ class Grammar(object):
| fixto(dubquestion, "_coconut_none_coalesce")
| fixto(dot, "_coconut.getattr")
| fixto(unsafe_dubcolon, "_coconut.itertools.chain")
| fixto(dollar, "_coconut.functools.partial")
| fixto(dollar, "_coconut_partial")
| fixto(exp_dubstar, "_coconut.operator.pow")
| fixto(mul_star, "_coconut.operator.mul")
| fixto(div_dubslash, "_coconut.operator.floordiv")
Expand Down
4 changes: 2 additions & 2 deletions coconut/compiler/header.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ def pattern_prepender(func):
return pattern_prepender
def datamaker(data_type):
"""DEPRECATED: use makedata instead."""
return _coconut.functools.partial(makedata, data_type)
return _coconut_partial(makedata, data_type)
of, parallel_map, concurrent_map, recursive_iterator = call, process_map, thread_map, recursive_generator
'''
if not strict else
Expand Down Expand Up @@ -599,7 +599,7 @@ async def __anext__(self):
# (extra_format_dict is to keep indentation levels matching)
extra_format_dict = dict(
# when anything is added to this list it must also be added to *both* __coconut__ stub files
underscore_imports="{tco_comma}{call_set_names_comma}{handle_cls_args_comma}_namedtuple_of, _coconut, _coconut_Expected, _coconut_MatchError, _coconut_SupportsAdd, _coconut_SupportsMinus, _coconut_SupportsMul, _coconut_SupportsPow, _coconut_SupportsTruediv, _coconut_SupportsFloordiv, _coconut_SupportsMod, _coconut_SupportsAnd, _coconut_SupportsXor, _coconut_SupportsOr, _coconut_SupportsLshift, _coconut_SupportsRshift, _coconut_SupportsMatmul, _coconut_SupportsInv, _coconut_iter_getitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_star_pipe, _coconut_dubstar_pipe, _coconut_back_pipe, _coconut_back_star_pipe, _coconut_back_dubstar_pipe, _coconut_none_pipe, _coconut_none_star_pipe, _coconut_none_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_raise, _coconut_mark_as_match, _coconut_reiterable, _coconut_self_match_types, _coconut_dict_merge, _coconut_exec, _coconut_comma_op, _coconut_multi_dim_arr, _coconut_mk_anon_namedtuple, _coconut_matmul, _coconut_py_str, _coconut_flatten, _coconut_multiset, _coconut_back_none_pipe, _coconut_back_none_star_pipe, _coconut_back_none_dubstar_pipe, _coconut_forward_none_compose, _coconut_back_none_compose, _coconut_forward_none_star_compose, _coconut_back_none_star_compose, _coconut_forward_none_dubstar_compose, _coconut_back_none_dubstar_compose, _coconut_call_or_coefficient, _coconut_in, _coconut_not_in, _coconut_attritemgetter".format(**format_dict),
underscore_imports="{tco_comma}{call_set_names_comma}{handle_cls_args_comma}_namedtuple_of, _coconut, _coconut_Expected, _coconut_MatchError, _coconut_SupportsAdd, _coconut_SupportsMinus, _coconut_SupportsMul, _coconut_SupportsPow, _coconut_SupportsTruediv, _coconut_SupportsFloordiv, _coconut_SupportsMod, _coconut_SupportsAnd, _coconut_SupportsXor, _coconut_SupportsOr, _coconut_SupportsLshift, _coconut_SupportsRshift, _coconut_SupportsMatmul, _coconut_SupportsInv, _coconut_iter_getitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_star_pipe, _coconut_dubstar_pipe, _coconut_back_pipe, _coconut_back_star_pipe, _coconut_back_dubstar_pipe, _coconut_none_pipe, _coconut_none_star_pipe, _coconut_none_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_complex_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_raise, _coconut_mark_as_match, _coconut_reiterable, _coconut_self_match_types, _coconut_dict_merge, _coconut_exec, _coconut_comma_op, _coconut_multi_dim_arr, _coconut_mk_anon_namedtuple, _coconut_matmul, _coconut_py_str, _coconut_flatten, _coconut_multiset, _coconut_back_none_pipe, _coconut_back_none_star_pipe, _coconut_back_none_dubstar_pipe, _coconut_forward_none_compose, _coconut_back_none_compose, _coconut_forward_none_star_compose, _coconut_back_none_star_compose, _coconut_forward_none_dubstar_compose, _coconut_back_none_dubstar_compose, _coconut_call_or_coefficient, _coconut_in, _coconut_not_in, _coconut_attritemgetter".format(**format_dict),
import_typing=pycondition(
(3, 5),
if_ge='''
Expand Down
Loading

0 comments on commit c3b0273

Please sign in to comment.