coconut-develop 3.0.4.post0.dev7__tar.gz → 3.0.4.post0.dev8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/DOCS.md +55 -4
  2. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/PKG-INFO +2 -2
  3. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/__coconut__/__init__.pyi +25 -3
  4. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/command/command.py +0 -1
  5. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/compiler/grammar.py +7 -3
  6. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/compiler/header.py +5 -4
  7. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/compiler/templates/header.py_template +66 -45
  8. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/constants.py +2 -1
  9. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/root.py +1 -1
  10. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/terminal.py +3 -1
  11. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/agnostic/suite.coco +3 -1
  12. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/agnostic/util.coco +18 -0
  13. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/extras.coco +3 -0
  14. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/CONTRIBUTING.md +0 -0
  15. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/FAQ.md +0 -0
  16. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/HELP.md +0 -0
  17. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/LICENSE.txt +0 -0
  18. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/MANIFEST.in +0 -0
  19. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/README.rst +0 -0
  20. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/__coconut__/__init__.py +0 -0
  21. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/__coconut__/py.typed +0 -0
  22. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/_coconut/__init__.py +0 -0
  23. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/_coconut/__init__.pyi +0 -0
  24. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/_coconut/py.typed +0 -0
  25. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/__coconut__.py +0 -0
  26. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/__coconut__.pyi +0 -0
  27. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/__init__.py +0 -0
  28. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/__init__.pyi +0 -0
  29. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/__main__.py +0 -0
  30. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/_pyparsing.py +0 -0
  31. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/api.py +0 -0
  32. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/api.pyi +0 -0
  33. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/command/__init__.py +0 -0
  34. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/command/__init__.pyi +0 -0
  35. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/command/cli.py +0 -0
  36. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/command/command.pyi +0 -0
  37. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/command/mypy.py +0 -0
  38. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/command/resources/zcoconut.pth +0 -0
  39. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/command/util.py +0 -0
  40. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/command/watch.py +0 -0
  41. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/compiler/__init__.py +0 -0
  42. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/compiler/compiler.py +0 -0
  43. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/compiler/matching.py +0 -0
  44. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/compiler/util.py +0 -0
  45. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/convenience.py +0 -0
  46. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/convenience.pyi +0 -0
  47. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/exceptions.py +0 -0
  48. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/highlighter.py +0 -0
  49. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/icoconut/__init__.py +0 -0
  50. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/icoconut/__main__.py +0 -0
  51. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/icoconut/coconut/kernel.json +0 -0
  52. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/icoconut/coconut_py/kernel.json +0 -0
  53. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/icoconut/coconut_py2/kernel.json +0 -0
  54. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/icoconut/coconut_py3/kernel.json +0 -0
  55. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/icoconut/embed.py +0 -0
  56. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/icoconut/root.py +0 -0
  57. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/integrations.py +0 -0
  58. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/main.py +0 -0
  59. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/py.typed +0 -0
  60. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/requirements.py +0 -0
  61. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/__init__.py +0 -0
  62. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/__main__.py +0 -0
  63. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/constants_test.py +0 -0
  64. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/main_test.py +0 -0
  65. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/agnostic/__init__.coco +0 -0
  66. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/agnostic/__main__.coco +0 -0
  67. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/agnostic/main.coco +0 -0
  68. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/agnostic/primary_1.coco +0 -0
  69. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/agnostic/primary_2.coco +0 -0
  70. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/agnostic/specific.coco +0 -0
  71. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/agnostic/tutorial.coco +0 -0
  72. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/non_strict/non_strict_test.coco +0 -0
  73. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/target_2/py2_test.coco +0 -0
  74. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/target_3/py3_test.coco +0 -0
  75. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/target_311/py311_test.coco +0 -0
  76. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/target_35/py35_test.coco +0 -0
  77. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/target_36/py36_test.coco +0 -0
  78. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/target_38/py38_test.coco +0 -0
  79. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/cocotest/target_sys/target_sys_test.coco +0 -0
  80. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/importable.coco +0 -0
  81. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/runnable.coco +0 -0
  82. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/tests/src/runner.coco +0 -0
  83. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut/util.py +0 -0
  84. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/coconut_develop.egg-info/SOURCES.txt +0 -0
  85. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/conf.py +0 -0
  86. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/pyproject.toml +0 -0
  87. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/setup.cfg +0 -0
  88. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/setup.py +0 -0
  89. {coconut-develop-3.0.4.post0.dev7 → coconut-develop-3.0.4.post0.dev8}/xontrib/coconut.py +0 -0
@@ -3505,11 +3505,11 @@ def flip(f, nargs=None) =
3505
3505
  )
3506
3506
  ```
3507
3507
 
3508
- #### `lift`
3508
+ #### `lift` and `lift_apart`
3509
3509
 
3510
- **lift**(_func_)
3510
+ ##### **lift**(_func_)
3511
3511
 
3512
- **lift**(_func_, *_func\_args_, **_func\_kwargs_)
3512
+ ##### **lift**(_func_, *_func\_args_, **_func\_kwargs_)
3513
3513
 
3514
3514
  Coconut's `lift` built-in is a higher-order function that takes in a function and “lifts” it up so that all of its arguments are functions.
3515
3515
 
@@ -3533,7 +3533,33 @@ def lift(f) = (
3533
3533
 
3534
3534
  `lift` also supports a shortcut form such that `lift(f, *func_args, **func_kwargs)` is equivalent to `lift(f)(*func_args, **func_kwargs)`.
3535
3535
 
3536
- ##### Example
3536
+ ##### **lift\_apart**(_func_)
3537
+
3538
+ ##### **lift\_apart**(_func_, *_func\_args_, **_func\_kwargs_)
3539
+
3540
+ Coconut's `lift_apart` built-in is very similar to `lift`, except instead of duplicating the final arguments to each function, it separates them out.
3541
+
3542
+ For a binary function `f(x, y)` and two unary functions `g(z)` and `h(z)`, `lift_apart` works as
3543
+ ```coconut
3544
+ lift_apart(f)(g, h)(z, w) == f(g(z), h(w))
3545
+ ```
3546
+ such that in this case `lift_apart` implements the `D2` combinator.
3547
+
3548
+ In the general case, `lift_apart` is equivalent to a pickleable version of
3549
+ ```coconut
3550
+ def lift_apart(f) = (
3551
+ (*func_args, **func_kwargs) =>
3552
+ (*args, **kwargs) =>
3553
+ f(
3554
+ *(f(x) for f, x in zip(func_args, args, strict=True)),
3555
+ **{k: func_kwargs[k](kwargs[k]) for k in func_kwargs.keys() | kwargs.keys()},
3556
+ )
3557
+ )
3558
+ ```
3559
+
3560
+ `lift_apart` supports the same shortcut form as `lift`.
3561
+
3562
+ ##### Examples
3537
3563
 
3538
3564
  **Coconut:**
3539
3565
  ```coconut
@@ -3552,8 +3578,33 @@ def plus_and_times(x, y):
3552
3578
  return x + y, x * y
3553
3579
  ```
3554
3580
 
3581
+ **Coconut:**
3582
+ ```coconut
3583
+ first_false_and_last_true = (
3584
+ lift(,)(ident, reversed)
3585
+ ..*> lift_apart(,)(dropwhile$(bool), dropwhile$(not))
3586
+ ..*> lift_apart(,)(.$[0], .$[0])
3587
+ )
3588
+ ```
3589
+
3590
+ **Python:**
3591
+ ```coconut_python
3592
+ from itertools import dropwhile
3593
+
3594
+ def first_false_and_last_true(xs):
3595
+ rev_xs = reversed(xs)
3596
+ return (
3597
+ next(dropwhile(bool, xs)),
3598
+ next(dropwhile(lambda x: not x, rev_xs)),
3599
+ )
3600
+ ```
3601
+
3555
3602
  #### `and_then` and `and_then_await`
3556
3603
 
3604
+ **and\_then**(_first\_async\_func_, _second\_func_)
3605
+
3606
+ **and\_then\_await**(_first\_async\_func_, _second\_async\_func_)
3607
+
3557
3608
  Coconut provides the `and_then` and `and_then_await` built-ins for composing `async` functions. Specifically:
3558
3609
  * To forwards compose an async function `async_f` with a normal function `g` (such that `g` is called on the result of `await`ing `async_f`), write ``async_f `and_then` g``.
3559
3610
  * To forwards compose an async function `async_f` with another async function `async_g` (such that `async_g` is called on the result of `await`ing `async_f`, and then `async_g` is itself awaited), write ``async_f `and_then_await` async_g``.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: coconut-develop
3
- Version: 3.0.4.post0.dev7
3
+ Version: 3.0.4.post0.dev8
4
4
  Summary: Simple, elegant, Pythonic functional programming.
5
5
  Home-page: http://coconut-lang.org
6
6
  Author: Evan Hubinger
@@ -89,7 +89,7 @@ Description: |logo| Coconut
89
89
  __ Sponsor_
90
90
  .. _Sponsor: https://opencollective.com/coconut#sponsor
91
91
 
92
- Keywords: functional,programming,language,compiler,pattern,pattern-matching,algebraic,data type,data types,lambda,lambdas,lazy,evaluation,lazy list,lazy lists,tail,recursion,call,recursive,recursive_iterator,infix,function,composition,compose,partial,application,currying,curry,pipeline,pipe,unicode,operator,operators,frozenset,literal,syntax,destructuring,assignment,fold,datamaker,prepattern,iterator,generator,none,coalesce,coalescing,statement,lru_cache,memoization,backport,typing,embed,PEP 622,overrides,islice,itertools,functools,TYPE_CHECKING,Expected,breakpoint,help,reduce,takewhile,dropwhile,tee,count,makedata,consume,process_map,thread_map,addpattern,recursive_generator,fmap,starmap,reiterable,scan,groupsof,memoize,zip_longest,override,flatten,ident,call,safe_call,flip,const,lift,all_equal,collectby,mapreduce,multi_enumerate,cartesian_product,multiset,cycle,windowsof,and_then,and_then_await,async_map,py_chr,py_dict,py_hex,py_input,py_int,py_map,py_object,py_oct,py_open,py_print,py_range,py_str,py_super,py_zip,py_filter,py_reversed,py_enumerate,py_raw_input,py_xrange,py_repr,py_breakpoint,_namedtuple_of,reveal_type,reveal_locals,MatchError,ExceptionGroup,BaseExceptionGroup,__fmap__,__iter_getitem__,data,match,case,cases,where,addpattern,then,operator,type,copyclosure,λ
92
+ Keywords: functional,programming,language,compiler,pattern,pattern-matching,algebraic,data type,data types,lambda,lambdas,lazy,evaluation,lazy list,lazy lists,tail,recursion,call,recursive,recursive_iterator,infix,function,composition,compose,partial,application,currying,curry,pipeline,pipe,unicode,operator,operators,frozenset,literal,syntax,destructuring,assignment,fold,datamaker,prepattern,iterator,generator,none,coalesce,coalescing,statement,lru_cache,memoization,backport,typing,embed,PEP 622,overrides,islice,itertools,functools,TYPE_CHECKING,Expected,breakpoint,help,reduce,takewhile,dropwhile,tee,count,makedata,consume,process_map,thread_map,addpattern,recursive_generator,fmap,starmap,reiterable,scan,groupsof,memoize,zip_longest,override,flatten,ident,call,safe_call,flip,const,lift,lift_apart,all_equal,collectby,mapreduce,multi_enumerate,cartesian_product,multiset,cycle,windowsof,and_then,and_then_await,async_map,py_chr,py_dict,py_hex,py_input,py_int,py_map,py_object,py_oct,py_open,py_print,py_range,py_str,py_super,py_zip,py_filter,py_reversed,py_enumerate,py_raw_input,py_xrange,py_repr,py_breakpoint,_namedtuple_of,reveal_type,reveal_locals,MatchError,ExceptionGroup,BaseExceptionGroup,__fmap__,__iter_getitem__,data,match,case,cases,where,addpattern,then,operator,type,copyclosure,λ
93
93
  Platform: UNKNOWN
94
94
  Classifier: Development Status :: 5 - Production/Stable
95
95
  Classifier: License :: OSI Approved :: Apache Software License
@@ -1636,20 +1636,42 @@ def lift(func: _t.Callable[[_T, _U], _W]) -> _coconut_lifted_2[_T, _U, _W]: ...
1636
1636
  def lift(func: _t.Callable[[_T, _U, _V], _W]) -> _coconut_lifted_3[_T, _U, _V, _W]: ...
1637
1637
  @_t.overload
1638
1638
  def lift(func: _t.Callable[..., _W]) -> _t.Callable[..., _t.Callable[..., _W]]:
1639
- """Lift a function up so that all of its arguments are functions.
1639
+ """Lift a function up so that all of its arguments are functions that all take the same arguments.
1640
1640
 
1641
1641
  For a binary function f(x, y) and two unary functions g(z) and h(z), lift works as the S' combinator:
1642
1642
  lift(f)(g, h)(z) == f(g(z), h(z))
1643
1643
 
1644
1644
  In general, lift is equivalent to:
1645
- def lift(f) = ((*func_args, **func_kwargs) -> (*args, **kwargs) ->
1646
- f(*(g(*args, **kwargs) for g in func_args), **{lbrace}k: h(*args, **kwargs) for k, h in func_kwargs.items(){rbrace}))
1645
+ def lift(f) = ((*func_args, **func_kwargs) => (*args, **kwargs) => (
1646
+ f(*(g(*args, **kwargs) for g in func_args), **{k: h(*args, **kwargs) for k, h in func_kwargs.items()}))
1647
+ )
1647
1648
 
1648
1649
  lift also supports a shortcut form such that lift(f, *func_args, **func_kwargs) is equivalent to lift(f)(*func_args, **func_kwargs).
1649
1650
  """
1650
1651
  ...
1651
1652
  _coconut_lift = lift
1652
1653
 
1654
+ @_t.overload
1655
+ def lift_apart(func: _t.Callable[[_T], _W]) -> _t.Callable[[_t.Callable[[_U], _T]], _t.Callable[[_U], _W]]: ...
1656
+ @_t.overload
1657
+ def lift_apart(func: _t.Callable[[_T, _X], _W]) -> _t.Callable[[_t.Callable[[_U], _T], _t.Callable[[_Y], _X]], _t.Callable[[_U, _Y], _W]]: ...
1658
+ @_t.overload
1659
+ def lift_apart(func: _t.Callable[..., _W]) -> _t.Callable[..., _t.Callable[..., _W]]:
1660
+ """Lift a function up so that all of its arguments are functions that each take separate arguments.
1661
+
1662
+ For a binary function f(x, y) and two unary functions g(z) and h(z), lift_apart works as the D2 combinator:
1663
+ lift_apart(f)(g, h)(z, w) == f(g(z), h(w))
1664
+
1665
+ In general, lift_apart is equivalent to:
1666
+ def lift_apart(func) = (*func_args, **func_kwargs) => (*args, **kwargs) => func(
1667
+ *map(call, func_args, args, strict=True),
1668
+ **{k: func_kwargs[k](kwargs[k]) for k in func_kwargs.keys() | kwargs.keys()},
1669
+ )
1670
+
1671
+ lift_apart also supports a shortcut form such that lift_apart(f, *func_args, **func_kwargs) is equivalent to lift_apart(f)(*func_args, **func_kwargs).
1672
+ """
1673
+ ...
1674
+
1653
1675
 
1654
1676
  def all_equal(iterable: _Iterable) -> bool:
1655
1677
  """For a given iterable, check whether all elements in that iterable are equal to each other.
@@ -246,7 +246,6 @@ class Command(object):
246
246
  unset_fast_pyparsing_reprs()
247
247
  if args.profile:
248
248
  start_profiling()
249
- logger.enable_colors()
250
249
 
251
250
  logger.log(cli_version)
252
251
  if original_args is not None:
@@ -1381,9 +1381,13 @@ class Grammar(object):
1381
1381
  simple_assign = Forward()
1382
1382
  simple_assign_ref = maybeparens(
1383
1383
  lparen,
1384
- (setname | passthrough_atom)
1385
- + ZeroOrMore(ZeroOrMore(complex_trailer) + OneOrMore(simple_trailer)),
1386
- rparen,
1384
+ (
1385
+ # refname if there's a trailer, setname if not
1386
+ (refname | passthrough_atom) + OneOrMore(ZeroOrMore(complex_trailer) + OneOrMore(simple_trailer))
1387
+ | setname
1388
+ | passthrough_atom
1389
+ ),
1390
+ rparen
1387
1391
  )
1388
1392
  simple_assignlist = maybeparens(lparen, itemlist(simple_assign, comma, suppress_trailing=False), rparen)
1389
1393
 
@@ -290,11 +290,15 @@ def process_header_args(which, use_hash, target, no_tco, strict, no_wrap):
290
290
  report_this_text=report_this_text,
291
291
  from_None=" from None" if target.startswith("3") else "",
292
292
  process_="process_" if target_info >= (3, 13) else "",
293
-
294
293
  numpy_modules=tuple_str_of(numpy_modules, add_quotes=True),
295
294
  pandas_numpy_modules=tuple_str_of(pandas_numpy_modules, add_quotes=True),
296
295
  jax_numpy_modules=tuple_str_of(jax_numpy_modules, add_quotes=True),
297
296
  self_match_types=tuple_str_of(self_match_types),
297
+ comma_bytearray=", bytearray" if not target.startswith("3") else "",
298
+ lstatic="staticmethod(" if not target.startswith("3") else "",
299
+ rstatic=")" if not target.startswith("3") else "",
300
+ all_keys="self.func_kwargs.keys() | kwargs.keys()" if target_info >= (3,) else "_coconut.set(self.func_kwargs.keys()) | _coconut.set(kwargs.keys())",
301
+
298
302
  set_super=(
299
303
  # we have to use _coconut_super even on the universal target, since once we set __class__ it becomes a local variable
300
304
  "super = py_super" if target.startswith("3") else "super = _coconut_super"
@@ -335,9 +339,6 @@ zip_longest = itertools.zip_longest if _coconut_sys.version_info >= (3,) else it
335
339
  else "zip_longest = itertools.izip_longest",
336
340
  indent=1,
337
341
  ),
338
- comma_bytearray=", bytearray" if not target.startswith("3") else "",
339
- lstatic="staticmethod(" if not target.startswith("3") else "",
340
- rstatic=")" if not target.startswith("3") else "",
341
342
  zip_iter=prepare(
342
343
  r'''
343
344
  for items in _coconut.iter(_coconut.zip(*self.iters, strict=self.strict) if _coconut_sys.version_info >= (3, 10) else _coconut.zip_longest(*self.iters, fillvalue=_coconut_sentinel) if self.strict else _coconut.zip(*self.iters)):
@@ -432,7 +432,7 @@ def and_then(first_async_func, second_func):
432
432
  first_async_func: async (**T) -> U,
433
433
  second_func: U -> V,
434
434
  ) -> async (**T) -> V =
435
- async def (*args, **kwargs) -> (
435
+ async def (*args, **kwargs) => (
436
436
  first_async_func(*args, **kwargs)
437
437
  |> await
438
438
  |> second_func
@@ -447,7 +447,7 @@ def and_then_await(first_async_func, second_async_func):
447
447
  first_async_func: async (**T) -> U,
448
448
  second_async_func: async U -> V,
449
449
  ) -> async (**T) -> V =
450
- async def (*args, **kwargs) -> (
450
+ async def (*args, **kwargs) => (
451
451
  first_async_func(*args, **kwargs)
452
452
  |> await
453
453
  |> second_async_func
@@ -458,98 +458,98 @@ def and_then_await(first_async_func, second_async_func):
458
458
  def _coconut_forward_compose(func, *funcs):
459
459
  """Forward composition operator (..>).
460
460
 
461
- (..>)(f, g) is effectively equivalent to (*args, **kwargs) -> g(f(*args, **kwargs))."""
461
+ (..>)(f, g) is effectively equivalent to (*args, **kwargs) => g(f(*args, **kwargs))."""
462
462
  return _coconut_base_compose(func, *((f, 0, False) for f in funcs))
463
463
  def _coconut_back_compose(*funcs):
464
464
  """Backward composition operator (<..).
465
465
 
466
- (<..)(f, g) is effectively equivalent to (*args, **kwargs) -> f(g(*args, **kwargs))."""
466
+ (<..)(f, g) is effectively equivalent to (*args, **kwargs) => f(g(*args, **kwargs))."""
467
467
  return _coconut_forward_compose(*_coconut.reversed(funcs))
468
468
  def _coconut_forward_none_compose(func, *funcs):
469
469
  """Forward none-aware composition operator (..?>).
470
470
 
471
- (..?>)(f, g) is effectively equivalent to (*args, **kwargs) -> g?(f(*args, **kwargs))."""
471
+ (..?>)(f, g) is effectively equivalent to (*args, **kwargs) => g?(f(*args, **kwargs))."""
472
472
  return _coconut_base_compose(func, *((f, 0, True) for f in funcs))
473
473
  def _coconut_back_none_compose(*funcs):
474
474
  """Backward none-aware composition operator (<..?).
475
475
 
476
- (<..?)(f, g) is effectively equivalent to (*args, **kwargs) -> f?(g(*args, **kwargs))."""
476
+ (<..?)(f, g) is effectively equivalent to (*args, **kwargs) => f?(g(*args, **kwargs))."""
477
477
  return _coconut_forward_none_compose(*_coconut.reversed(funcs))
478
478
  def _coconut_forward_star_compose(func, *funcs):
479
479
  """Forward star composition operator (..*>).
480
480
 
481
- (..*>)(f, g) is effectively equivalent to (*args, **kwargs) -> g(*f(*args, **kwargs))."""
481
+ (..*>)(f, g) is effectively equivalent to (*args, **kwargs) => g(*f(*args, **kwargs))."""
482
482
  return _coconut_base_compose(func, *((f, 1, False) for f in funcs))
483
483
  def _coconut_back_star_compose(*funcs):
484
484
  """Backward star composition operator (<*..).
485
485
 
486
- (<*..)(f, g) is effectively equivalent to (*args, **kwargs) -> f(*g(*args, **kwargs))."""
486
+ (<*..)(f, g) is effectively equivalent to (*args, **kwargs) => f(*g(*args, **kwargs))."""
487
487
  return _coconut_forward_star_compose(*_coconut.reversed(funcs))
488
488
  def _coconut_forward_none_star_compose(func, *funcs):
489
489
  """Forward none-aware star composition operator (..?*>).
490
490
 
491
- (..?*>)(f, g) is effectively equivalent to (*args, **kwargs) -> g?(*f(*args, **kwargs))."""
491
+ (..?*>)(f, g) is effectively equivalent to (*args, **kwargs) => g?(*f(*args, **kwargs))."""
492
492
  return _coconut_base_compose(func, *((f, 1, True) for f in funcs))
493
493
  def _coconut_back_none_star_compose(*funcs):
494
494
  """Backward none-aware star composition operator (<*?..).
495
495
 
496
- (<*?..)(f, g) is effectively equivalent to (*args, **kwargs) -> f?(*g(*args, **kwargs))."""
496
+ (<*?..)(f, g) is effectively equivalent to (*args, **kwargs) => f?(*g(*args, **kwargs))."""
497
497
  return _coconut_forward_none_star_compose(*_coconut.reversed(funcs))
498
498
  def _coconut_forward_dubstar_compose(func, *funcs):
499
499
  """Forward double star composition operator (..**>).
500
500
 
501
- (..**>)(f, g) is effectively equivalent to (*args, **kwargs) -> g(**f(*args, **kwargs))."""
501
+ (..**>)(f, g) is effectively equivalent to (*args, **kwargs) => g(**f(*args, **kwargs))."""
502
502
  return _coconut_base_compose(func, *((f, 2, False) for f in funcs))
503
503
  def _coconut_back_dubstar_compose(*funcs):
504
504
  """Backward double star composition operator (<**..).
505
505
 
506
- (<**..)(f, g) is effectively equivalent to (*args, **kwargs) -> f(**g(*args, **kwargs))."""
506
+ (<**..)(f, g) is effectively equivalent to (*args, **kwargs) => f(**g(*args, **kwargs))."""
507
507
  return _coconut_forward_dubstar_compose(*_coconut.reversed(funcs))
508
508
  def _coconut_forward_none_dubstar_compose(func, *funcs):
509
509
  """Forward none-aware double star composition operator (..?**>).
510
510
 
511
- (..?**>)(f, g) is effectively equivalent to (*args, **kwargs) -> g?(**f(*args, **kwargs))."""
511
+ (..?**>)(f, g) is effectively equivalent to (*args, **kwargs) => g?(**f(*args, **kwargs))."""
512
512
  return _coconut_base_compose(func, *((f, 2, True) for f in funcs))
513
513
  def _coconut_back_none_dubstar_compose(*funcs):
514
514
  """Backward none-aware double star composition operator (<**?..).
515
515
 
516
- (<**?..)(f, g) is effectively equivalent to (*args, **kwargs) -> f?(**g(*args, **kwargs))."""
516
+ (<**?..)(f, g) is effectively equivalent to (*args, **kwargs) => f?(**g(*args, **kwargs))."""
517
517
  return _coconut_forward_none_dubstar_compose(*_coconut.reversed(funcs))
518
518
  def _coconut_pipe(x, f):
519
- """Pipe operator (|>). Equivalent to (x, f) -> f(x)."""
519
+ """Pipe operator (|>). Equivalent to (x, f) => f(x)."""
520
520
  return f(x)
521
521
  def _coconut_star_pipe(xs, f):
522
- """Star pipe operator (*|>). Equivalent to (xs, f) -> f(*xs)."""
522
+ """Star pipe operator (*|>). Equivalent to (xs, f) => f(*xs)."""
523
523
  return f(*xs)
524
524
  def _coconut_dubstar_pipe(kws, f):
525
- """Double star pipe operator (**|>). Equivalent to (kws, f) -> f(**kws)."""
525
+ """Double star pipe operator (**|>). Equivalent to (kws, f) => f(**kws)."""
526
526
  return f(**kws)
527
527
  def _coconut_back_pipe(f, x):
528
- """Backward pipe operator (<|). Equivalent to (f, x) -> f(x)."""
528
+ """Backward pipe operator (<|). Equivalent to (f, x) => f(x)."""
529
529
  return f(x)
530
530
  def _coconut_back_star_pipe(f, xs):
531
- """Backward star pipe operator (<*|). Equivalent to (f, xs) -> f(*xs)."""
531
+ """Backward star pipe operator (<*|). Equivalent to (f, xs) => f(*xs)."""
532
532
  return f(*xs)
533
533
  def _coconut_back_dubstar_pipe(f, kws):
534
- """Backward double star pipe operator (<**|). Equivalent to (f, kws) -> f(**kws)."""
534
+ """Backward double star pipe operator (<**|). Equivalent to (f, kws) => f(**kws)."""
535
535
  return f(**kws)
536
536
  def _coconut_none_pipe(x, f):
537
- """Nullable pipe operator (|?>). Equivalent to (x, f) -> f(x) if x is not None else None."""
537
+ """Nullable pipe operator (|?>). Equivalent to (x, f) => f(x) if x is not None else None."""
538
538
  return None if x is None else f(x)
539
539
  def _coconut_none_star_pipe(xs, f):
540
- """Nullable star pipe operator (|?*>). Equivalent to (xs, f) -> f(*xs) if xs is not None else None."""
540
+ """Nullable star pipe operator (|?*>). Equivalent to (xs, f) => f(*xs) if xs is not None else None."""
541
541
  return None if xs is None else f(*xs)
542
542
  def _coconut_none_dubstar_pipe(kws, f):
543
- """Nullable double star pipe operator (|?**>). Equivalent to (kws, f) -> f(**kws) if kws is not None else None."""
543
+ """Nullable double star pipe operator (|?**>). Equivalent to (kws, f) => f(**kws) if kws is not None else None."""
544
544
  return None if kws is None else f(**kws)
545
545
  def _coconut_back_none_pipe(f, x):
546
- """Nullable backward pipe operator (<?|). Equivalent to (f, x) -> f(x) if x is not None else None."""
546
+ """Nullable backward pipe operator (<?|). Equivalent to (f, x) => f(x) if x is not None else None."""
547
547
  return None if x is None else f(x)
548
548
  def _coconut_back_none_star_pipe(f, xs):
549
- """Nullable backward star pipe operator (<*?|). Equivalent to (f, xs) -> f(*xs) if xs is not None else None."""
549
+ """Nullable backward star pipe operator (<*?|). Equivalent to (f, xs) => f(*xs) if xs is not None else None."""
550
550
  return None if xs is None else f(*xs)
551
551
  def _coconut_back_none_dubstar_pipe(f, kws):
552
- """Nullable backward double star pipe operator (<**?|). Equivalent to (kws, f) -> f(**kws) if kws is not None else None."""
552
+ """Nullable backward double star pipe operator (<**?|). Equivalent to (kws, f) => f(**kws) if kws is not None else None."""
553
553
  return None if kws is None else f(**kws)
554
554
  def _coconut_assert(cond, msg=None):
555
555
  """Assert operator (assert). Asserts condition with optional message."""
@@ -563,27 +563,27 @@ def _coconut_raise(exc=None, from_exc=None):
563
563
  exc.__cause__ = from_exc
564
564
  raise exc
565
565
  def _coconut_bool_and(a, b):
566
- """Boolean and operator (and). Equivalent to (a, b) -> a and b."""
566
+ """Boolean and operator (and). Equivalent to (a, b) => a and b."""
567
567
  return a and b
568
568
  def _coconut_bool_or(a, b):
569
- """Boolean or operator (or). Equivalent to (a, b) -> a or b."""
569
+ """Boolean or operator (or). Equivalent to (a, b) => a or b."""
570
570
  return a or b
571
571
  def _coconut_in(a, b):
572
- """Containment operator (in). Equivalent to (a, b) -> a in b."""
572
+ """Containment operator (in). Equivalent to (a, b) => a in b."""
573
573
  return a in b
574
574
  def _coconut_not_in(a, b):
575
- """Negative containment operator (not in). Equivalent to (a, b) -> a not in b."""
575
+ """Negative containment operator (not in). Equivalent to (a, b) => a not in b."""
576
576
  return a not in b
577
577
  def _coconut_none_coalesce(a, b):
578
- """None coalescing operator (??). Equivalent to (a, b) -> a if a is not None else b."""
578
+ """None coalescing operator (??). Equivalent to (a, b) => a if a is not None else b."""
579
579
  return b if a is None else a
580
580
  def _coconut_minus(a, b=_coconut_sentinel):
581
- """Minus operator (-). Effectively equivalent to (a, b=None) -> a - b if b is not None else -a."""
581
+ """Minus operator (-). Effectively equivalent to (a, b=None) => a - b if b is not None else -a."""
582
582
  if b is _coconut_sentinel:
583
583
  return -a
584
584
  return a - b
585
585
  def _coconut_comma_op(*args):
586
- """Comma operator (,). Equivalent to (*args) -> args."""
586
+ """Comma operator (,). Equivalent to (*args) => args."""
587
587
  return args
588
588
  {def_coconut_matmul}
589
589
  class scan(_coconut_has_iter):
@@ -1678,7 +1678,7 @@ def _coconut_dict_merge(*dicts, **kwargs):
1678
1678
  prevlen = _coconut.len(newdict)
1679
1679
  return newdict
1680
1680
  def ident(x, **kwargs):
1681
- """The identity function. Generally equivalent to x -> x. Useful in point-free programming.
1681
+ """The identity function. Generally equivalent to x => x. Useful in point-free programming.
1682
1682
  Accepts one keyword-only argument, side_effect, which specifies a function to call on the argument before it is returned."""
1683
1683
  side_effect = kwargs.pop("side_effect", None)
1684
1684
  if kwargs:
@@ -1874,30 +1874,36 @@ class const(_coconut_base_callable):
1874
1874
  def __repr__(self):
1875
1875
  return "const(%s)" % (_coconut.repr(self.value),)
1876
1876
  class _coconut_lifted(_coconut_base_callable):
1877
- __slots__ = ("func", "func_args", "func_kwargs")
1878
- def __init__(self, _coconut_func, *func_args, **func_kwargs):
1879
- self.func = _coconut_func
1877
+ __slots__ = ("apart", "func", "func_args", "func_kwargs")
1878
+ def __init__(self, apart, func, func_args, func_kwargs):
1879
+ self.apart = apart
1880
+ self.func = func
1880
1881
  self.func_args = func_args
1881
1882
  self.func_kwargs = func_kwargs
1882
1883
  def __reduce__(self):
1883
- return (self.__class__, (self.func,) + self.func_args, {lbrace}"func_kwargs": self.func_kwargs{rbrace})
1884
+ return (self.__class__, (self.apart, self.func, self.func_args, self.func_kwargs))
1884
1885
  def __call__(self, *args, **kwargs):
1885
- return self.func(*(g(*args, **kwargs) for g in self.func_args), **_coconut_py_dict((k, h(*args, **kwargs)) for k, h in self.func_kwargs.items()))
1886
+ if self.apart:
1887
+ return self.func(*(f(x) for f, x in {_coconut_}zip(self.func_args, args, strict=True)), **_coconut_py_dict((k, self.func_kwargs[k](kwargs[k])) for k in {all_keys}))
1888
+ else:
1889
+ return self.func(*(g(*args, **kwargs) for g in self.func_args), **_coconut_py_dict((k, h(*args, **kwargs)) for k, h in self.func_kwargs.items()))
1886
1890
  def __repr__(self):
1887
- return "lift(%r)(%s%s)" % (self.func, ", ".join(_coconut.repr(g) for g in self.func_args), ", ".join(k + "=" + _coconut.repr(h) for k, h in self.func_kwargs.items()))
1891
+ return "lift%s(%r)(%s%s)" % (self.func, ("_apart" if self.apart else ""), ", ".join(_coconut.repr(g) for g in self.func_args), ", ".join(k + "=" + _coconut.repr(h) for k, h in self.func_kwargs.items()))
1888
1892
  class lift(_coconut_base_callable):
1889
- """Lift a function up so that all of its arguments are functions.
1893
+ """Lift a function up so that all of its arguments are functions that all take the same arguments.
1890
1894
 
1891
1895
  For a binary function f(x, y) and two unary functions g(z) and h(z), lift works as the S' combinator:
1892
1896
  lift(f)(g, h)(z) == f(g(z), h(z))
1893
1897
 
1894
1898
  In general, lift is equivalent to:
1895
- def lift(f) = ((*func_args, **func_kwargs) -> (*args, **kwargs) ->
1899
+ def lift(f) = ((*func_args, **func_kwargs) => (*args, **kwargs) => (
1896
1900
  f(*(g(*args, **kwargs) for g in func_args), **{lbrace}k: h(*args, **kwargs) for k, h in func_kwargs.items(){rbrace}))
1901
+ )
1897
1902
 
1898
1903
  lift also supports a shortcut form such that lift(f, *func_args, **func_kwargs) is equivalent to lift(f)(*func_args, **func_kwargs).
1899
1904
  """
1900
1905
  __slots__ = ("func",)
1906
+ _apart = False
1901
1907
  def __new__(cls, func, *func_args, **func_kwargs):
1902
1908
  self = _coconut.super({_coconut_}lift, cls).__new__(cls)
1903
1909
  self.func = func
@@ -1907,9 +1913,24 @@ class lift(_coconut_base_callable):
1907
1913
  def __reduce__(self):
1908
1914
  return (self.__class__, (self.func,))
1909
1915
  def __repr__(self):
1910
- return "lift(%r)" % (self.func,)
1916
+ return "lift%s(%r)" % (("_apart" if self._apart else ""), self.func)
1911
1917
  def __call__(self, *func_args, **func_kwargs):
1912
- return _coconut_lifted(self.func, *func_args, **func_kwargs)
1918
+ return _coconut_lifted(self._apart, self.func, func_args, func_kwargs)
1919
+ class lift_apart(lift):
1920
+ """Lift a function up so that all of its arguments are functions that each take separate arguments.
1921
+
1922
+ For a binary function f(x, y) and two unary functions g(z) and h(z), lift_apart works as the D2 combinator:
1923
+ lift_apart(f)(g, h)(z, w) == f(g(z), h(w))
1924
+
1925
+ In general, lift_apart is equivalent to:
1926
+ def lift_apart(func) = (*func_args, **func_kwargs) => (*args, **kwargs) => func(
1927
+ *(f(x) for f, x in zip(func_args, args, strict=True)),
1928
+ **{lbrace}k: func_kwargs[k](kwargs[k]) for k in func_kwargs.keys() | kwargs.keys(){rbrace},
1929
+ )
1930
+
1931
+ lift_apart also supports a shortcut form such that lift_apart(f, *func_args, **func_kwargs) is equivalent to lift_apart(f)(*func_args, **func_kwargs).
1932
+ """
1933
+ _apart = True
1913
1934
  def all_equal(iterable):
1914
1935
  """For a given iterable, check whether all elements in that iterable are equal to each other.
1915
1936
 
@@ -1974,7 +1995,7 @@ def collectby(key_func, iterable, value_func=None, **kwargs):
1974
1995
  If map_using is passed, calculate key_func and value_func by mapping them over
1975
1996
  the iterable using map_using as map. Useful with process_map/thread_map.
1976
1997
  """
1977
- return {_coconut_}mapreduce(_coconut_lifted(_coconut_comma_op, key_func, {_coconut_}ident if value_func is None else value_func), iterable, **kwargs)
1998
+ return {_coconut_}mapreduce(_coconut_lifted(False, _coconut_comma_op, (key_func, {_coconut_}ident if value_func is None else value_func), {empty_dict}), iterable, **kwargs)
1978
1999
  collectby.using_processes = _coconut_partial(_coconut_parallel_mapreduce, collectby, process_map)
1979
2000
  collectby.using_threads = _coconut_partial(_coconut_parallel_mapreduce, collectby, thread_map)
1980
2001
  def _namedtuple_of(**kwargs):
@@ -639,7 +639,7 @@ force_verbose_logger = get_bool_env_var("COCONUT_FORCE_VERBOSE", False)
639
639
 
640
640
  coconut_home = get_path_env_var(home_env_var, "~")
641
641
 
642
- use_color = get_bool_env_var("COCONUT_USE_COLOR", None)
642
+ use_color_env_var = "COCONUT_USE_COLOR"
643
643
  error_color_code = "31"
644
644
  log_color_code = "93"
645
645
 
@@ -794,6 +794,7 @@ coconut_specific_builtins = (
794
794
  "flip",
795
795
  "const",
796
796
  "lift",
797
+ "lift_apart",
797
798
  "all_equal",
798
799
  "collectby",
799
800
  "mapreduce",
@@ -26,7 +26,7 @@ import sys as _coconut_sys
26
26
  VERSION = "3.0.4"
27
27
  VERSION_NAME = None
28
28
  # False for release, int >= 1 for develop
29
- DEVELOP = 7
29
+ DEVELOP = 8
30
30
  ALPHA = False # for pre releases rather than post releases
31
31
 
32
32
  assert DEVELOP is False or DEVELOP >= 1, "DEVELOP must be False or an int >= 1"
@@ -47,7 +47,8 @@ from coconut.constants import (
47
47
  taberrfmt,
48
48
  use_packrat_parser,
49
49
  embed_on_internal_exc,
50
- use_color,
50
+ use_color_env_var,
51
+ get_bool_env_var,
51
52
  error_color_code,
52
53
  log_color_code,
53
54
  ansii_escape,
@@ -209,6 +210,7 @@ class Logger(object):
209
210
  @classmethod
210
211
  def enable_colors(cls, file=None):
211
212
  """Attempt to enable CLI colors."""
213
+ use_color = get_bool_env_var(use_color_env_var)
212
214
  if (
213
215
  use_color is False
214
216
  or use_color is None and file is not None and not isatty(file)
@@ -1029,7 +1029,7 @@ forward 2""") == 900
1029
1029
  assert Phi((,), (.+1), (.-1)) <| 5 == (6, 4)
1030
1030
  assert Psi((,), (.+1), 3) <| 4 == (4, 5)
1031
1031
  assert D1((,), 0, 1, (.+1)) <| 1 == (0, 1, 2)
1032
- assert D2((+), (.*2), 3, (.+1)) <| 4 == 11
1032
+ assert D2((+), (.*2), 3, (.+1)) <| 4 == 11 == D2_((+), (.*2), (.+1))(3, 4)
1033
1033
  assert E((+), 10, (*), 2) <| 3 == 16
1034
1034
  assert Phi1((,), (+), (*), 2) <| 3 == (5, 6)
1035
1035
  assert BE((,), (+), 10, 2, (*), 2) <| 3 == (12, 6)
@@ -1075,6 +1075,8 @@ forward 2""") == 900
1075
1075
  assert pickle_round_trip(.loc[0]) <| (loc=[10]) == 10
1076
1076
  assert pickle_round_trip(.method(0)) <| (method=const 10) == 10
1077
1077
  assert pickle_round_trip(.method(x=10)) <| (method=x -> x) == 10
1078
+ assert sq_and_t2p1(10) == (100, 21)
1079
+ assert first_false_and_last_true([3, 2, 1, 0, "11", "1", ""]) == (0, "1")
1078
1080
 
1079
1081
  with process_map.multiple_sequential_calls(): # type: ignore
1080
1082
  assert process_map(tuple <.. (|>)$(to_sort), qsorts) |> list == [to_sort |> sorted |> tuple] * len(qsorts)
@@ -1544,6 +1544,24 @@ def BE(f, g, x, y, h, z) = lift(f)(const(g x y), h$(z))
1544
1544
 
1545
1545
  def on(b, u) = (,) ..> map$(u) ..*> b
1546
1546
 
1547
+ def D2_(f, g, h) = lift_apart(f)(g, h)
1548
+
1549
+
1550
+ # branching
1551
+ branch = lift(,)
1552
+ branched = lift_apart(,)
1553
+
1554
+ sq_and_t2p1 = (
1555
+ branch(ident, (.*2))
1556
+ ..*> branched((.**2), (.+1)) # type: ignore
1557
+ )
1558
+
1559
+ first_false_and_last_true = (
1560
+ lift(,)(ident, reversed)
1561
+ ..*> lift_apart(,)(dropwhile$(bool), dropwhile$(not)) # type: ignore
1562
+ ..*> lift_apart(,)(.$[0], .$[0]) # type: ignore
1563
+ )
1564
+
1547
1565
 
1548
1566
  # maximum difference
1549
1567
  def maxdiff1(ns) = (
@@ -1,5 +1,8 @@
1
+ import os
1
2
  from collections.abc import Sequence
2
3
 
4
+ os.environ["COCONUT_USE_COLOR"] = "False"
5
+
3
6
  from coconut.__coconut__ import consume as coc_consume
4
7
  from coconut.constants import (
5
8
  IPY,