
Product
Introducing Webhook Events for Alert Changes
Add real-time Socket webhook events to your workflows to automatically receive software supply chain alert changes in real time.
pygraham
Advanced tools
A high-performance functional programming library for Python that brings truly functional programming capabilities with significant performance improvements over vanilla Python and other functional libraries.
Python is wonderful, but it lacks true functional programming features. PyGraham fills this gap by providing:
pip install pygraham
def process_data(data):
if data is None:
return None
result = transform(data)
if result is None:
return None
validated = validate(result)
if validated is None:
return None
return save(validated)
from pygraham import Maybe, pipe
process_data = pipe(
Maybe.of,
lambda m: m.flat_map(transform),
lambda m: m.flat_map(validate),
lambda m: m.flat_map(save)
)
# Or even cleaner:
result = (Maybe.of(data)
.flat_map(transform)
.flat_map(validate)
.flat_map(save)
.get_or_else(default_value))
from pygraham import ImmutableList, ImmutableDict
# Lists
numbers = ImmutableList.of(1, 2, 3, 4, 5)
doubled = numbers.map(lambda x: x * 2)
evens = numbers.filter(lambda x: x % 2 == 0)
total = numbers.reduce(lambda acc, x: acc + x, 0)
# Original unchanged!
assert list(numbers) == [1, 2, 3, 4, 5]
# Chaining operations
result = (ImmutableList.of(1, 2, 3, 4, 5)
.filter(lambda x: x % 2 == 0)
.map(lambda x: x * 2)
.reverse())
# [8, 4]
# Dictionaries
config = ImmutableDict.of(debug=True, port=8080)
new_config = config.set("host", "localhost").set("debug", False)
# Original unchanged!
from pygraham import compose, pipe, curry
# Compose (right to left)
add_one = lambda x: x + 1
double = lambda x: x * 2
f = compose(double, add_one)
f(3) # (3 + 1) * 2 = 8
# Pipe (left to right)
g = pipe(add_one, double)
g(3) # (3 + 1) * 2 = 8
# Curry
@curry
def add_three(a, b, c):
return a + b + c
add_three(1)(2)(3) # 6
add_three(1, 2)(3) # 6
add_three(1)(2, 3) # 6
from pygraham import LazySequence
# Process infinite sequences efficiently
result = (LazySequence.infinite(1)
.filter(lambda x: x % 2 == 0)
.map(lambda x: x * 2)
.take(5)
.to_list())
# [4, 8, 12, 16, 20]
# Only computes what's needed!
large_data = LazySequence.from_iterable(range(1_000_000))
result = large_data.filter(lambda x: x % 100 == 0).take(10).to_list()
# Only processes 1000 elements, not 1 million!
from pygraham import match, case, _
def classify_number(n):
return match(n,
case(0, lambda x: "zero"),
case(lambda x: x < 0, lambda x: "negative"),
case(lambda x: x < 10, lambda x: "small"),
case(lambda x: x < 100, lambda x: "medium"),
case(_, lambda x: "large")
)
classify_number(5) # "small"
classify_number(50) # "medium"
classify_number(500) # "large"
# Type-based matching
result = match("hello",
case(int, lambda x: f"integer: {x}"),
case(str, lambda x: f"string: {x}"),
case(_, lambda x: "unknown")
)
# "string: hello"
from pygraham import Either, Left, Right
def divide(a, b):
if b == 0:
return Left("Division by zero")
return Right(a / b)
result = (divide(10, 2)
.map(lambda x: x * 2)
.map(lambda x: x + 1)
.fold(
lambda error: f"Error: {error}",
lambda value: f"Result: {value}"
))
# "Result: 11.0"
# Chaining operations
result = (Right(10)
.flat_map(lambda x: divide(x, 2))
.flat_map(lambda x: divide(x, 0)) # Error here
.fold(
lambda error: f"Error: {error}",
lambda value: f"Result: {value}"
))
# "Error: Division by zero"
PyGraham includes C++ extensions for performance-critical operations, providing significant speedups over vanilla Python.
Processing 10,000 transactions with filtering, mapping, and aggregation:
| Implementation | Time (ms) | Speedup |
|---|---|---|
| Vanilla Python | 2.45 | 1.0x |
| PyGraham | 1.87 | 1.31x faster |
Processing lists with map, filter, reduce operations:
| Operation | Vanilla Python | PyGraham | Speedup |
|---|---|---|---|
| Map (100k items) | 8.2ms | 5.1ms | 1.61x |
| Filter (100k items) | 7.8ms | 4.9ms | 1.59x |
| Reduce (100k items) | 6.5ms | 3.8ms | 1.71x |
See examples/tsp_comparison.py for a complete before/after comparison.
Vanilla Python (Imperative):
def find_shortest_route_vanilla(cities):
best_route = None
best_distance = float('inf')
for perm in permutations(range(len(cities))):
route = list(perm)
dist = calculate_distance(cities, route)
if dist < best_distance:
best_distance = dist
best_route = route
return best_route, best_distance
PyGraham (Functional):
def find_shortest_route_fp(cities):
return (LazySequence.from_iterable(permutations(range(len(cities))))
.map(lambda perm: ImmutableList(perm))
.map(lambda route: (route, calculate_distance(cities, route)))
.reduce(lambda best, current:
current if current[1] < best[1] else best,
(None, float('inf'))))
Results:
Handle optional values without null checks:
from pygraham import Maybe, Just, Nothing
# Safe dictionary access
def get_user_age(users, user_id):
return (Maybe.of(users.get(user_id))
.map(lambda user: user.get('age'))
.filter(lambda age: age >= 0)
.get_or_else(0))
# Safe computation chain
result = (Just(5)
.map(lambda x: x * 2)
.filter(lambda x: x > 8)
.map(lambda x: x + 1)
.get_or_else(0))
# 11
Efficient immutable data structures with structural sharing:
from pygraham import ImmutableList, ImmutableDict
# Lists support all functional operations
numbers = ImmutableList.of(1, 2, 3, 4, 5)
processed = (numbers
.filter(lambda x: x % 2 == 0)
.map(lambda x: x ** 2)
.sort(reverse=True))
# Dictionaries are immutable too
user = ImmutableDict.of(name="Alice", age=30, city="NYC")
updated_user = user.set("age", 31).set("country", "USA")
# Original user unchanged
Process large datasets efficiently:
from pygraham import LazySequence
# Infinite sequences
fibonacci = (LazySequence.infinite(0)
.scan(lambda acc, _: acc + 1, 0)
.take(10)
.to_list())
# Large file processing (only loads needed lines)
result = (LazySequence.from_iterable(open('huge_file.txt'))
.filter(lambda line: 'ERROR' in line)
.take(10)
.to_list())
Expressive pattern matching for complex logic:
from pygraham import match, case, _, instance_of, in_range
def handle_response(response):
return match(response.status_code,
case(200, lambda _: "Success"),
case(404, lambda _: "Not Found"),
case(in_range(400, 499), lambda _: "Client Error"),
case(in_range(500, 599), lambda _: "Server Error"),
case(_, lambda code: f"Unknown: {code}")
)
Check out the examples/ directory for comprehensive examples:
Learn each concept from the ground up with simple, clear examples:
01_maybe_basics.py: Maybe monad fundamentals - handling optional values without None checks02_either_basics.py: Either monad fundamentals - explicit error handling without exceptions03_immutable_list_basics.py: ImmutableList fundamentals - working with lists that never change04_compose_pipe_curry_basics.py: Function composition fundamentals - building complex operations from simple functions05_pattern_matching_basics.py: Pattern matching fundamentals - elegant alternatives to if-elif chainsReal-world comparisons showing functional programming advantages:
file_system_tree.py: File system tree traversal comparing OOP visitor pattern vs Functional recursion - showcases the elegance of recursive solutions, catamorphisms (fold), and pattern matching for hierarchical databanking_transactions.py: Banking transaction processing comparing OOP vs Functional approaches - demonstrates the power of immutability, monads, and pure functionstsp_comparison.py: Travelling Salesman Problem solved with vanilla Python vs PyGrahamdata_pipeline.py: Complex data processing pipeline showcasing all featuresRun examples:
# Basic tutorials
python examples/01_maybe_basics.py
python examples/02_either_basics.py
python examples/03_immutable_list_basics.py
python examples/04_compose_pipe_curry_basics.py
python examples/05_pattern_matching_basics.py
# Advanced examples
python examples/file_system_tree.py
python examples/banking_transactions.py
python examples/tsp_comparison.py
python examples/data_pipeline.py
git clone https://github.com/Bernardi-sh/pygraham.git
cd pygraham
pip install -e ".[dev]"
pytest tests/ -v
PyGraham uses SonarQube for continuous code quality analysis. The project is analyzed for:
To set up SonarQube integration, see SONARQUBE_SETUP.md.
The C++ extensions are optional but provide significant performance improvements:
pip install pybind11
python setup.py build_ext --inplace
Named in honor of Paul Graham, a pioneer in functional programming and the creator of Arc, who has advocated for the power of functional programming in software development.
| Feature | PyGraham | fn.py | toolz | PyFunctional |
|---|---|---|---|---|
| Monads | ✅ | ❌ | ❌ | ❌ |
| Immutable Collections | ✅ | ❌ | ✅ | ❌ |
| Pattern Matching | ✅ | ❌ | ❌ | ❌ |
| Lazy Evaluation | âś… | âś… | âś… | âś… |
| C++ Extensions | ✅ | ❌ | ✅ | ❌ |
| Type Hints | ✅ | ❌ | ✅ | ❌ |
| Active Development | ✅ | ❌ | ✅ | ❌ |
Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ for functional programming enthusiasts
FAQs
A high-performance functional programming library for Python
We found that pygraham demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Add real-time Socket webhook events to your workflows to automatically receive software supply chain alert changes in real time.

Security News
ENISA has become a CVE Program Root, giving the EU a central authority for coordinating vulnerability reporting, disclosure, and cross-border response.

Product
Socket now scans OpenVSX extensions, giving teams early detection of risky behaviors, hidden capabilities, and supply chain threats in developer tools.