pythonic-fp-fptools 5.1.2__tar.gz → 5.2.0__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 (28) hide show
  1. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/CHANGELOG.rst +10 -0
  2. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/PKG-INFO +9 -9
  3. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/README.rst +2 -2
  4. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/pyproject.toml +7 -7
  5. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/src/pythonic_fp/fptools/either.py +25 -17
  6. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/src/pythonic_fp/fptools/either.pyi +1 -1
  7. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/src/pythonic_fp/fptools/function.py +22 -26
  8. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/src/pythonic_fp/fptools/function.pyi +2 -1
  9. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/src/pythonic_fp/fptools/lazy.py +18 -22
  10. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/src/pythonic_fp/fptools/maybe.py +6 -7
  11. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/src/pythonic_fp/fptools/state.py +7 -8
  12. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/.gitignore +0 -0
  13. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/LICENSE +0 -0
  14. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/src/pythonic_fp/fptools/__init__.py +0 -0
  15. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/src/pythonic_fp/fptools/__init__.pyi +0 -0
  16. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/src/pythonic_fp/fptools/lazy.pyi +0 -0
  17. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/src/pythonic_fp/fptools/maybe.pyi +0 -0
  18. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/src/pythonic_fp/fptools/py.typed +0 -0
  19. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/src/pythonic_fp/fptools/state.pyi +0 -0
  20. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/tests/either/test_either.py +0 -0
  21. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/tests/either/test_sequence_either.py +0 -0
  22. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/tests/either/test_str_repr_either.py +0 -0
  23. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/tests/function/test_function.py +0 -0
  24. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/tests/lazy/test_lazy.py +0 -0
  25. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/tests/maybe/maybe.py +0 -0
  26. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/tests/maybe/test_sequence_maybe.py +0 -0
  27. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/tests/maybe/test_str_repr_maybe.py +0 -0
  28. {pythonic_fp_fptools-5.1.2 → pythonic_fp_fptools-5.2.0}/tests/state/test_state.py +0 -0
@@ -17,6 +17,16 @@ See `Semantic Versioning 2.0.0 <https://semver.org>`_.
17
17
  Releases and Important Milestones
18
18
  ---------------------------------
19
19
 
20
+ PyPI 5.2.0 - 2026-01-13
21
+ ~~~~~~~~~~~~~~~~~~~~~~~
22
+
23
+ Added function.compose back on 2025-12-04. Docstring updates for Sphinx.
24
+
25
+ PyPI 5.1.2 - 2025-09-28
26
+ ~~~~~~~~~~~~~~~~~~~~~~~
27
+
28
+ Patch bump for pythonic-fp PyPI coordinated release 3.3.3.
29
+
20
30
  PyPI 5.1.1 - 2025-09-09
21
31
  ~~~~~~~~~~~~~~~~~~~~~~~
22
32
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonic-fp-fptools
3
- Version: 5.1.2
3
+ Version: 5.2.0
4
4
  Summary: Pythonic FP - Functional Programming Tools
5
5
  Keywords: either,fp,functional,functional programming,lazy,maybe,monad,non-strict
6
6
  Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
@@ -14,13 +14,13 @@ Classifier: Operating System :: OS Independent
14
14
  Classifier: Programming Language :: Python :: 3.13
15
15
  Classifier: Typing :: Typed
16
16
  License-File: LICENSE
17
- Requires-Dist: pythonic-fp-booleans>=2.0.0
18
- Requires-Dist: pythonic-fp-circulararray>=6.0.0
19
- Requires-Dist: pythonic-fp-gadgets>=3.1.0
17
+ Requires-Dist: pythonic-fp-booleans>=2.1.1
18
+ Requires-Dist: pythonic-fp-circulararray>=6.0.1
19
+ Requires-Dist: pythonic-fp-gadgets>=4.0.2
20
20
  Requires-Dist: pytest>=8.4.1 ; extra == "test"
21
- Requires-Dist: pythonic-fp-containers>=4.0.0 ; extra == "test"
22
- Requires-Dist: pythonic-fp-iterables>=5.1.2 ; extra == "test"
23
- Requires-Dist: pythonic-fp-queues>=4.1.0 ; extra == "test"
21
+ Requires-Dist: pythonic-fp-containers>=4.0.1 ; extra == "test"
22
+ Requires-Dist: pythonic-fp-iterables>=5.1.3 ; extra == "test"
23
+ Requires-Dist: pythonic-fp-queues>=5.1.1 ; extra == "test"
24
24
  Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-fptools/blob/main/CHANGELOG.rst
25
25
  Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/fptools/development/build/html/
26
26
  Project-URL: Homepage, https://grscheller.github.io/pythonic-fp/homepage/build/html/
@@ -53,7 +53,7 @@ endeavoring to be Pythonic.
53
53
 
54
54
  Part of the
55
55
  `pythonic-fp
56
- <https://grscheller.github.io/pythonic-fp/homepage/build/html/index.html>`_
56
+ <https://grscheller.github.io/pythonic-fp>`_
57
57
  PyPI projects.
58
58
 
59
59
  Documentation
@@ -61,7 +61,7 @@ Documentation
61
61
 
62
62
  Documentation for this project is hosted on
63
63
  `GitHub Pages
64
- <https://grscheller.github.io/pythonic-fp/fptools/development/build/html>`_.
64
+ <https://grscheller.github.io/pythonic-fp/fptools>`_.
65
65
 
66
66
  Copyright and License
67
67
  ---------------------
@@ -24,7 +24,7 @@ endeavoring to be Pythonic.
24
24
 
25
25
  Part of the
26
26
  `pythonic-fp
27
- <https://grscheller.github.io/pythonic-fp/homepage/build/html/index.html>`_
27
+ <https://grscheller.github.io/pythonic-fp>`_
28
28
  PyPI projects.
29
29
 
30
30
  Documentation
@@ -32,7 +32,7 @@ Documentation
32
32
 
33
33
  Documentation for this project is hosted on
34
34
  `GitHub Pages
35
- <https://grscheller.github.io/pythonic-fp/fptools/development/build/html>`_.
35
+ <https://grscheller.github.io/pythonic-fp/fptools>`_.
36
36
 
37
37
  Copyright and License
38
38
  ---------------------
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pythonic-fp-fptools"
3
- version = "5.1.2"
3
+ version = "5.2.0"
4
4
  readme = "README.rst"
5
5
  requires-python = ">=3.12"
6
6
  license = { file = "LICENSE" }
@@ -25,9 +25,9 @@ classifiers = [
25
25
  "Typing :: Typed",
26
26
  ]
27
27
  dependencies = [
28
- "pythonic-fp-booleans>=2.0.0",
29
- "pythonic-fp-circulararray>=6.0.0",
30
- "pythonic-fp-gadgets>=3.1.0",
28
+ "pythonic-fp-booleans>=2.1.1",
29
+ "pythonic-fp-circulararray>=6.0.1",
30
+ "pythonic-fp-gadgets>=4.0.2",
31
31
  ]
32
32
  dynamic = ["description"]
33
33
 
@@ -40,9 +40,9 @@ Source = "https://github.com/grscheller/pythonic-fp-fptools"
40
40
  [project.optional-dependencies]
41
41
  test = [
42
42
  "pytest>=8.4.1",
43
- "pythonic-fp-containers>=4.0.0",
44
- "pythonic-fp-iterables>=5.1.2",
45
- "pythonic-fp-queues>=4.1.0",
43
+ "pythonic-fp-containers>=4.0.1",
44
+ "pythonic-fp-iterables>=5.1.3",
45
+ "pythonic-fp-queues>=5.1.1",
46
46
  ]
47
47
 
48
48
  [build-system]
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- __all__ = ['Either', 'LEFT', 'RIGHT']
15
+ __all__ = ['Either', 'EitherBool', 'LEFT', 'RIGHT']
16
16
 
17
17
  from collections.abc import Callable, Iterator, Sequence
18
18
  from typing import cast, overload
@@ -21,6 +21,12 @@ from .maybe import MayBe
21
21
 
22
22
 
23
23
  class EitherBool(SBool):
24
+ """Boolean-like type used by Either constructor.
25
+
26
+ - A "truthy" value passed to constructor produces the unique ``LEFT`` value.
27
+ - A "falsy" value passed to constructor produces the unique ``RIGHT`` value.
28
+
29
+ """
24
30
  def __repr__(self) -> str:
25
31
  if self:
26
32
  return 'LEFT'
@@ -28,36 +34,38 @@ class EitherBool(SBool):
28
34
 
29
35
 
30
36
  LEFT = EitherBool(True)
37
+ """Singleton value signaling ``Either`` constructor to produce a ``LEFT`` Either, the default."""
38
+
31
39
  RIGHT = EitherBool(False)
40
+ """Singleton value signaling ``Either`` constructor to produce a ``RIGHT`` Either."""
32
41
 
33
42
 
34
43
  class Either[L, R]:
35
44
  """
36
- Either Monad
37
- ------------
45
+ .. admonition:: Either Monad
38
46
 
39
- Data structure semantically containing either a left
40
- or a right value, but not both.
47
+ Data structure semantically containing either a left
48
+ or a right value, but not both.
41
49
 
42
- Implements a left biased Either Monad.
50
+ Implements a left biased Either Monad.
43
51
 
44
- - ``Either(value: +L, LEFT)`` produces a left ``Either``
45
- - ``Either(value: +L, RIGHT)`` produces a right ``Either``
52
+ - ``Either(value: +L, LEFT)`` produces a left ``Either``
53
+ - ``Either(value: +L, RIGHT)`` produces a right ``Either``
46
54
 
47
- In a Boolean context
55
+ In a Boolean context
48
56
 
49
- - A left ``Either`` is "truthy"
50
- - A right ``Either`` is "falsy"
57
+ - A left ``Either`` is "truthy"
58
+ - A right ``Either`` is "falsy"
51
59
 
52
- Two ``Either`` objects compare as equal when
60
+ Two ``Either`` objects compare as equal when
53
61
 
54
- - both are left values or both are right values whose values
62
+ - both are left values or both are right values whose values
55
63
 
56
- - are the same object
57
- - compare as equal
64
+ - are the same object
65
+ - compare as equal
58
66
 
59
- Immutable, an ``Either`` does not change after being created.
60
- Therefore ``map`` & ``bind`` return new instances.
67
+ Immutable, an ``Either`` does not change after being created.
68
+ Therefore ``map`` & ``bind`` return new instances.
61
69
 
62
70
  .. warning::
63
71
 
@@ -4,7 +4,7 @@ from collections.abc import Callable, Iterator, Sequence
4
4
  from pythonic_fp.booleans.subtypable import SBool
5
5
  from typing import overload
6
6
 
7
- __all__ = ['Either', 'LEFT', 'RIGHT']
7
+ __all__ = ['Either', 'EitherBool', 'LEFT', 'RIGHT']
8
8
 
9
9
  class EitherBool(SBool): ...
10
10
 
@@ -13,32 +13,28 @@
13
13
  # limitations under the License.
14
14
 
15
15
  """
16
- FP tools for functions
17
- ======================
16
+ .. admonition:: FP tools for functions
18
17
 
19
- FP utilities to manipulate and partially apply functions
18
+ FP utilities to manipulate and partially apply functions.
20
19
 
21
- - *function* **swap** - Swap the arguments of a 2 argument function
22
- - *function* **sequenced** - Convert function to take a sequence of its arguments
23
- - *function* **negate** - Transforms a predicate to its negation
24
- - *function* **partial** - Returns a partially applied function
20
+ - *function* **swap** - Swap the arguments of a 2 argument function
21
+ - *function* **compose** - Function composition
22
+ - *function* **negate** - Transforms a predicate to its negation
23
+ - *function* **sequenced** - Convert function to take a sequence of its arguments
24
+ - *function* **partial** - Returns a partially applied function
25
25
 
26
26
  """
27
27
 
28
28
  from collections.abc import Callable
29
29
  from typing import Any, ParamSpec
30
30
 
31
- __all__ = ['swap', 'sequenced', 'partial', 'negate']
31
+ __all__ = ['swap', 'compose', 'negate', 'sequenced', 'partial']
32
32
 
33
33
  P = ParamSpec('P')
34
34
 
35
35
 
36
36
  def swap[U, V, R](f: Callable[[U, V], R]) -> Callable[[V, U], R]:
37
- """
38
- Swap args
39
- ---------
40
-
41
- Swap arguments of a two argument function.
37
+ """Swap arguments of a two argument function.
42
38
 
43
39
  :param f: Two argument function.
44
40
  :returns: A version of ``f`` with its arguments swapped.
@@ -47,17 +43,23 @@ def swap[U, V, R](f: Callable[[U, V], R]) -> Callable[[V, U], R]:
47
43
  return lambda v, u: f(u, v)
48
44
 
49
45
 
50
- def negate[**P](f: Callable[P, bool]) -> Callable[P, bool]:
46
+ def compose[D, T, R](f: Callable[[D], T], g: Callable[[T], R]) -> Callable[[D], R]:
47
+ """Function Composition
48
+
49
+ :param f: Function called first with domain D and range T.
50
+ :param g: Function called on result with domain T and range R.
51
+ :returns: The composite function g∘f(d) = g(f(d))
52
+
51
53
  """
52
- Negate predicate
53
- ----------------
54
+ return lambda d: g(f(d))
55
+
54
56
 
55
- Take a predicate and return its negation.
57
+ def negate[**P](f: Callable[P, bool]) -> Callable[P, bool]:
58
+ """Take a predicate and return its negation.
56
59
 
57
60
  :param f: a function ``f`` which returns a bool
58
61
  :returns: the function ``not f``
59
62
  """
60
-
61
63
  def ff(*args: P.args, **kwargs: P.kwargs) -> bool:
62
64
  return not f(*args, **kwargs)
63
65
 
@@ -65,9 +67,7 @@ def negate[**P](f: Callable[P, bool]) -> Callable[P, bool]:
65
67
 
66
68
 
67
69
  def sequenced[R](f: Callable[..., R]) -> Callable[[tuple[Any]], R]:
68
- """
69
- Multi-to-single valued
70
- ----------------------
70
+ """Convert a function from multi-to-single valued.
71
71
 
72
72
  Convert a function with arbitrary positional arguments to one taking
73
73
  a tuple of the original arguments.
@@ -80,7 +80,6 @@ def sequenced[R](f: Callable[..., R]) -> Callable[[tuple[Any]], R]:
80
80
  TODO: Look into replacing this function with a Callable class?
81
81
 
82
82
  """
83
-
84
83
  def ff(tupled_args: tuple[Any]) -> R:
85
84
  return f(*tupled_args)
86
85
 
@@ -88,9 +87,7 @@ def sequenced[R](f: Callable[..., R]) -> Callable[[tuple[Any]], R]:
88
87
 
89
88
 
90
89
  def partial[**P, R](f: Callable[P, R], *args: Any) -> Callable[..., R]:
91
- """
92
- Partial application
93
- -------------------
90
+ """Partial function application.
94
91
 
95
92
  Partially apply arguments to a function, left to right.
96
93
 
@@ -98,7 +95,6 @@ def partial[**P, R](f: Callable[P, R], *args: Any) -> Callable[..., R]:
98
95
  - best practice is to cast the result immediately
99
96
 
100
97
  """
101
-
102
98
  def finish(*rest: Any) -> R:
103
99
  return sequenced(f)(args + rest)
104
100
 
@@ -1,11 +1,12 @@
1
1
  from collections.abc import Callable
2
2
  from typing import Any, ParamSpec
3
3
 
4
- __all__ = ['swap', 'sequenced', 'partial', 'negate']
4
+ __all__ = ['swap', 'compose', 'negate', 'sequenced', 'partial']
5
5
 
6
6
  P = ParamSpec('P')
7
7
 
8
8
  def swap[U, V, R](f: Callable[[U, V], R]) -> Callable[[V, U], R]: ...
9
+ def compose[D, T, R](f: Callable[[D], T], g: Callable[[T], R]) -> Callable[[D], R]: ...
9
10
  def negate[**P](f: Callable[P, bool]) -> Callable[P, bool]: ...
10
11
  def sequenced[R](f: Callable[..., R]) -> Callable[[tuple[Any]], R]: ...
11
12
  def partial[**P, R](f: Callable[P, R], *args: Any) -> Callable[..., R]: ...
@@ -13,17 +13,16 @@
13
13
  # limitations under the License.
14
14
 
15
15
  """
16
- Lazy function evaluation
17
- ========================
16
+ .. admonition:: Lazy function evaluation
18
17
 
19
- Delayed function evaluations. FP tools for "non-strict" function evaluations.
20
- Useful to delay a function's evaluation until some inner scope.
18
+ Delayed function evaluations. FP tools for "non-strict" function evaluations.
19
+ Useful to delay a function's evaluation until some inner scope.
21
20
 
22
- Non-strict delayed function evaluation.
21
+ Non-strict delayed function evaluation.
23
22
 
24
- - *class* **Lazy** - Delay evaluation of functions taking & returning single values
25
- - *function* **lazy** - Delay evaluation of functions taking any number of values
26
- - *function* **real_lazy** - Version of ``lazy`` which caches its result
23
+ - *class* **Lazy** - Delay evaluation of functions taking & returning single values
24
+ - *function* **lazy** - Delay evaluation of functions taking any number of values
25
+ - *function* **real_lazy** - Version of ``lazy`` which caches its result
27
26
 
28
27
  """
29
28
 
@@ -38,14 +37,13 @@ __all__ = ['Lazy', 'lazy', 'real_lazy']
38
37
 
39
38
  class Lazy[D, R]:
40
39
  """
41
- Non-strict function evaluation
42
- ------------------------------
40
+ .. admonition:: Non-strict function evaluation
43
41
 
44
- Delayed evaluation of a singled valued function.
42
+ Delayed evaluation of a singled valued function.
45
43
 
46
- Class instance delays the executable of a function where ``Lazy(f, arg)``
47
- constructs an object that can evaluate the Callable ``f`` with its argument
48
- at a later time.
44
+ Class instance delays the executable of a function where ``Lazy(f, arg)``
45
+ constructs an object that can evaluate the Callable ``f`` with its argument
46
+ at a later time.
49
47
 
50
48
  .. note::
51
49
 
@@ -153,11 +151,10 @@ def lazy[**P, R](
153
151
  f: Callable[P, R], *args: P.args, **kwargs: P.kwargs
154
152
  ) -> Lazy[tuple[Any, ...], R]:
155
153
  """
156
- Delayed evaluations
157
- -------------------
154
+ .. admonition:: Delayed evaluations
158
155
 
159
- Function returning a delayed evaluation of a function of an arbitrary number
160
- of positional arguments.
156
+ Function returning a delayed evaluation of a function of an arbitrary number
157
+ of positional arguments.
161
158
 
162
159
  :param f: Function whose evaluation is to be delayed.
163
160
  :param args: Positional arguments to be passed to ``f``.
@@ -172,11 +169,10 @@ def real_lazy[**P, R](
172
169
  f: Callable[P, R], *args: P.args, **kwargs: P.kwargs
173
170
  ) -> Lazy[tuple[Any, ...], R]:
174
171
  """
175
- Cached Delayed evaluations
176
- --------------------------
172
+ .. admonition:: Cached Delayed evaluations
177
173
 
178
- Function returning a delayed evaluation of a function of an arbitrary number
179
- of positional arguments. Evaluation is cached.
174
+ Function returning a delayed evaluation of a function of an
175
+ arbitrary number of positional arguments. Evaluation is cached.
180
176
 
181
177
  :param f: Function whose evaluation is to be delayed.
182
178
  :param args: Positional arguments to be passed to ``f``.
@@ -23,18 +23,17 @@ from pythonic_fp.gadgets.sentinels.flavored import Sentinel
23
23
 
24
24
  class MayBe[D]:
25
25
  """
26
- Maybe Monad
27
- -----------
26
+ .. admonition:: Maybe Monad
28
27
 
29
- Data structure wrapping a potentially missing item.
28
+ Data structure wrapping a potentially missing item.
30
29
 
31
- Immutable semantics
30
+ Immutable semantics
32
31
 
33
- - can store any item of any type, including ``None``
32
+ - can store any item of any type, including ``None``
34
33
 
35
- - with one hidden implementation dependent exception
34
+ - with one hidden implementation dependent exception
36
35
 
37
- - immutable semantics, therefore covariant
36
+ - immutable semantics
38
37
 
39
38
  .. warning::
40
39
 
@@ -20,17 +20,16 @@ from pythonic_fp.circulararray.auto import CA
20
20
 
21
21
  class State[S, A]:
22
22
  """
23
- State Monad
24
- -----------
23
+ .. admonition:: State Monad
25
24
 
26
- Data structure generating values while propagating changes of state.
27
- A pure FP implementation for the State Monad
25
+ Data structure generating values while propagating changes of state.
26
+ A pure FP implementation for the State Monad
28
27
 
29
- - class ``State`` represents neither a state nor (value, state) pair
28
+ - class ``State`` represents neither a state nor a (value, state) pair
30
29
 
31
- - it wraps a transformation old_state -> (value, new_state)
32
- - the ``run`` method is this wrapped transformation
33
- - ``bind`` is just state propagating function composition
30
+ - it wraps a transformation old_state -> (value, new_state)
31
+ - the ``run`` method is this wrapped transformation
32
+ - ``bind`` is just state propagating function composition
34
33
 
35
34
  """
36
35