punctional 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.
- punctional-0.1.0/.gitignore +10 -0
- punctional-0.1.0/LICENSE +21 -0
- punctional-0.1.0/PKG-INFO +445 -0
- punctional-0.1.0/README.md +421 -0
- punctional-0.1.0/punctional/__init__.py +61 -0
- punctional-0.1.0/punctional/arithmetic.py +46 -0
- punctional-0.1.0/punctional/comparison.py +36 -0
- punctional-0.1.0/punctional/core.py +92 -0
- punctional-0.1.0/punctional/list_filters.py +40 -0
- punctional-0.1.0/punctional/logical.py +38 -0
- punctional-0.1.0/punctional/monads.py +357 -0
- punctional-0.1.0/punctional/string.py +29 -0
- punctional-0.1.0/punctional/types.py +63 -0
- punctional-0.1.0/pyproject.toml +60 -0
punctional-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mehdi Yaminli
|
|
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.
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: punctional
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A functional programming framework for Python โ composable filters, method chaining, and expressive data pipelines with Option and Result monads.
|
|
5
|
+
Project-URL: Homepage, https://github.com/peghaz/punctional
|
|
6
|
+
Project-URL: Repository, https://github.com/peghaz/punctional
|
|
7
|
+
Project-URL: Documentation, https://github.com/peghaz/punctional#readme
|
|
8
|
+
Project-URL: Issues, https://github.com/peghaz/punctional/issues
|
|
9
|
+
Author-email: Mehdi Peghaz <peghaz@example.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: composition,data-pipeline,filters,functional-programming,method-chaining,monads,option,pipe-operator,result
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.12
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# Punctional
|
|
26
|
+
|
|
27
|
+
> A functional programming framework for Python โ enabling composable filters, method chaining, and expressive data pipelines.
|
|
28
|
+
|
|
29
|
+
[](https://www.python.org/downloads/)
|
|
30
|
+
[](LICENSE)
|
|
31
|
+
|
|
32
|
+
## ๐ฏ What is Punctional?
|
|
33
|
+
|
|
34
|
+
**Punctional** is a lightweight functional programming library that brings powerful functional programming patterns to Python. It allows you to compose operations using an intuitive pipe (`|`) operator, create reusable transformation filters, and apply common functional design patterns like Option and Result monads.
|
|
35
|
+
|
|
36
|
+
Whether you're building data pipelines, validation logic, or just want cleaner, more declarative code โ Punctional provides the building blocks.
|
|
37
|
+
|
|
38
|
+
## โจ Key Features
|
|
39
|
+
|
|
40
|
+
- **๐ Pipe Operator Chaining** โ Chain transformations using the intuitive `|` operator
|
|
41
|
+
- **๐งฑ Composable Filters** โ Create reusable, testable transformation units
|
|
42
|
+
- **๐ฆ Functional Wrappers** โ Wrap native types (`int`, `float`, `str`) for functional operations
|
|
43
|
+
- **๐ญ Monads** โ Built-in `Option` (Some/Nothing) and `Result` (Ok/Error) monads
|
|
44
|
+
- **๐ง Extensible** โ Easily create custom filters for your domain
|
|
45
|
+
- **๐ Dataclass Support** โ Make any dataclass functional with the `Functional` mixin
|
|
46
|
+
|
|
47
|
+
## ๐ฆ Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install punctional
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or install from source:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/peghaz/punctional.git
|
|
57
|
+
cd punctional
|
|
58
|
+
pip install -e .
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## ๐ Quick Start
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from punctional import fint, fstr, Add, Mult, ToUpper, GreaterThan, AndFilter, LessThan
|
|
65
|
+
|
|
66
|
+
# Arithmetic chaining
|
|
67
|
+
result = fint(10) | Add(5) | Mult(2) # (10 + 5) * 2 = 30
|
|
68
|
+
|
|
69
|
+
# String transformations
|
|
70
|
+
text = fstr("hello") | ToUpper() | Add("!") # "HELLO!"
|
|
71
|
+
|
|
72
|
+
# Logical validation
|
|
73
|
+
is_valid = fint(42) | AndFilter(GreaterThan(10), LessThan(100)) # True
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## ๐ Core Concepts
|
|
77
|
+
|
|
78
|
+
### Filters
|
|
79
|
+
|
|
80
|
+
A **Filter** is the fundamental building block โ a transformation that takes an input and produces an output:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from punctional import Filter, fint
|
|
84
|
+
|
|
85
|
+
class Square(Filter[int, int]):
|
|
86
|
+
def apply(self, value: int) -> int:
|
|
87
|
+
return value ** 2
|
|
88
|
+
|
|
89
|
+
# Use it
|
|
90
|
+
result = fint(5) | Square() # 25
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Functional Wrappers
|
|
94
|
+
|
|
95
|
+
Wrap native Python types to enable pipe operations:
|
|
96
|
+
|
|
97
|
+
| Function | Type | Description |
|
|
98
|
+
|----------|------|-------------|
|
|
99
|
+
| `fint(x)` | `FunctionalInt` | Functional integer wrapper |
|
|
100
|
+
| `ffloat(x)` | `FunctionalFloat` | Functional float wrapper |
|
|
101
|
+
| `fstr(x)` | `FunctionalStr` | Functional string wrapper |
|
|
102
|
+
|
|
103
|
+
### The Pipe Operator
|
|
104
|
+
|
|
105
|
+
Chain filters using the `|` operator for readable, left-to-right transformations:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
# Instead of nested calls:
|
|
109
|
+
result = Div(4).apply(Sub(3).apply(Mult(2).apply(Add(5).apply(10))))
|
|
110
|
+
|
|
111
|
+
# Write declaratively:
|
|
112
|
+
result = fint(10) | Add(5) | Mult(2) | Sub(3) | Div(4)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## ๐ง Built-in Filters
|
|
116
|
+
|
|
117
|
+
### Arithmetic Filters
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from punctional import Add, Sub, Mult, Div
|
|
121
|
+
|
|
122
|
+
fint(10) | Add(5) # 15
|
|
123
|
+
fint(10) | Sub(3) # 7
|
|
124
|
+
fint(10) | Mult(2) # 20
|
|
125
|
+
fint(10) | Div(4) # 2.5
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Comparison Filters
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from punctional import GreaterThan, LessThan, Equals
|
|
132
|
+
|
|
133
|
+
fint(42) | GreaterThan(10) # True
|
|
134
|
+
fint(5) | LessThan(10) # True
|
|
135
|
+
fint(42) | Equals(42) # True
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Logical Filters
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
from punctional import AndFilter, OrFilter, NotFilter, GreaterThan, LessThan, Equals
|
|
142
|
+
|
|
143
|
+
# AND: all conditions must be true
|
|
144
|
+
fint(42) | AndFilter(GreaterThan(10), LessThan(100)) # True
|
|
145
|
+
|
|
146
|
+
# OR: at least one condition must be true
|
|
147
|
+
fint(5) | OrFilter(LessThan(10), GreaterThan(100)) # True
|
|
148
|
+
|
|
149
|
+
# NOT: negate a condition
|
|
150
|
+
fint(5) | NotFilter(Equals(0)) # True
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### String Filters
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from punctional import ToUpper, ToLower, Contains, fstr
|
|
157
|
+
|
|
158
|
+
fstr("hello") | ToUpper() # "HELLO"
|
|
159
|
+
fstr("WORLD") | ToLower() # "world"
|
|
160
|
+
fstr("hello world") | Contains("world") # True
|
|
161
|
+
fstr("ha") | Mult(3) # "hahaha"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### List Filters
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from punctional import Map, FilterList, Reduce, Mult, GreaterThan
|
|
168
|
+
|
|
169
|
+
numbers = [1, 2, 3, 4, 5]
|
|
170
|
+
|
|
171
|
+
# Transform each element
|
|
172
|
+
Map(Mult(2)).apply(numbers) # [2, 4, 6, 8, 10]
|
|
173
|
+
|
|
174
|
+
# Filter elements
|
|
175
|
+
FilterList(GreaterThan(2)).apply(numbers) # [3, 4, 5]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Composition
|
|
179
|
+
|
|
180
|
+
Create reusable pipelines with `Compose`:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from punctional import Compose, Mult, Add, fint
|
|
184
|
+
|
|
185
|
+
# Create a reusable transformation
|
|
186
|
+
double_plus_ten = Compose(Mult(2), Add(10))
|
|
187
|
+
|
|
188
|
+
fint(5) | double_plus_ten # 20
|
|
189
|
+
fint(10) | double_plus_ten # 30
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## ๐ญ Functional Design Patterns
|
|
193
|
+
|
|
194
|
+
### Option Monad (Some/Nothing)
|
|
195
|
+
|
|
196
|
+
Handle nullable values without explicit `None` checks:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from punctional import Some, Nothing, some
|
|
200
|
+
|
|
201
|
+
# Wrap a value
|
|
202
|
+
value = Some(42)
|
|
203
|
+
print(value.map(lambda x: x * 2)) # Some(84)
|
|
204
|
+
print(value.get_or_else(0)) # 42
|
|
205
|
+
|
|
206
|
+
# Handle absence
|
|
207
|
+
empty = Nothing()
|
|
208
|
+
print(empty.map(lambda x: x * 2)) # Nothing
|
|
209
|
+
print(empty.get_or_else(0)) # 0
|
|
210
|
+
|
|
211
|
+
# Auto-convert from potentially None values
|
|
212
|
+
result = some(potentially_none_value) # Returns Nothing if None
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### Option Operations
|
|
216
|
+
|
|
217
|
+
| Method | Description |
|
|
218
|
+
|--------|-------------|
|
|
219
|
+
| `map(f)` | Transform value if present |
|
|
220
|
+
| `flat_map(f)` | Chain operations returning Option |
|
|
221
|
+
| `bind(f)` | Alias for flat_map |
|
|
222
|
+
| `get_or_else(default)` | Get value or default |
|
|
223
|
+
| `get_or_none()` | Get value or None |
|
|
224
|
+
| `filter(predicate)` | Return Nothing if predicate fails |
|
|
225
|
+
| `or_else(alternative)` | Return alternative if Nothing |
|
|
226
|
+
| `is_some()` | Check if value is present |
|
|
227
|
+
| `is_nothing()` | Check if value is absent |
|
|
228
|
+
|
|
229
|
+
### Result Monad (Ok/Error)
|
|
230
|
+
|
|
231
|
+
Handle operations that can fail with meaningful errors:
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
from punctional import Ok, Error, try_result
|
|
235
|
+
|
|
236
|
+
# Successful operation
|
|
237
|
+
success = Ok(42)
|
|
238
|
+
print(success.map(lambda x: x * 2)) # Ok(84)
|
|
239
|
+
|
|
240
|
+
# Failed operation
|
|
241
|
+
failure = Error("Something went wrong")
|
|
242
|
+
print(failure.map(lambda x: x * 2)) # Error("Something went wrong")
|
|
243
|
+
|
|
244
|
+
# Wrap potentially throwing functions
|
|
245
|
+
def divide(a, b):
|
|
246
|
+
return a / b
|
|
247
|
+
|
|
248
|
+
result = try_result(lambda: divide(10, 0))
|
|
249
|
+
# Error(ZeroDivisionError(...))
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
#### Result Operations
|
|
253
|
+
|
|
254
|
+
| Method | Description |
|
|
255
|
+
|--------|-------------|
|
|
256
|
+
| `map(f)` | Transform success value |
|
|
257
|
+
| `map_error(f)` | Transform error value |
|
|
258
|
+
| `flat_map(f)` | Chain operations returning Result |
|
|
259
|
+
| `bind(f)` | Alias for flat_map |
|
|
260
|
+
| `get_or_else(default)` | Get value or default |
|
|
261
|
+
| `is_ok()` | Check if successful |
|
|
262
|
+
| `is_error()` | Check if failed |
|
|
263
|
+
| `to_option()` | Convert to Option (discards error info) |
|
|
264
|
+
|
|
265
|
+
## ๐๏ธ Extending the Framework
|
|
266
|
+
|
|
267
|
+
### Creating Custom Filters
|
|
268
|
+
|
|
269
|
+
#### Simple Filter
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
from punctional import Filter
|
|
273
|
+
|
|
274
|
+
class Increment(Filter[int, int]):
|
|
275
|
+
def apply(self, value: int) -> int:
|
|
276
|
+
return value + 1
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### Parameterized Filter
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
class Power(Filter[int, int]):
|
|
283
|
+
def __init__(self, exponent: int):
|
|
284
|
+
self.exponent = exponent
|
|
285
|
+
|
|
286
|
+
def apply(self, value: int) -> int:
|
|
287
|
+
return value ** self.exponent
|
|
288
|
+
|
|
289
|
+
fint(2) | Power(3) # 8
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### Stateful Filter
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
class Accumulator(Filter[int, int]):
|
|
296
|
+
def __init__(self, initial: int = 0):
|
|
297
|
+
self.total = initial
|
|
298
|
+
|
|
299
|
+
def apply(self, value: int) -> int:
|
|
300
|
+
self.total += value
|
|
301
|
+
return self.total
|
|
302
|
+
|
|
303
|
+
acc = Accumulator()
|
|
304
|
+
acc(5) # 5
|
|
305
|
+
acc(10) # 15
|
|
306
|
+
acc(3) # 18
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Functional Dataclasses
|
|
310
|
+
|
|
311
|
+
Make any dataclass functional with the `Functional` mixin:
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
from dataclasses import dataclass
|
|
315
|
+
from punctional import Functional, Filter
|
|
316
|
+
|
|
317
|
+
@dataclass
|
|
318
|
+
class Point(Functional):
|
|
319
|
+
x: float
|
|
320
|
+
y: float
|
|
321
|
+
|
|
322
|
+
class ScalePoint(Filter[Point, Point]):
|
|
323
|
+
def __init__(self, factor: float):
|
|
324
|
+
self.factor = factor
|
|
325
|
+
|
|
326
|
+
def apply(self, point: Point) -> Point:
|
|
327
|
+
return Point(point.x * self.factor, point.y * self.factor)
|
|
328
|
+
|
|
329
|
+
# Now Point supports piping!
|
|
330
|
+
point = Point(3, 4)
|
|
331
|
+
scaled = point | ScalePoint(2.5) # Point(7.5, 10.0)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## ๐ Examples
|
|
335
|
+
|
|
336
|
+
The [examples/](examples/) directory contains comprehensive examples:
|
|
337
|
+
|
|
338
|
+
| File | Description |
|
|
339
|
+
|------|-------------|
|
|
340
|
+
| [basics.py](examples/basics.py) | Basic usage and introduction to all features |
|
|
341
|
+
| [extending.py](examples/extending.py) | Guide to creating custom filters and domain-specific extensions |
|
|
342
|
+
| [data_transformation.py](examples/data_transformation.py) | Advanced patterns: validation pipelines, data transformations |
|
|
343
|
+
| [quick_reference.py](examples/quick_reference.py) | Cheat sheet for quick lookup |
|
|
344
|
+
|
|
345
|
+
### Run the examples:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
python -m examples.basics
|
|
349
|
+
python -m examples.extending
|
|
350
|
+
python -m examples.data_transformation
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## ๐งช Common Patterns
|
|
354
|
+
|
|
355
|
+
### Validation Pipeline
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
from punctional import AndFilter, Functional, Filter
|
|
359
|
+
from dataclasses import dataclass
|
|
360
|
+
|
|
361
|
+
@dataclass
|
|
362
|
+
class Person(Functional):
|
|
363
|
+
name: str
|
|
364
|
+
age: int
|
|
365
|
+
email: str
|
|
366
|
+
|
|
367
|
+
class ValidateName(Filter[Person, bool]):
|
|
368
|
+
def apply(self, person: Person) -> bool:
|
|
369
|
+
return 1 <= len(person.name) <= 100
|
|
370
|
+
|
|
371
|
+
class ValidateAge(Filter[Person, bool]):
|
|
372
|
+
def apply(self, person: Person) -> bool:
|
|
373
|
+
return 0 <= person.age <= 150
|
|
374
|
+
|
|
375
|
+
class ValidateEmail(Filter[Person, bool]):
|
|
376
|
+
def apply(self, person: Person) -> bool:
|
|
377
|
+
return "@" in person.email and "." in person.email
|
|
378
|
+
|
|
379
|
+
# Use the validation pipeline
|
|
380
|
+
person = Person("Alice", 30, "alice@example.com")
|
|
381
|
+
is_valid = person | AndFilter(ValidateName(), ValidateAge(), ValidateEmail()) # True
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Data Transformation Pipeline
|
|
385
|
+
|
|
386
|
+
```python
|
|
387
|
+
class ApplyBonus(Filter[Person, Person]):
|
|
388
|
+
def __init__(self, percentage: float):
|
|
389
|
+
self.percentage = percentage
|
|
390
|
+
|
|
391
|
+
def apply(self, person: Person) -> Person:
|
|
392
|
+
return Person(person.name, person.age, person.email)
|
|
393
|
+
|
|
394
|
+
person | ApplyBonus(10) | PromoteAge() | SaveToDatabase()
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### List Processing Pipeline
|
|
398
|
+
|
|
399
|
+
```python
|
|
400
|
+
from punctional import FilterList, Map, Mult
|
|
401
|
+
|
|
402
|
+
class IsEven(Filter[int, bool]):
|
|
403
|
+
def apply(self, value: int) -> bool:
|
|
404
|
+
return value % 2 == 0
|
|
405
|
+
|
|
406
|
+
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
407
|
+
result = FilterList(IsEven()).apply(numbers) # [2, 4, 6, 8, 10]
|
|
408
|
+
doubled = Map(Mult(2)).apply(result) # [4, 8, 12, 16, 20]
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Error Handling with Result
|
|
412
|
+
|
|
413
|
+
```python
|
|
414
|
+
from punctional import Result, Ok, Error
|
|
415
|
+
|
|
416
|
+
def fetch_user(id: int) -> Result[dict, str]:
|
|
417
|
+
if id < 0:
|
|
418
|
+
return Error("Invalid ID")
|
|
419
|
+
return Ok({"id": id, "name": "Alice"})
|
|
420
|
+
|
|
421
|
+
result = fetch_user(42).map(lambda u: u["name"]).get_or_else("Unknown") # "Alice"
|
|
422
|
+
result = fetch_user(-1).map(lambda u: u["name"]).get_or_else("Unknown") # "Unknown"
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
## ๐ Design Principles
|
|
426
|
+
|
|
427
|
+
1. **Immutability** โ Filters don't modify input; they return new values
|
|
428
|
+
2. **Composability** โ Filters can be combined to create complex transformations
|
|
429
|
+
3. **Type Safety** โ Generic types help catch errors at development time
|
|
430
|
+
4. **Readability** โ Pipe operator makes data flow explicit and easy to follow
|
|
431
|
+
5. **Extensibility** โ Easy to create domain-specific filters
|
|
432
|
+
|
|
433
|
+
## ๐ค Contributing
|
|
434
|
+
|
|
435
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
436
|
+
|
|
437
|
+
## ๐ License
|
|
438
|
+
|
|
439
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
<p align="center">
|
|
444
|
+
Made with โค๏ธ for functional programming enthusiasts
|
|
445
|
+
</p>
|