stolas 0.1.0__py3-none-any.whl

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 (50) hide show
  1. stolas/__init__.py +0 -0
  2. stolas/logic/__init__.py +56 -0
  3. stolas/logic/access.py +44 -0
  4. stolas/logic/access.pyi +17 -0
  5. stolas/logic/collection.py +137 -0
  6. stolas/logic/collection.pyi +54 -0
  7. stolas/logic/common.py +66 -0
  8. stolas/logic/common.pyi +25 -0
  9. stolas/logic/flow.py +37 -0
  10. stolas/logic/flow.pyi +16 -0
  11. stolas/logic/placeholder.py +296 -0
  12. stolas/logic/placeholder.pyi +81 -0
  13. stolas/logic/predicates.py +107 -0
  14. stolas/logic/predicates.pyi +19 -0
  15. stolas/logic/utils.py +66 -0
  16. stolas/logic/utils.pyi +27 -0
  17. stolas/mypy_plugin.py +62 -0
  18. stolas/operand/__init__.py +30 -0
  19. stolas/operand/arity.py +213 -0
  20. stolas/operand/arity.pyi +85 -0
  21. stolas/operand/cases.py +146 -0
  22. stolas/operand/cases.pyi +19 -0
  23. stolas/operand/concurrent.py +31 -0
  24. stolas/operand/concurrent.pyi +14 -0
  25. stolas/operand/ops.py +37 -0
  26. stolas/operand/ops.pyi +21 -0
  27. stolas/operand/safe.py +131 -0
  28. stolas/operand/safe.pyi +24 -0
  29. stolas/py.typed +0 -0
  30. stolas/struct/__init__.py +6 -0
  31. stolas/struct/struct.py +149 -0
  32. stolas/struct/struct.pyi +15 -0
  33. stolas/struct/trait.py +258 -0
  34. stolas/struct/trait.pyi +71 -0
  35. stolas/types/__init__.py +21 -0
  36. stolas/types/effect.py +79 -0
  37. stolas/types/effect.pyi +25 -0
  38. stolas/types/many.py +126 -0
  39. stolas/types/many.pyi +31 -0
  40. stolas/types/option.py +127 -0
  41. stolas/types/option.pyi +45 -0
  42. stolas/types/result.py +146 -0
  43. stolas/types/result.pyi +50 -0
  44. stolas/types/validated.py +122 -0
  45. stolas/types/validated.pyi +43 -0
  46. stolas-0.1.0.dist-info/LICENSE.md +21 -0
  47. stolas-0.1.0.dist-info/METADATA +120 -0
  48. stolas-0.1.0.dist-info/RECORD +50 -0
  49. stolas-0.1.0.dist-info/WHEEL +5 -0
  50. stolas-0.1.0.dist-info/top_level.txt +1 -0
stolas/__init__.py ADDED
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
+ ]
stolas/logic/access.py ADDED
@@ -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
+
stolas/logic/common.py ADDED
@@ -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
+ ...
stolas/logic/flow.py ADDED
@@ -0,0 +1,37 @@
1
+ """Flow control helpers: check, strict."""
2
+
3
+ from typing import Any, Callable, TypeVar
4
+
5
+ from stolas.types.result import Error, Ok
6
+
7
+ T = TypeVar("T")
8
+
9
+
10
+ def check(
11
+ predicate: Callable[[T], bool], error_msg: str
12
+ ) -> Callable[[T], Ok[T] | Error[str]]:
13
+ """Validate value with predicate, return Ok or Error.
14
+
15
+ Usage: Ok(val) >> check(_ > 0, "must be positive")
16
+ """
17
+
18
+ def wrapper(x: T) -> Ok[T] | Error[str]:
19
+ if predicate(x):
20
+ return Ok(x)
21
+ return Error(error_msg)
22
+
23
+ return wrapper
24
+
25
+
26
+ def strict(type_: type[T]) -> Callable[[Any], Ok[T] | Error[TypeError]]:
27
+ """Validate that value is instance of type, return Ok or Error.
28
+
29
+ Usage: Ok(val) >> strict(int)
30
+ """
31
+
32
+ def wrapper(x: Any) -> Ok[T] | Error[TypeError]:
33
+ if isinstance(x, type_):
34
+ return Ok(x)
35
+ return Error(TypeError(f"Expected {type_.__name__}, got {type(x).__name__}"))
36
+
37
+ return wrapper
stolas/logic/flow.pyi ADDED
@@ -0,0 +1,16 @@
1
+ """Type stubs for flow module."""
2
+
3
+ from typing import Any, Callable, TypeVar
4
+ from stolas.types.result import Ok, Error
5
+
6
+ T = TypeVar("T")
7
+
8
+ def check(
9
+ predicate: Callable[[T], bool], error_msg: str
10
+ ) -> Callable[[T], Ok[T] | Error[str]]:
11
+ """Validate with predicate, return Ok or Error."""
12
+ ...
13
+
14
+ def strict(type_: type[T]) -> Callable[[Any], Ok[T] | Error[TypeError]]:
15
+ """Type validation returning Ok or Error."""
16
+ ...