stolas 0.1.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 (55) hide show
  1. stolas-0.1.0/LICENSE.md +21 -0
  2. stolas-0.1.0/PKG-INFO +120 -0
  3. stolas-0.1.0/README.md +95 -0
  4. stolas-0.1.0/pyproject.toml +49 -0
  5. stolas-0.1.0/setup.cfg +4 -0
  6. stolas-0.1.0/src/stolas/__init__.py +0 -0
  7. stolas-0.1.0/src/stolas/logic/__init__.py +56 -0
  8. stolas-0.1.0/src/stolas/logic/access.py +44 -0
  9. stolas-0.1.0/src/stolas/logic/access.pyi +17 -0
  10. stolas-0.1.0/src/stolas/logic/collection.py +137 -0
  11. stolas-0.1.0/src/stolas/logic/collection.pyi +54 -0
  12. stolas-0.1.0/src/stolas/logic/common.py +66 -0
  13. stolas-0.1.0/src/stolas/logic/common.pyi +25 -0
  14. stolas-0.1.0/src/stolas/logic/flow.py +37 -0
  15. stolas-0.1.0/src/stolas/logic/flow.pyi +16 -0
  16. stolas-0.1.0/src/stolas/logic/placeholder.py +296 -0
  17. stolas-0.1.0/src/stolas/logic/placeholder.pyi +81 -0
  18. stolas-0.1.0/src/stolas/logic/predicates.py +107 -0
  19. stolas-0.1.0/src/stolas/logic/predicates.pyi +19 -0
  20. stolas-0.1.0/src/stolas/logic/utils.py +66 -0
  21. stolas-0.1.0/src/stolas/logic/utils.pyi +27 -0
  22. stolas-0.1.0/src/stolas/mypy_plugin.py +62 -0
  23. stolas-0.1.0/src/stolas/operand/__init__.py +30 -0
  24. stolas-0.1.0/src/stolas/operand/arity.py +213 -0
  25. stolas-0.1.0/src/stolas/operand/arity.pyi +85 -0
  26. stolas-0.1.0/src/stolas/operand/cases.py +146 -0
  27. stolas-0.1.0/src/stolas/operand/cases.pyi +19 -0
  28. stolas-0.1.0/src/stolas/operand/concurrent.py +31 -0
  29. stolas-0.1.0/src/stolas/operand/concurrent.pyi +14 -0
  30. stolas-0.1.0/src/stolas/operand/ops.py +37 -0
  31. stolas-0.1.0/src/stolas/operand/ops.pyi +21 -0
  32. stolas-0.1.0/src/stolas/operand/safe.py +131 -0
  33. stolas-0.1.0/src/stolas/operand/safe.pyi +24 -0
  34. stolas-0.1.0/src/stolas/py.typed +0 -0
  35. stolas-0.1.0/src/stolas/struct/__init__.py +6 -0
  36. stolas-0.1.0/src/stolas/struct/struct.py +149 -0
  37. stolas-0.1.0/src/stolas/struct/struct.pyi +15 -0
  38. stolas-0.1.0/src/stolas/struct/trait.py +258 -0
  39. stolas-0.1.0/src/stolas/struct/trait.pyi +71 -0
  40. stolas-0.1.0/src/stolas/types/__init__.py +21 -0
  41. stolas-0.1.0/src/stolas/types/effect.py +79 -0
  42. stolas-0.1.0/src/stolas/types/effect.pyi +25 -0
  43. stolas-0.1.0/src/stolas/types/many.py +126 -0
  44. stolas-0.1.0/src/stolas/types/many.pyi +31 -0
  45. stolas-0.1.0/src/stolas/types/option.py +127 -0
  46. stolas-0.1.0/src/stolas/types/option.pyi +45 -0
  47. stolas-0.1.0/src/stolas/types/result.py +146 -0
  48. stolas-0.1.0/src/stolas/types/result.pyi +50 -0
  49. stolas-0.1.0/src/stolas/types/validated.py +122 -0
  50. stolas-0.1.0/src/stolas/types/validated.pyi +43 -0
  51. stolas-0.1.0/src/stolas.egg-info/PKG-INFO +120 -0
  52. stolas-0.1.0/src/stolas.egg-info/SOURCES.txt +53 -0
  53. stolas-0.1.0/src/stolas.egg-info/dependency_links.txt +1 -0
  54. stolas-0.1.0/src/stolas.egg-info/requires.txt +5 -0
  55. stolas-0.1.0/src/stolas.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stolas Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
stolas-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.1
2
+ Name: stolas
3
+ Version: 0.1.0
4
+ Summary: The strict, multi-paradigm framework enabling pure functional patterns in Python.
5
+ Author: Stolas Contributors
6
+ Project-URL: Homepage, https://github.com/eaksit-bua/stolas
7
+ Project-URL: Repository, https://github.com/eaksit-bua/stolas
8
+ Project-URL: Documentation, https://github.com/eaksit-bua/stolas#readme
9
+ Keywords: functional,monads,immutable,struct,trait
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE.md
21
+ Provides-Extra: dev
22
+ Requires-Dist: mypy>=1.19.1; extra == "dev"
23
+ Requires-Dist: pytest>=9.0.2; extra == "dev"
24
+ Requires-Dist: pytest-cov>=7.0.0; extra == "dev"
25
+
26
+ # 🦉 Stolas
27
+
28
+ **The strict, multi-paradigm framework enabling pure functional patterns in Python.**
29
+
30
+ *Wisdom through pure functional patterns* — Safe separation of data and behavior with monadic safety, immutability, and type-safe composition.
31
+
32
+ ## 🦉 The S-T-O-L-A-S Framework
33
+
34
+ ### **S**truct
35
+ Fast, immutable data classes with `@struct` and polymorphic `@trait` for behavior dispatch.
36
+
37
+ ### **T**ypes
38
+ Safe monadic containers: `Result`, `Option`, `Validated`, `Effect`, `Many`
39
+
40
+ ### **O**perands
41
+ Powerful decorators: `@ops`, `@cases`, `@binary`, `@as_result`, `concurrent()`
42
+
43
+ ### **L**ogic
44
+ Ergonomic functional combinators: `get`, `at`, `where`, `apply`, `_` placeholder, and 20+ utilities
45
+
46
+ ### **A-S**
47
+ *(Reserved for future expansion)*
48
+
49
+ ## 🦉 Key Features
50
+
51
+ - ✅ **Strictness**: Runtime type checking + `__slots__` for memory efficiency
52
+ - ✅ **Sealed ADTs**: `@cases` decorator with pattern matching and monadic compatibility
53
+ - ✅ **Functional Composition**: Pipeline chaining with `>>`
54
+ - ✅ **Async Concurrency**: Parallel workflows with `concurrent()`
55
+ - ✅ **Polymorphism**: Trait-based dispatch for decoupled behavior
56
+ - ✅ **Type Safety**: Full `mypy --strict` compatibility
57
+
58
+ ## 🦉 Installation
59
+
60
+ ```bash
61
+ pip install stolas
62
+ ```
63
+
64
+ ## 🦉 Quick Example
65
+
66
+ ```python
67
+ from stolas.struct import struct
68
+ from stolas.types import Many
69
+ from stolas.operand import binary, as_result, ops
70
+ from stolas.logic import where, apply, _
71
+
72
+ # Immutable data
73
+ @struct
74
+ class User:
75
+ id: int
76
+ name: str
77
+ email: str
78
+
79
+ # Safe, curried operations
80
+ @ops(binary, as_result)
81
+ def divide(a: int, b: int) -> float:
82
+ return a / b
83
+
84
+ # Functional pipelines with placeholder
85
+ users = Many([
86
+ User(1, "Alice", "alice@example.com"),
87
+ User(2, "Bob", "bob@example.com"),
88
+ ])
89
+
90
+ result = users >> where(_.id > 1) >> apply(_.name) # Many(["Bob"])
91
+
92
+ # Monadic safety
93
+ divide(10)(2) # Ok(5.0)
94
+ divide(10)(0) # Error(ZeroDivisionError(...))
95
+ ```
96
+
97
+ ## 🦉 Documentation
98
+
99
+ For detailed documentation, see the **[docs/](docs/)** directory:
100
+
101
+ - **[Struct & Trait](docs/struct.md)** - Polymorphism (`@trait`) and immutable data (`@struct`)
102
+ - **[Monadic Types](docs/types.md)** - `Result`, `Option`, `Validated`, `Effect`, `Many`
103
+ - **[Operands](docs/operands.md)** - Decorators `@cases`, `@safe`, and Concurrency
104
+ - **[Logic Reference](docs/logic.md)** - Combinators and Placeholder (`_`)
105
+
106
+ ## 🦉 Testing
107
+
108
+ ```bash
109
+ # Run tests
110
+ python -m pytest tests/
111
+
112
+ # Type checking
113
+ mypy src/stolas --strict
114
+ ```
115
+
116
+ **Status:** 🦉 739 tests passing • 100% coverage • 100% mypy strict compliance
117
+
118
+ ## 🦉 License
119
+
120
+ MIT License
stolas-0.1.0/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # 🦉 Stolas
2
+
3
+ **The strict, multi-paradigm framework enabling pure functional patterns in Python.**
4
+
5
+ *Wisdom through pure functional patterns* — Safe separation of data and behavior with monadic safety, immutability, and type-safe composition.
6
+
7
+ ## 🦉 The S-T-O-L-A-S Framework
8
+
9
+ ### **S**truct
10
+ Fast, immutable data classes with `@struct` and polymorphic `@trait` for behavior dispatch.
11
+
12
+ ### **T**ypes
13
+ Safe monadic containers: `Result`, `Option`, `Validated`, `Effect`, `Many`
14
+
15
+ ### **O**perands
16
+ Powerful decorators: `@ops`, `@cases`, `@binary`, `@as_result`, `concurrent()`
17
+
18
+ ### **L**ogic
19
+ Ergonomic functional combinators: `get`, `at`, `where`, `apply`, `_` placeholder, and 20+ utilities
20
+
21
+ ### **A-S**
22
+ *(Reserved for future expansion)*
23
+
24
+ ## 🦉 Key Features
25
+
26
+ - ✅ **Strictness**: Runtime type checking + `__slots__` for memory efficiency
27
+ - ✅ **Sealed ADTs**: `@cases` decorator with pattern matching and monadic compatibility
28
+ - ✅ **Functional Composition**: Pipeline chaining with `>>`
29
+ - ✅ **Async Concurrency**: Parallel workflows with `concurrent()`
30
+ - ✅ **Polymorphism**: Trait-based dispatch for decoupled behavior
31
+ - ✅ **Type Safety**: Full `mypy --strict` compatibility
32
+
33
+ ## 🦉 Installation
34
+
35
+ ```bash
36
+ pip install stolas
37
+ ```
38
+
39
+ ## 🦉 Quick Example
40
+
41
+ ```python
42
+ from stolas.struct import struct
43
+ from stolas.types import Many
44
+ from stolas.operand import binary, as_result, ops
45
+ from stolas.logic import where, apply, _
46
+
47
+ # Immutable data
48
+ @struct
49
+ class User:
50
+ id: int
51
+ name: str
52
+ email: str
53
+
54
+ # Safe, curried operations
55
+ @ops(binary, as_result)
56
+ def divide(a: int, b: int) -> float:
57
+ return a / b
58
+
59
+ # Functional pipelines with placeholder
60
+ users = Many([
61
+ User(1, "Alice", "alice@example.com"),
62
+ User(2, "Bob", "bob@example.com"),
63
+ ])
64
+
65
+ result = users >> where(_.id > 1) >> apply(_.name) # Many(["Bob"])
66
+
67
+ # Monadic safety
68
+ divide(10)(2) # Ok(5.0)
69
+ divide(10)(0) # Error(ZeroDivisionError(...))
70
+ ```
71
+
72
+ ## 🦉 Documentation
73
+
74
+ For detailed documentation, see the **[docs/](docs/)** directory:
75
+
76
+ - **[Struct & Trait](docs/struct.md)** - Polymorphism (`@trait`) and immutable data (`@struct`)
77
+ - **[Monadic Types](docs/types.md)** - `Result`, `Option`, `Validated`, `Effect`, `Many`
78
+ - **[Operands](docs/operands.md)** - Decorators `@cases`, `@safe`, and Concurrency
79
+ - **[Logic Reference](docs/logic.md)** - Combinators and Placeholder (`_`)
80
+
81
+ ## 🦉 Testing
82
+
83
+ ```bash
84
+ # Run tests
85
+ python -m pytest tests/
86
+
87
+ # Type checking
88
+ mypy src/stolas --strict
89
+ ```
90
+
91
+ **Status:** 🦉 739 tests passing • 100% coverage • 100% mypy strict compliance
92
+
93
+ ## 🦉 License
94
+
95
+ MIT License
@@ -0,0 +1,49 @@
1
+ [project]
2
+ name = "stolas"
3
+ version = "0.1.0"
4
+ description = "The strict, multi-paradigm framework enabling pure functional patterns in Python."
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ authors = [
8
+ {name = "Stolas Contributors"}
9
+ ]
10
+ keywords = ["functional", "monads", "immutable", "struct", "trait"]
11
+ classifiers = [
12
+ "Development Status :: 4 - Beta",
13
+ "Intended Audience :: Developers",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Topic :: Software Development :: Libraries :: Python Modules",
19
+ "Typing :: Typed",
20
+ ]
21
+ dependencies = []
22
+
23
+ [project.optional-dependencies]
24
+ dev = [
25
+ "mypy>=1.19.1",
26
+ "pytest>=9.0.2",
27
+ "pytest-cov>=7.0.0",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/eaksit-bua/stolas"
32
+ Repository = "https://github.com/eaksit-bua/stolas"
33
+ Documentation = "https://github.com/eaksit-bua/stolas#readme"
34
+
35
+ [build-system]
36
+ requires = ["setuptools>=61.0,<75.0"]
37
+ build-backend = "setuptools.build_meta"
38
+
39
+ [tool.setuptools.packages.find]
40
+ where = ["src"]
41
+
42
+ [tool.coverage.run]
43
+ omit = ["src/stolas/mypy_plugin.py"]
44
+
45
+ [tool.coverage.report]
46
+ exclude_lines = [
47
+ "pragma: no cover",
48
+ "if TYPE_CHECKING:",
49
+ ]
stolas-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,56 @@
1
+ """Logic module: functional utilities and helpers."""
2
+
3
+ from stolas.logic.access import at, call, get
4
+ from stolas.logic.collection import (
5
+ apply,
6
+ chain,
7
+ count,
8
+ find,
9
+ first,
10
+ last,
11
+ pair,
12
+ sort,
13
+ where,
14
+ )
15
+ from stolas.logic.common import const, fmt, identity, tap, tee
16
+ from stolas.logic.predicates import both, contains, either, negate
17
+ from stolas.logic.flow import check, strict
18
+ from stolas.logic.placeholder import _
19
+ from stolas.logic.utils import alt, compose, when, wrap
20
+
21
+ __all__ = [
22
+ # Access
23
+ "get",
24
+ "at",
25
+ "call",
26
+ # Collection
27
+ "chain",
28
+ "where",
29
+ "apply",
30
+ "count",
31
+ "first",
32
+ "last",
33
+ "pair",
34
+ "find",
35
+ "sort",
36
+ # Flow
37
+ "check",
38
+ "strict",
39
+ # Placeholder
40
+ "_",
41
+ # Utils
42
+ "identity",
43
+ "const",
44
+ "tap",
45
+ "tee",
46
+ "fmt",
47
+ "wrap",
48
+ "when",
49
+ "compose",
50
+ "alt",
51
+ # Predicates
52
+ "contains",
53
+ "negate",
54
+ "both",
55
+ "either",
56
+ ]
@@ -0,0 +1,44 @@
1
+ """Accessor helpers: get, at, call."""
2
+
3
+ from typing import Any, Callable, TypeVar
4
+
5
+ T = TypeVar("T")
6
+ K = TypeVar("K")
7
+
8
+
9
+ def get(attr: str) -> Callable[[Any], Any]:
10
+ """Return a function that gets an attribute from an object.
11
+
12
+ Usage: Ok(user) >> get("name")
13
+ """
14
+
15
+ def wrapper(x: Any) -> Any:
16
+ return getattr(x, attr)
17
+
18
+ return wrapper
19
+
20
+
21
+ def at(key: K) -> Callable[[Any], Any]:
22
+ """Return a function that gets an item by key/index.
23
+
24
+ Usage: Ok(data) >> at("id")
25
+ Ok(items) >> at(0)
26
+ """
27
+
28
+ def wrapper(x: Any) -> Any:
29
+ return x[key]
30
+
31
+ return wrapper
32
+
33
+
34
+ def call(method: str, *args: Any, **kwargs: Any) -> Callable[[Any], Any]:
35
+ """Return a function that calls a method on an object.
36
+
37
+ Usage: Ok(s) >> call("upper")
38
+ Ok(s) >> call("replace", "a", "b")
39
+ """
40
+
41
+ def wrapper(x: Any) -> Any:
42
+ return getattr(x, method)(*args, **kwargs)
43
+
44
+ return wrapper
@@ -0,0 +1,17 @@
1
+ """Type stubs for access module."""
2
+
3
+ from typing import Any, Callable, TypeVar
4
+
5
+ K = TypeVar("K")
6
+
7
+ def get(attr: str) -> Callable[[Any], Any]:
8
+ """Get attribute from object."""
9
+ ...
10
+
11
+ def at(key: K) -> Callable[[Any], Any]:
12
+ """Get item by key or index."""
13
+ ...
14
+
15
+ def call(method: str, *args: Any, **kwargs: Any) -> Callable[[Any], Any]:
16
+ """Call method on object."""
17
+ ...
@@ -0,0 +1,137 @@
1
+ """Collection helpers: chain, where, apply, count, first, last, pair, find, sort."""
2
+
3
+ from typing import Any, Callable, Iterable, TypeVar
4
+
5
+ from stolas.types.many import Many
6
+ from stolas.types.option import Nothing, Option, Some
7
+
8
+ T = TypeVar("T")
9
+ U = TypeVar("U")
10
+
11
+
12
+ def chain(
13
+ func: Callable[[T], Iterable[U]] | Callable[[T], Many[U]],
14
+ ) -> Callable[[Many[T]], Many[U]]:
15
+ """FlatMap helper: maps function over Many items and flattens result.
16
+
17
+ Usage: Many(...) >> chain(_.sub_items)
18
+ """
19
+
20
+ def wrapper(m: Many[T]) -> Many[U]:
21
+ results: list[U] = []
22
+ for x in m._items:
23
+ res = func(x)
24
+ if isinstance(res, Many):
25
+ results.extend(res.items)
26
+ elif isinstance(res, Iterable):
27
+ results.extend(res)
28
+ else:
29
+ raise TypeError(f"Expected Iterable or Many, got {type(res)}")
30
+ return Many(tuple(results))
31
+
32
+ return wrapper
33
+
34
+
35
+ def where(predicate: Callable[[T], bool]) -> Callable[[Many[T]], Many[T]]:
36
+ """Filter helper: keeps items matching predicate.
37
+
38
+ Usage: Many(...) >> where(_ > 10)
39
+ """
40
+
41
+ def wrapper(m: Many[T]) -> Many[T]:
42
+ return Many(tuple(x for x in m._items if predicate(x)))
43
+
44
+ return wrapper
45
+
46
+
47
+ def apply(func: Callable[[T], U]) -> Callable[[Many[T]], Many[U]]:
48
+ """Map helper: applies function to each item.
49
+
50
+ Usage: Many(...) >> apply(_.upper())
51
+ """
52
+
53
+ def wrapper(m: Many[T]) -> Many[U]:
54
+ return Many(tuple(func(x) for x in m._items))
55
+
56
+ return wrapper
57
+
58
+
59
+ def count() -> Callable[[Many[T]], Some[int]]:
60
+ """Return count of items wrapped in Some.
61
+
62
+ Usage: Many(...) >> count() # returns Some(N)
63
+ """
64
+
65
+ def wrapper(m: Many[T]) -> Some[int]:
66
+ return Some(len(m._items))
67
+
68
+ return wrapper
69
+
70
+
71
+ def first() -> Callable[[Many[T]], Option[T]]:
72
+ """Return first item as Some, or Nothing if empty.
73
+
74
+ Usage: Many(...) >> first() # returns Some(x) or Nothing
75
+ """
76
+
77
+ def wrapper(m: Many[T]) -> Option[T]:
78
+ if m._items:
79
+ return Some(m._items[0])
80
+ return Nothing
81
+
82
+ return wrapper
83
+
84
+
85
+ def last() -> Callable[[Many[T]], Option[T]]:
86
+ """Return last item as Some, or Nothing if empty.
87
+
88
+ Usage: Many(...) >> last() # returns Some(x) or Nothing
89
+ """
90
+
91
+ def wrapper(m: Many[T]) -> Option[T]:
92
+ if m._items:
93
+ return Some(m._items[-1])
94
+ return Nothing
95
+
96
+ return wrapper
97
+
98
+
99
+ def pair(other: Many[U]) -> Callable[[Many[T]], Many[tuple[T, U]]]:
100
+ """Zip with another Many collection.
101
+
102
+ Usage: Many([1,2]) >> pair(Many(['a','b'])) # Many([(1,'a'), (2,'b')])
103
+ """
104
+
105
+ def wrapper(m: Many[T]) -> Many[tuple[T, U]]:
106
+ return Many(tuple(zip(m._items, other._items)))
107
+
108
+ return wrapper
109
+
110
+
111
+ def find(predicate: Callable[[T], bool]) -> Callable[[Many[T]], Option[T]]:
112
+ """Find first item matching predicate.
113
+
114
+ Usage: Many(...) >> find(_ == 5) # returns Some(5) or Nothing
115
+ """
116
+
117
+ def wrapper(m: Many[T]) -> Option[T]:
118
+ for x in m._items:
119
+ if predicate(x):
120
+ return Some(x)
121
+ return Nothing
122
+
123
+ return wrapper
124
+
125
+
126
+ def sort(
127
+ key: Callable[[T], Any] | None = None, reverse: bool = False
128
+ ) -> Callable[[Many[T]], Many[T]]:
129
+ """Return sorted Many.
130
+
131
+ Usage: Many(...) >> sort(key=_.age)
132
+ """
133
+
134
+ def wrapper(m: Many[T]) -> Many[T]:
135
+ return Many(tuple(sorted(m._items, key=key, reverse=reverse))) # type: ignore[arg-type,type-var]
136
+
137
+ return wrapper
@@ -0,0 +1,54 @@
1
+ """Type stubs for collection module."""
2
+
3
+ from typing import Any, Callable, Iterable, TypeVar, overload
4
+ from stolas.types.many import Many
5
+ from stolas.types.option import Option, Some
6
+
7
+ T = TypeVar("T")
8
+ U = TypeVar("U")
9
+
10
+ def chain(func: Callable[[Any], Iterable[U]]) -> Callable[[Many[Any]], Many[U]]:
11
+ """FlatMap over Many items.
12
+
13
+ Works with any function returning Iterable (including Many).
14
+ """
15
+ ...
16
+
17
+ def where(predicate: Callable[[T], bool]) -> Callable[[Many[T]], Many[T]]:
18
+ """Filter items by predicate."""
19
+ ...
20
+
21
+ def apply(func: Callable[[T], U]) -> Callable[[Many[T]], Many[U]]:
22
+ """Map function over items."""
23
+ ...
24
+
25
+ def count() -> Callable[[Many[Any]], Some[int]]:
26
+ """Count items."""
27
+ ...
28
+
29
+ def first() -> Callable[[Many[T]], Option[T]]:
30
+ """Get first item."""
31
+ ...
32
+
33
+ def last() -> Callable[[Many[T]], Option[T]]:
34
+ """Get last item."""
35
+ ...
36
+
37
+ def pair(other: Many[U]) -> Callable[[Many[T]], Many[tuple[T, U]]]:
38
+ """Zip with another Many."""
39
+ ...
40
+
41
+ def find(predicate: Callable[[T], bool]) -> Callable[[Many[T]], Option[T]]:
42
+ """Find first matching item."""
43
+ ...
44
+
45
+ @overload
46
+ def sort(*, reverse: bool = ...) -> Callable[[Many[Any]], Many[Any]]:
47
+ """Sort items with default key."""
48
+ ...
49
+
50
+ @overload
51
+ def sort(key: Callable[[T], Any], reverse: bool = ...) -> Callable[[Many[T]], Many[T]]:
52
+ """Sort items with custom key."""
53
+ ...
54
+
@@ -0,0 +1,66 @@
1
+ """Common functional combinators: identity, const, tap, tee, fmt."""
2
+
3
+ from typing import Any, Callable, TypeVar
4
+
5
+ T = TypeVar("T")
6
+
7
+
8
+ def identity(x: T) -> T:
9
+ """Return the input unchanged (pass-through).
10
+
11
+ Usage: stream >> identity
12
+ """
13
+ return x
14
+
15
+
16
+ def const(value: T) -> Callable[[Any], T]:
17
+ """Return a function that always returns the given value.
18
+
19
+ Usage: input >> const(10) # returns 10
20
+ """
21
+
22
+ def wrapper(_: Any) -> T:
23
+ return value
24
+
25
+ return wrapper
26
+
27
+
28
+ def tap(func: Callable[[T], Any]) -> Callable[[T], T]:
29
+ """Execute func(x) for side effect and return x unchanged.
30
+
31
+ Usage: Ok(x) >> tap(print)
32
+ """
33
+
34
+ def wrapper(x: T) -> T:
35
+ func(x)
36
+ return x
37
+
38
+ return wrapper
39
+
40
+
41
+ def tee(func: Callable[[T], Any]) -> Callable[[T], T]:
42
+ """Execute func(x) and return x. Alias for tap.
43
+
44
+ Usage: Ok(x) >> tee(log_to_db)
45
+ """
46
+
47
+ def wrapper(x: T) -> T:
48
+ func(x)
49
+ return x
50
+
51
+ return wrapper
52
+
53
+
54
+ def fmt(template: str) -> Callable[[Any], str]:
55
+ """Format a value into a template string.
56
+
57
+ Returns a function that calls template.format(x).
58
+
59
+ Usage: Ok("Alice") >> fmt("Hello, {}!") # Ok("Hello, Alice!")
60
+ compose(_.name, fmt("{}: adult"))
61
+ """
62
+
63
+ def wrapper(x: Any) -> str:
64
+ return template.format(x)
65
+
66
+ return wrapper
@@ -0,0 +1,25 @@
1
+ """Type stubs for common module."""
2
+
3
+ from typing import Any, Callable, TypeVar
4
+
5
+ T = TypeVar("T")
6
+
7
+ def identity(x: T) -> T:
8
+ """Return input unchanged."""
9
+ ...
10
+
11
+ def const(value: T) -> Callable[[Any], T]:
12
+ """Return function that always returns value."""
13
+ ...
14
+
15
+ def tap(func: Callable[[T], Any]) -> Callable[[T], T]:
16
+ """Execute side-effect and return input."""
17
+ ...
18
+
19
+ def tee(func: Callable[[T], Any]) -> Callable[[T], T]:
20
+ """Alias for tap."""
21
+ ...
22
+
23
+ def fmt(template: str) -> Callable[[Any], str]:
24
+ """Format value into template string."""
25
+ ...