ima-claude 2.9.0
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.
- package/LICENSE +21 -0
- package/README.md +463 -0
- package/dist/cli.js +1064 -0
- package/package.json +49 -0
- package/platforms/claude/adapter.ts +115 -0
- package/platforms/junie/adapter.ts +254 -0
- package/platforms/junie/agents-template.md +113 -0
- package/platforms/junie/hook-translations.md +84 -0
- package/platforms/shared/detector.ts +27 -0
- package/platforms/shared/installer.ts +202 -0
- package/platforms/shared/types.ts +78 -0
- package/plugins/ima-claude/.claude-plugin/plugin.json +25 -0
- package/plugins/ima-claude/agents/explorer.md +30 -0
- package/plugins/ima-claude/agents/implementer.md +30 -0
- package/plugins/ima-claude/agents/memory.md +42 -0
- package/plugins/ima-claude/agents/reviewer.md +53 -0
- package/plugins/ima-claude/agents/tester.md +33 -0
- package/plugins/ima-claude/agents/wp-developer.md +46 -0
- package/plugins/ima-claude/hooks/README.md +145 -0
- package/plugins/ima-claude/hooks/atlassian_prereqs.py +112 -0
- package/plugins/ima-claude/hooks/block_sed_edits.py +59 -0
- package/plugins/ima-claude/hooks/bootstrap.sh +90 -0
- package/plugins/ima-claude/hooks/bootstrap_utility_check.py +94 -0
- package/plugins/ima-claude/hooks/composer_autoload_check.py +70 -0
- package/plugins/ima-claude/hooks/docs_organization.py +104 -0
- package/plugins/ima-claude/hooks/enforce_rg_over_grep.py +56 -0
- package/plugins/ima-claude/hooks/fp_utility_check.py +90 -0
- package/plugins/ima-claude/hooks/hook_logger.py +69 -0
- package/plugins/ima-claude/hooks/hooks.json +239 -0
- package/plugins/ima-claude/hooks/jira_issue_fetch.py +79 -0
- package/plugins/ima-claude/hooks/jquery_in_wordpress.py +92 -0
- package/plugins/ima-claude/hooks/memory_bootstrap.py +79 -0
- package/plugins/ima-claude/hooks/memory_store_reminder.py +75 -0
- package/plugins/ima-claude/hooks/prompt_coach.py +125 -0
- package/plugins/ima-claude/hooks/prompt_coach_digest.md +48 -0
- package/plugins/ima-claude/hooks/prompt_coach_system.md +30 -0
- package/plugins/ima-claude/hooks/sequential_thinking_check.py +81 -0
- package/plugins/ima-claude/hooks/serena_over_grep.py +96 -0
- package/plugins/ima-claude/hooks/serena_over_read.py +66 -0
- package/plugins/ima-claude/hooks/serena_project_check.py +133 -0
- package/plugins/ima-claude/hooks/sql_injection_check.py +73 -0
- package/plugins/ima-claude/hooks/task_master_after_plan.py +31 -0
- package/plugins/ima-claude/hooks/task_master_before_impl.py +93 -0
- package/plugins/ima-claude/hooks/tavily_extract_advanced.py +48 -0
- package/plugins/ima-claude/hooks/vestige_before_external.py +86 -0
- package/plugins/ima-claude/hooks/webfetch_to_tavily.py +42 -0
- package/plugins/ima-claude/hooks/websearch_to_tavily.py +41 -0
- package/plugins/ima-claude/hooks/wp_security_check.py +150 -0
- package/plugins/ima-claude/personalities/README.md +45 -0
- package/plugins/ima-claude/personalities/enable-40k.md +69 -0
- package/plugins/ima-claude/personalities/enable-templars.md +69 -0
- package/plugins/ima-claude/skills/.research-summary.md +340 -0
- package/plugins/ima-claude/skills/architect/SKILL.md +304 -0
- package/plugins/ima-claude/skills/compound-bridge/SKILL.md +200 -0
- package/plugins/ima-claude/skills/discourse/SKILL.md +440 -0
- package/plugins/ima-claude/skills/discourse-admin/SKILL.md +192 -0
- package/plugins/ima-claude/skills/discourse-admin/references/api-endpoints.md +441 -0
- package/plugins/ima-claude/skills/discourse-admin/references/gotchas.md +107 -0
- package/plugins/ima-claude/skills/discourse-admin/references/staging-defaults.md +98 -0
- package/plugins/ima-claude/skills/discourse-admin/scripts/discourse-admin.py +319 -0
- package/plugins/ima-claude/skills/docs-organize/SKILL.md +254 -0
- package/plugins/ima-claude/skills/docs-organize/templates/active-README.md +50 -0
- package/plugins/ima-claude/skills/docs-organize/templates/archive-README.md +57 -0
- package/plugins/ima-claude/skills/docs-organize/templates/docs-README.md +43 -0
- package/plugins/ima-claude/skills/docs-organize/templates/phase-archive-README.md +83 -0
- package/plugins/ima-claude/skills/docs-organize/templates/section-README.md +48 -0
- package/plugins/ima-claude/skills/docs-organize/templates/transient-README.md +79 -0
- package/plugins/ima-claude/skills/docs-organize/templates/transient-gitignore +9 -0
- package/plugins/ima-claude/skills/ember-discourse/SKILL.md +496 -0
- package/plugins/ima-claude/skills/functional-programmer/SKILL.md +258 -0
- package/plugins/ima-claude/skills/ima-bootstrap/SKILL.md +278 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/bootstrap-patterns.md +356 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/ima-brand.md +273 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/theme-integration.md +212 -0
- package/plugins/ima-claude/skills/ima-brand/SKILL.md +108 -0
- package/plugins/ima-claude/skills/ima-brand/references/brand-identity.md +140 -0
- package/plugins/ima-claude/skills/ima-brand/references/digital-standards.md +180 -0
- package/plugins/ima-claude/skills/ima-brand/references/visual-system.md +173 -0
- package/plugins/ima-claude/skills/ima-forms-expert/SKILL.md +175 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/container-components.md +154 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/examples.md +328 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/field-components.md +298 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/form-factory.md +193 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/quick-reference.md +153 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/validation-engine.md +336 -0
- package/plugins/ima-claude/skills/jira-checkpoint/SKILL.md +178 -0
- package/plugins/ima-claude/skills/jquery/SKILL.md +413 -0
- package/plugins/ima-claude/skills/js-fp/SKILL.md +463 -0
- package/plugins/ima-claude/skills/js-fp/core-principles.md +487 -0
- package/plugins/ima-claude/skills/js-fp/examples/pure-functions.js +260 -0
- package/plugins/ima-claude/skills/js-fp/examples/tests/pure-functions.test.js +262 -0
- package/plugins/ima-claude/skills/js-fp/references/anti-patterns.md +120 -0
- package/plugins/ima-claude/skills/js-fp/references/performance-patterns.md +116 -0
- package/plugins/ima-claude/skills/js-fp/references/testing-patterns.md +134 -0
- package/plugins/ima-claude/skills/js-fp-api/SKILL.md +280 -0
- package/plugins/ima-claude/skills/js-fp-api/examples/crud-endpoint.js +258 -0
- package/plugins/ima-claude/skills/js-fp-api/references/middleware-patterns.md +134 -0
- package/plugins/ima-claude/skills/js-fp-api/references/security-sql.md +110 -0
- package/plugins/ima-claude/skills/js-fp-api/references/validation-patterns.md +165 -0
- package/plugins/ima-claude/skills/js-fp-react/SKILL.md +447 -0
- package/plugins/ima-claude/skills/js-fp-react/examples/ProductCard.tsx +65 -0
- package/plugins/ima-claude/skills/js-fp-react/references/hooks-advanced.md +136 -0
- package/plugins/ima-claude/skills/js-fp-react/references/performance-patterns.md +175 -0
- package/plugins/ima-claude/skills/js-fp-vue/SKILL.md +322 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/complete-examples.md +397 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/composables-advanced.md +282 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/reactivity-patterns.md +348 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/testing.md +314 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/SKILL.md +301 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/ajax-patterns.md +192 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/event-patterns.md +136 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/wp-integration.md +248 -0
- package/plugins/ima-claude/skills/livecanvas/SKILL.md +209 -0
- package/plugins/ima-claude/skills/livecanvas/references/livecanvas-features.md +311 -0
- package/plugins/ima-claude/skills/livecanvas/references/loops-and-logic.md +730 -0
- package/plugins/ima-claude/skills/livecanvas/references/picostrap.md +227 -0
- package/plugins/ima-claude/skills/mcp-atlassian/SKILL.md +339 -0
- package/plugins/ima-claude/skills/mcp-context7/SKILL.md +109 -0
- package/plugins/ima-claude/skills/mcp-memory/SKILL.md +182 -0
- package/plugins/ima-claude/skills/mcp-qdrant/SKILL.md +233 -0
- package/plugins/ima-claude/skills/mcp-sequential/SKILL.md +149 -0
- package/plugins/ima-claude/skills/mcp-serena/SKILL.md +174 -0
- package/plugins/ima-claude/skills/mcp-tavily/SKILL.md +118 -0
- package/plugins/ima-claude/skills/mcp-vestige/SKILL.md +259 -0
- package/plugins/ima-claude/skills/php-authnet/SKILL.md +275 -0
- package/plugins/ima-claude/skills/php-authnet/references/api-reference.md +624 -0
- package/plugins/ima-claude/skills/php-authnet/references/sandbox-testing.md +424 -0
- package/plugins/ima-claude/skills/php-fp/SKILL.md +333 -0
- package/plugins/ima-claude/skills/php-fp/examples/pure-functions.php +403 -0
- package/plugins/ima-claude/skills/php-fp/examples/tests/PureFunctionsTest.php +515 -0
- package/plugins/ima-claude/skills/php-fp/references/core-principles.md +277 -0
- package/plugins/ima-claude/skills/php-fp/references/testing-patterns.md +374 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/SKILL.md +216 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/fp-patterns.md +275 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/plugin-architecture.md +295 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/security-examples.md +203 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/testing-strategy.md +259 -0
- package/plugins/ima-claude/skills/phpunit-wp/SKILL.md +716 -0
- package/plugins/ima-claude/skills/playwright/SKILL.md +434 -0
- package/plugins/ima-claude/skills/playwright/references/accessibility-testing.md +153 -0
- package/plugins/ima-claude/skills/playwright/references/ci-cd.md +268 -0
- package/plugins/ima-claude/skills/playwright/references/network-mocking.md +270 -0
- package/plugins/ima-claude/skills/playwright/references/visual-regression.md +215 -0
- package/plugins/ima-claude/skills/py-fp/SKILL.md +663 -0
- package/plugins/ima-claude/skills/py-fp/examples/pure-functions.py +185 -0
- package/plugins/ima-claude/skills/py-fp/examples/tests/test_pure_functions.py +244 -0
- package/plugins/ima-claude/skills/py-fp/references/core-principles.md +381 -0
- package/plugins/ima-claude/skills/py-fp/references/testing-patterns.md +283 -0
- package/plugins/ima-claude/skills/quasar-fp/SKILL.md +327 -0
- package/plugins/ima-claude/skills/quasar-fp/metadata.json +85 -0
- package/plugins/ima-claude/skills/quasar-fp/references/component-patterns.md +257 -0
- package/plugins/ima-claude/skills/quasar-fp/references/theme-integration.md +233 -0
- package/plugins/ima-claude/skills/quasar-fp/references/utility-classes.md +237 -0
- package/plugins/ima-claude/skills/quickstart/SKILL.md +129 -0
- package/plugins/ima-claude/skills/rails/SKILL.md +359 -0
- package/plugins/ima-claude/skills/resume-session/SKILL.md +68 -0
- package/plugins/ima-claude/skills/rg/SKILL.md +205 -0
- package/plugins/ima-claude/skills/ruby-fp/SKILL.md +336 -0
- package/plugins/ima-claude/skills/save-session/SKILL.md +81 -0
- package/plugins/ima-claude/skills/scorecard/SKILL.md +96 -0
- package/plugins/ima-claude/skills/skill-analyzer/SKILL.md +127 -0
- package/plugins/ima-claude/skills/skill-analyzer/references/advanced-checklist.md +44 -0
- package/plugins/ima-claude/skills/skill-analyzer/references/core-checklist.md +60 -0
- package/plugins/ima-claude/skills/skill-analyzer/scripts/analyze_skill.py +418 -0
- package/plugins/ima-claude/skills/skill-creator/LICENSE.txt +202 -0
- package/plugins/ima-claude/skills/skill-creator/SKILL.md +343 -0
- package/plugins/ima-claude/skills/skill-creator/references/output-patterns.md +82 -0
- package/plugins/ima-claude/skills/skill-creator/references/workflows.md +28 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/plugins/ima-claude/skills/task-master/SKILL.md +51 -0
- package/plugins/ima-claude/skills/task-planner/SKILL.md +228 -0
- package/plugins/ima-claude/skills/task-runner/SKILL.md +192 -0
- package/plugins/ima-claude/skills/unit-testing/SKILL.md +198 -0
- package/plugins/ima-claude/skills/unit-testing/references/mock-patterns.md +181 -0
- package/plugins/ima-claude/skills/unit-testing/references/tdd-workflow.md +177 -0
- package/plugins/ima-claude/skills/unit-testing/references/test-strategy.md +126 -0
- package/plugins/ima-claude/skills/wp-local/SKILL.md +246 -0
- package/plugins/ima-claude/skills/wp-local/references/configuration.md +198 -0
- package/plugins/ima-claude/skills/wp-local/references/wp-cli-reference.md +406 -0
- package/plugins/ima-claude/skills/wp-local/scripts/wp-local.sh +61 -0
|
@@ -0,0 +1,663 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "py-fp"
|
|
3
|
+
description: "Python FP principles with anti-over-engineering focus - Simple > Complex | Evidence > Assumptions"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Python Functional Programming
|
|
7
|
+
|
|
8
|
+
Core functional programming principles for Python with anti-over-engineering enforcement. Python is a multi-paradigm language with solid FP support — but it's not Haskell. This skill teaches you to be functional in spirit, Pythonic in expression.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
- Implementing pure, testable Python functions
|
|
13
|
+
- Data transformation pipelines (pandas, polars, ML preprocessing)
|
|
14
|
+
- Need FP architectural guidance for Python
|
|
15
|
+
- Preventing over-engineering and custom FP utility creation
|
|
16
|
+
- Comprehensive testing strategies with pytest
|
|
17
|
+
- Evidence-based performance optimization
|
|
18
|
+
|
|
19
|
+
## CRITICAL: Anti-Over-Engineering (PRIMARY FOCUS)
|
|
20
|
+
|
|
21
|
+
**Core Principle**: "Simple > Complex | Evidence > Assumptions"
|
|
22
|
+
|
|
23
|
+
> **Clarification**: This skill prevents CREATING custom FP utility functions (pipe, compose, curry, monads) to make Python "feel" like Haskell. Using established libraries (toolz, pandas, itertools, etc.) is perfectly fine. FP is a mindset — pure functions, immutability, composition — not a rigid API signature.
|
|
24
|
+
|
|
25
|
+
### Don't Create Custom FP Utilities
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
# DON'T CREATE: pipe() utility
|
|
29
|
+
def pipe(value, *functions):
|
|
30
|
+
for fn in functions:
|
|
31
|
+
value = fn(value)
|
|
32
|
+
return value
|
|
33
|
+
|
|
34
|
+
# INSTEAD: Native function calls with early returns
|
|
35
|
+
def validate_user(user_data: dict) -> dict:
|
|
36
|
+
required_check = validate_required(["email", "name"], user_data)
|
|
37
|
+
if not required_check["valid"]:
|
|
38
|
+
return required_check
|
|
39
|
+
|
|
40
|
+
email_check = validate_email(user_data)
|
|
41
|
+
if not email_check["valid"]:
|
|
42
|
+
return email_check
|
|
43
|
+
|
|
44
|
+
return validate_name_length(user_data)
|
|
45
|
+
|
|
46
|
+
# DON'T CREATE: compose() utility
|
|
47
|
+
def compose(*fns):
|
|
48
|
+
def composed(x):
|
|
49
|
+
for fn in reversed(fns):
|
|
50
|
+
x = fn(x)
|
|
51
|
+
return x
|
|
52
|
+
return composed
|
|
53
|
+
|
|
54
|
+
# INSTEAD: Direct function calls
|
|
55
|
+
def process_data(raw: dict) -> dict:
|
|
56
|
+
normalized = normalize(raw)
|
|
57
|
+
validated = validate(normalized)
|
|
58
|
+
return transform(validated)
|
|
59
|
+
|
|
60
|
+
# DON'T CREATE: curry() utility
|
|
61
|
+
# INSTEAD: functools.partial or closures
|
|
62
|
+
from functools import partial
|
|
63
|
+
|
|
64
|
+
validate_min_length = partial(validate_length, min_len=3)
|
|
65
|
+
|
|
66
|
+
# Or closures for more complex cases
|
|
67
|
+
def create_validator(rules: list) -> callable:
|
|
68
|
+
def validate(value):
|
|
69
|
+
errors = [r["message"] for r in rules if not r["check"](value)]
|
|
70
|
+
return {"valid": True} if not errors else {"valid": False, "errors": errors}
|
|
71
|
+
return validate
|
|
72
|
+
|
|
73
|
+
# DON'T CREATE: Custom monads (Maybe, Either, IO)
|
|
74
|
+
# INSTEAD: Native error handling with result dicts
|
|
75
|
+
def get_user(user_id: int, db) -> dict:
|
|
76
|
+
try:
|
|
77
|
+
user = db.find(user_id)
|
|
78
|
+
return {"success": True, "data": user}
|
|
79
|
+
except Exception as e:
|
|
80
|
+
return {"success": False, "error": str(e)}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Context-Appropriate Complexity
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# CLI Script: Simple and direct
|
|
87
|
+
def process_file(file_path: str) -> list[str]:
|
|
88
|
+
with open(file_path) as f:
|
|
89
|
+
return [line.upper() for line in f if line.strip()]
|
|
90
|
+
|
|
91
|
+
# Production Service: Appropriate error handling
|
|
92
|
+
def process_file(file_path: str, logger) -> dict:
|
|
93
|
+
try:
|
|
94
|
+
with open(file_path) as f:
|
|
95
|
+
lines = [line.strip() for line in f if line.strip()]
|
|
96
|
+
logger.info("File processed", extra={"path": file_path, "lines": len(lines)})
|
|
97
|
+
return {"success": True, "data": [line.upper() for line in lines]}
|
|
98
|
+
except OSError as e:
|
|
99
|
+
logger.error("File processing failed", extra={"path": file_path, "error": str(e)})
|
|
100
|
+
return {"success": False, "error": str(e)}
|
|
101
|
+
|
|
102
|
+
# Data Pipeline: Composable transformations
|
|
103
|
+
def create_file_processor(transforms: list[callable]):
|
|
104
|
+
compiled = [compile_transform(t) for t in transforms]
|
|
105
|
+
|
|
106
|
+
def process(file_path: str, logger) -> dict:
|
|
107
|
+
with open(file_path) as f:
|
|
108
|
+
data = f.read()
|
|
109
|
+
for transform in compiled:
|
|
110
|
+
data = transform(data)
|
|
111
|
+
return {"success": True, "data": data}
|
|
112
|
+
|
|
113
|
+
return process
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Core FP Patterns (Error-Preventing Essentials)
|
|
117
|
+
|
|
118
|
+
### 1. Purity and Side Effect Isolation
|
|
119
|
+
|
|
120
|
+
**Rule**: Separate business logic from side effects.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
# IMPURE - side effects mixed with logic
|
|
124
|
+
def calculate_total(items: list[dict]) -> float:
|
|
125
|
+
print("Processing items") # Side effect
|
|
126
|
+
global running_total
|
|
127
|
+
running_total += sum(i["price"] for i in items) # Mutation
|
|
128
|
+
return running_total
|
|
129
|
+
|
|
130
|
+
# PURE: business logic
|
|
131
|
+
def calculate_total(items: list[dict]) -> float:
|
|
132
|
+
return sum(item["price"] for item in items)
|
|
133
|
+
|
|
134
|
+
# ISOLATED: side effects separate
|
|
135
|
+
def log_and_calculate(items: list[dict], logger) -> float:
|
|
136
|
+
total = calculate_total(items) # Pure calculation
|
|
137
|
+
logger.info(f"Total: {total}") # Side effect isolated
|
|
138
|
+
return total
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Benefits**:
|
|
142
|
+
- 100% testable with all edge cases
|
|
143
|
+
- Predictable behavior and debugging
|
|
144
|
+
- Safe for `multiprocessing` (pure functions serialize trivially)
|
|
145
|
+
- Enables memoization via `@lru_cache`
|
|
146
|
+
|
|
147
|
+
### 2. Composition Over Inheritance
|
|
148
|
+
|
|
149
|
+
**Rule**: Build complex behavior from simple functions.
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
# Class hierarchy approach (avoid)
|
|
153
|
+
class BaseValidator:
|
|
154
|
+
def validate(self, value): raise NotImplementedError
|
|
155
|
+
|
|
156
|
+
class EmailValidator(BaseValidator):
|
|
157
|
+
def validate(self, value): ...
|
|
158
|
+
|
|
159
|
+
# Function composition (no utilities needed)
|
|
160
|
+
def validate_required(value) -> bool:
|
|
161
|
+
return value is not None and value != ""
|
|
162
|
+
|
|
163
|
+
def validate_email(value: str) -> bool:
|
|
164
|
+
return "@" in value and "." in value.split("@")[-1]
|
|
165
|
+
|
|
166
|
+
def validate_length(min_len: int, max_len: int):
|
|
167
|
+
return lambda value: min_len <= len(value) <= max_len
|
|
168
|
+
|
|
169
|
+
# Simple composition without pipe() utility
|
|
170
|
+
def validate_user_email(email: str) -> dict:
|
|
171
|
+
if not validate_required(email):
|
|
172
|
+
return {"valid": False, "error": "Required"}
|
|
173
|
+
if not validate_email(email):
|
|
174
|
+
return {"valid": False, "error": "Invalid email"}
|
|
175
|
+
if not validate_length(5, 100)(email):
|
|
176
|
+
return {"valid": False, "error": "Length"}
|
|
177
|
+
return {"valid": True}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 3. Dependency Injection Through Parameters
|
|
181
|
+
|
|
182
|
+
**Rule**: Pass dependencies explicitly, avoid global state.
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
# Hidden dependencies, hard to test
|
|
186
|
+
def save_user(user_data: dict) -> dict:
|
|
187
|
+
hashed = bcrypt.hash(user_data["password"]) # Hidden
|
|
188
|
+
return database.save({**user_data, "password": hashed}) # Hidden
|
|
189
|
+
|
|
190
|
+
# Explicit dependencies, fully testable
|
|
191
|
+
def save_user(user_data: dict, hasher, database) -> dict:
|
|
192
|
+
hashed = hasher.hash(user_data["password"])
|
|
193
|
+
return database.save({**user_data, "password": hashed})
|
|
194
|
+
|
|
195
|
+
# Factory for repeated use (closure-based DI)
|
|
196
|
+
def create_user_service(hasher, database):
|
|
197
|
+
def save(user_data: dict) -> dict:
|
|
198
|
+
return save_user(user_data, hasher, database)
|
|
199
|
+
|
|
200
|
+
def find(user_id: int) -> dict:
|
|
201
|
+
return database.find_by_id(user_id)
|
|
202
|
+
|
|
203
|
+
return {"save": save, "find": find}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### 4. Immutability Patterns
|
|
207
|
+
|
|
208
|
+
**Rule**: Don't mutate input data. Create new structures.
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from dataclasses import dataclass, replace, field
|
|
212
|
+
from typing import NamedTuple
|
|
213
|
+
|
|
214
|
+
# Frozen dataclasses — the primary immutable record
|
|
215
|
+
@dataclass(frozen=True)
|
|
216
|
+
class User:
|
|
217
|
+
name: str
|
|
218
|
+
email: str
|
|
219
|
+
settings: dict = field(default_factory=dict)
|
|
220
|
+
|
|
221
|
+
# "Update" via replace() — returns new instance
|
|
222
|
+
def update_email(user: User, new_email: str) -> User:
|
|
223
|
+
return replace(user, email=new_email)
|
|
224
|
+
|
|
225
|
+
# NamedTuple — lighter weight alternative
|
|
226
|
+
class Point(NamedTuple):
|
|
227
|
+
x: float
|
|
228
|
+
y: float
|
|
229
|
+
|
|
230
|
+
def translate(point: Point, dx: float, dy: float) -> Point:
|
|
231
|
+
return Point(point.x + dx, point.y + dy)
|
|
232
|
+
|
|
233
|
+
# Dict immutability — spread with {**d}
|
|
234
|
+
def update_settings(user: dict, settings: dict) -> dict:
|
|
235
|
+
return {**user, "settings": {**user.get("settings", {}), **settings}}
|
|
236
|
+
|
|
237
|
+
# List operations without mutation
|
|
238
|
+
def add_item(items: list, new_item) -> list:
|
|
239
|
+
return [*items, new_item]
|
|
240
|
+
|
|
241
|
+
def remove_item(items: list[dict], item_id: int) -> list[dict]:
|
|
242
|
+
return [i for i in items if i["id"] != item_id]
|
|
243
|
+
|
|
244
|
+
def update_item(items: list[dict], item_id: int, updates: dict) -> list[dict]:
|
|
245
|
+
return [{**i, **updates} if i["id"] == item_id else i for i in items]
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
> **Important**: `frozen=True` is shallow — it prevents attribute reassignment but doesn't freeze mutable attribute values (like dicts or lists inside). For deep immutability, use `pyrsistent` or keep nested values as tuples/frozensets.
|
|
249
|
+
|
|
250
|
+
## Python-Specific Patterns
|
|
251
|
+
|
|
252
|
+
### Comprehensions Over map/filter (Pythonic FP)
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
# map/filter style (less Pythonic)
|
|
256
|
+
active_names = list(map(
|
|
257
|
+
lambda u: u["name"],
|
|
258
|
+
filter(lambda u: u["active"], users)
|
|
259
|
+
))
|
|
260
|
+
|
|
261
|
+
# Comprehension style (Pythonic FP)
|
|
262
|
+
active_names = [u["name"] for u in users if u["active"]]
|
|
263
|
+
|
|
264
|
+
# Dict comprehension for transformations
|
|
265
|
+
name_by_id = {u["id"]: u["name"] for u in users}
|
|
266
|
+
|
|
267
|
+
# Generator expression for lazy evaluation (large data)
|
|
268
|
+
totals = sum(item["price"] for item in items if item["taxable"])
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Generators for Lazy Pipelines
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
# Generators are Python's answer to lazy evaluation
|
|
275
|
+
def read_records(path: str):
|
|
276
|
+
with open(path) as f:
|
|
277
|
+
for line in f:
|
|
278
|
+
yield parse_record(line)
|
|
279
|
+
|
|
280
|
+
def filter_valid(records):
|
|
281
|
+
for record in records:
|
|
282
|
+
if record["status"] == "active":
|
|
283
|
+
yield record
|
|
284
|
+
|
|
285
|
+
def transform(records):
|
|
286
|
+
for record in records:
|
|
287
|
+
yield {**record, "name": record["name"].upper()}
|
|
288
|
+
|
|
289
|
+
# Compose lazily — no intermediate lists
|
|
290
|
+
pipeline = transform(filter_valid(read_records("data.csv")))
|
|
291
|
+
results = list(pipeline) # Materializes only when needed
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### itertools and functools (The FP Standard Library)
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
from functools import partial, lru_cache, reduce
|
|
298
|
+
from itertools import chain, groupby, islice, takewhile
|
|
299
|
+
from operator import itemgetter
|
|
300
|
+
|
|
301
|
+
# partial — the Pythonic way to "curry"
|
|
302
|
+
multiply_by_tax = partial(lambda rate, price: price * (1 + rate), 0.08)
|
|
303
|
+
|
|
304
|
+
# lru_cache — memoize pure functions (MUST be pure!)
|
|
305
|
+
@lru_cache(maxsize=256)
|
|
306
|
+
def fibonacci(n: int) -> int:
|
|
307
|
+
if n < 2:
|
|
308
|
+
return n
|
|
309
|
+
return fibonacci(n - 1) + fibonacci(n - 2)
|
|
310
|
+
|
|
311
|
+
# operator module — eliminate trivial lambdas
|
|
312
|
+
from operator import itemgetter, attrgetter
|
|
313
|
+
|
|
314
|
+
sorted_by_name = sorted(users, key=itemgetter("name"))
|
|
315
|
+
# vs: sorted(users, key=lambda u: u["name"])
|
|
316
|
+
|
|
317
|
+
# itertools for complex transformations
|
|
318
|
+
from itertools import groupby
|
|
319
|
+
|
|
320
|
+
def group_by_category(items: list[dict]) -> dict:
|
|
321
|
+
sorted_items = sorted(items, key=itemgetter("category"))
|
|
322
|
+
return {
|
|
323
|
+
k: list(v)
|
|
324
|
+
for k, v in groupby(sorted_items, key=itemgetter("category"))
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Pattern Matching (Python 3.10+)
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
# Structural pattern matching for clean dispatch
|
|
332
|
+
def process_event(event: dict) -> dict:
|
|
333
|
+
match event:
|
|
334
|
+
case {"type": "click", "target": target}:
|
|
335
|
+
return handle_click(target)
|
|
336
|
+
case {"type": "submit", "data": data}:
|
|
337
|
+
return handle_submit(data)
|
|
338
|
+
case {"type": "error", "code": code, "message": msg}:
|
|
339
|
+
return handle_error(code, msg)
|
|
340
|
+
case _:
|
|
341
|
+
return {"error": f"Unknown event type: {event.get('type')}"}
|
|
342
|
+
|
|
343
|
+
# Pattern matching with guards
|
|
344
|
+
def categorize_score(score: int) -> str:
|
|
345
|
+
match score:
|
|
346
|
+
case n if n >= 90: return "A"
|
|
347
|
+
case n if n >= 80: return "B"
|
|
348
|
+
case n if n >= 70: return "C"
|
|
349
|
+
case _: return "F"
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Type Hints for FP Clarity
|
|
353
|
+
|
|
354
|
+
```python
|
|
355
|
+
from typing import TypeVar, Callable, Optional
|
|
356
|
+
from collections.abc import Iterable, Iterator
|
|
357
|
+
|
|
358
|
+
T = TypeVar("T")
|
|
359
|
+
U = TypeVar("U")
|
|
360
|
+
|
|
361
|
+
# Type hints make pure function contracts explicit
|
|
362
|
+
def transform_all(items: Iterable[T], fn: Callable[[T], U]) -> list[U]:
|
|
363
|
+
return [fn(item) for item in items]
|
|
364
|
+
|
|
365
|
+
# Union types (3.10+) for result patterns
|
|
366
|
+
def divide(a: float, b: float) -> dict:
|
|
367
|
+
if b == 0:
|
|
368
|
+
return {"success": False, "error": "Division by zero"}
|
|
369
|
+
return {"success": True, "data": a / b}
|
|
370
|
+
|
|
371
|
+
# Optional signals nullable return
|
|
372
|
+
def find_user(users: list[dict], user_id: int) -> Optional[dict]:
|
|
373
|
+
return next((u for u in users if u["id"] == user_id), None)
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## Data Science / ML Patterns
|
|
377
|
+
|
|
378
|
+
### pandas pipe() for Transformation Chains
|
|
379
|
+
|
|
380
|
+
```python
|
|
381
|
+
import pandas as pd
|
|
382
|
+
|
|
383
|
+
# Each function: DataFrame in, DataFrame out (pure transforms)
|
|
384
|
+
def clean_nulls(df: pd.DataFrame) -> pd.DataFrame:
|
|
385
|
+
return df.dropna(subset=["email", "name"])
|
|
386
|
+
|
|
387
|
+
def normalize_names(df: pd.DataFrame) -> pd.DataFrame:
|
|
388
|
+
return df.assign(name=df["name"].str.strip().str.title())
|
|
389
|
+
|
|
390
|
+
def add_age_group(df: pd.DataFrame) -> pd.DataFrame:
|
|
391
|
+
bins = [0, 18, 35, 55, 120]
|
|
392
|
+
labels = ["youth", "young_adult", "adult", "senior"]
|
|
393
|
+
return df.assign(age_group=pd.cut(df["age"], bins=bins, labels=labels))
|
|
394
|
+
|
|
395
|
+
# Compose via pipe() — FP pipeline, Pythonic syntax
|
|
396
|
+
result = (
|
|
397
|
+
raw_df
|
|
398
|
+
.pipe(clean_nulls)
|
|
399
|
+
.pipe(normalize_names)
|
|
400
|
+
.pipe(add_age_group)
|
|
401
|
+
)
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Pure Feature Engineering
|
|
405
|
+
|
|
406
|
+
```python
|
|
407
|
+
# Pure transformation functions for ML pipelines
|
|
408
|
+
def extract_features(df: pd.DataFrame) -> pd.DataFrame:
|
|
409
|
+
return df.assign(
|
|
410
|
+
word_count=df["text"].str.split().str.len(),
|
|
411
|
+
has_url=df["text"].str.contains(r"https?://", regex=True),
|
|
412
|
+
text_length=df["text"].str.len(),
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
# scikit-learn FunctionTransformer for pipeline integration
|
|
416
|
+
from sklearn.preprocessing import FunctionTransformer
|
|
417
|
+
from sklearn.pipeline import Pipeline
|
|
418
|
+
|
|
419
|
+
feature_extractor = FunctionTransformer(extract_features)
|
|
420
|
+
|
|
421
|
+
pipeline = Pipeline([
|
|
422
|
+
("features", feature_extractor),
|
|
423
|
+
("scaler", StandardScaler()),
|
|
424
|
+
("model", LogisticRegression()),
|
|
425
|
+
])
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Polars (FP-Friendly Alternative to pandas)
|
|
429
|
+
|
|
430
|
+
```python
|
|
431
|
+
import polars as pl
|
|
432
|
+
|
|
433
|
+
# Polars expressions are lazy and composable by design
|
|
434
|
+
result = (
|
|
435
|
+
pl.scan_csv("data.csv") # Lazy — nothing runs yet
|
|
436
|
+
.filter(pl.col("status") == "active")
|
|
437
|
+
.with_columns(
|
|
438
|
+
full_name=pl.col("first_name") + " " + pl.col("last_name"),
|
|
439
|
+
age_group=pl.when(pl.col("age") < 30).then(pl.lit("young"))
|
|
440
|
+
.otherwise(pl.lit("senior")),
|
|
441
|
+
)
|
|
442
|
+
.group_by("department")
|
|
443
|
+
.agg(pl.col("salary").mean().alias("avg_salary"))
|
|
444
|
+
.collect() # Execute the entire plan at once
|
|
445
|
+
)
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## Result Type Pattern
|
|
449
|
+
|
|
450
|
+
```python
|
|
451
|
+
# Standard result shape — consistent across the codebase
|
|
452
|
+
def success(data=None) -> dict:
|
|
453
|
+
return {"success": True, "data": data}
|
|
454
|
+
|
|
455
|
+
def failure(error: str) -> dict:
|
|
456
|
+
return {"success": False, "error": error}
|
|
457
|
+
|
|
458
|
+
# Chain results with early return
|
|
459
|
+
def process_order(order: dict) -> dict:
|
|
460
|
+
validated = validate_order(order)
|
|
461
|
+
if not validated["success"]:
|
|
462
|
+
return validated
|
|
463
|
+
|
|
464
|
+
priced = calculate_pricing(validated["data"])
|
|
465
|
+
if not priced["success"]:
|
|
466
|
+
return priced
|
|
467
|
+
|
|
468
|
+
return submit_order(priced["data"])
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
## Testing Essentials (Enabled by Purity)
|
|
472
|
+
|
|
473
|
+
**Philosophy**: Pure functions enable testing all edge cases systematically.
|
|
474
|
+
|
|
475
|
+
```python
|
|
476
|
+
import pytest
|
|
477
|
+
|
|
478
|
+
# Parametrized testing — the FP way
|
|
479
|
+
@pytest.mark.parametrize("price,rate,expected", [
|
|
480
|
+
(100.0, 0.1, 10.0),
|
|
481
|
+
(50.0, 0.2, 10.0),
|
|
482
|
+
(0.0, 0.1, 0.0),
|
|
483
|
+
(100.0, 0.0, 0.0),
|
|
484
|
+
])
|
|
485
|
+
def test_calculate_discount(price, rate, expected):
|
|
486
|
+
assert calculate_discount(price, rate) == expected
|
|
487
|
+
|
|
488
|
+
# Edge case testing — pure functions handle all inputs
|
|
489
|
+
@pytest.mark.parametrize("invalid_input", [None, "", [], {}, 0])
|
|
490
|
+
def test_validate_required_rejects_invalid(invalid_input):
|
|
491
|
+
assert not validate_required(invalid_input)
|
|
492
|
+
|
|
493
|
+
# Testing result chains
|
|
494
|
+
def test_process_order_validation_failure():
|
|
495
|
+
result = process_order({"items": []})
|
|
496
|
+
assert not result["success"]
|
|
497
|
+
assert "empty" in result["error"].lower()
|
|
498
|
+
|
|
499
|
+
# Frozen dataclass testability
|
|
500
|
+
def test_update_email_returns_new_instance():
|
|
501
|
+
user = User(name="Alice", email="old@test.com")
|
|
502
|
+
updated = update_email(user, "new@test.com")
|
|
503
|
+
assert updated.email == "new@test.com"
|
|
504
|
+
assert user.email == "old@test.com" # Original unchanged
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
## Performance Patterns (Evidence-Based)
|
|
508
|
+
|
|
509
|
+
**IMPORTANT**: Optimize only when needed with evidence.
|
|
510
|
+
|
|
511
|
+
### Memoization for Expensive Pure Functions
|
|
512
|
+
|
|
513
|
+
```python
|
|
514
|
+
from functools import lru_cache
|
|
515
|
+
|
|
516
|
+
# ONLY memoize pure functions with hashable arguments
|
|
517
|
+
@lru_cache(maxsize=128)
|
|
518
|
+
def parse_template(template: str) -> dict:
|
|
519
|
+
# Expensive parsing — cached after first call
|
|
520
|
+
return heavy_parse(template)
|
|
521
|
+
|
|
522
|
+
# For unhashable args, manually cache
|
|
523
|
+
_cache = {}
|
|
524
|
+
def get_config(key: str) -> dict:
|
|
525
|
+
if key not in _cache:
|
|
526
|
+
_cache[key] = load_config(key)
|
|
527
|
+
return _cache[key]
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Generator Pipelines for Large Data
|
|
531
|
+
|
|
532
|
+
```python
|
|
533
|
+
# Memory-efficient: processes one record at a time
|
|
534
|
+
def process_large_file(path: str):
|
|
535
|
+
records = read_records(path) # Generator
|
|
536
|
+
valid = filter_valid(records) # Generator
|
|
537
|
+
transformed = transform(valid) # Generator
|
|
538
|
+
|
|
539
|
+
# Only materializes in batches
|
|
540
|
+
batch = []
|
|
541
|
+
for record in transformed:
|
|
542
|
+
batch.append(record)
|
|
543
|
+
if len(batch) >= 1000:
|
|
544
|
+
yield batch
|
|
545
|
+
batch = []
|
|
546
|
+
if batch:
|
|
547
|
+
yield batch
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### multiprocessing with Pure Functions
|
|
551
|
+
|
|
552
|
+
```python
|
|
553
|
+
from multiprocessing import Pool
|
|
554
|
+
|
|
555
|
+
# Pure functions parallelize trivially
|
|
556
|
+
def process_item(item: dict) -> dict:
|
|
557
|
+
return {**item, "score": compute_score(item)}
|
|
558
|
+
|
|
559
|
+
# No shared state, no locks, no bugs
|
|
560
|
+
with Pool(4) as pool:
|
|
561
|
+
results = pool.map(process_item, items)
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
## Python-Specific Gotchas
|
|
565
|
+
|
|
566
|
+
### Recursion Limit
|
|
567
|
+
|
|
568
|
+
```python
|
|
569
|
+
# Python has a 1000-call recursion limit. No tail call optimization.
|
|
570
|
+
# DON'T: recursive algorithms for large inputs
|
|
571
|
+
def factorial(n):
|
|
572
|
+
return 1 if n <= 1 else n * factorial(n - 1) # Blows up at n > 1000
|
|
573
|
+
|
|
574
|
+
# DO: iterative or reduce
|
|
575
|
+
from functools import reduce
|
|
576
|
+
from operator import mul
|
|
577
|
+
|
|
578
|
+
def factorial(n: int) -> int:
|
|
579
|
+
return reduce(mul, range(1, n + 1), 1)
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
### Mutable Default Arguments
|
|
583
|
+
|
|
584
|
+
```python
|
|
585
|
+
# NEVER use mutable defaults
|
|
586
|
+
def add_item(item, items=[]): # BUG: shared mutable default
|
|
587
|
+
items.append(item)
|
|
588
|
+
return items
|
|
589
|
+
|
|
590
|
+
# INSTEAD: None sentinel
|
|
591
|
+
def add_item(item, items=None):
|
|
592
|
+
items = items if items is not None else []
|
|
593
|
+
return [*items, item] # Return new list, don't mutate
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### Shallow vs Deep Copy
|
|
597
|
+
|
|
598
|
+
```python
|
|
599
|
+
# frozen=True is SHALLOW
|
|
600
|
+
@dataclass(frozen=True)
|
|
601
|
+
class Config:
|
|
602
|
+
settings: dict # This dict is still mutable!
|
|
603
|
+
|
|
604
|
+
config = Config(settings={"debug": True})
|
|
605
|
+
# config.settings = {} # FrozenInstanceError
|
|
606
|
+
config.settings["debug"] = False # This WORKS — shallow freeze
|
|
607
|
+
|
|
608
|
+
# For deep immutability: use tuples, frozensets, or pyrsistent
|
|
609
|
+
from types import MappingProxyType
|
|
610
|
+
|
|
611
|
+
def freeze_dict(d: dict):
|
|
612
|
+
return MappingProxyType(d) # Read-only view
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
## Quality Gates (Pre-Implementation Checklist)
|
|
616
|
+
|
|
617
|
+
1. **"Can this be pure?"** - Separate business logic from side effects
|
|
618
|
+
2. **"Can this use native patterns?"** - Comprehensions, generators, functools, itertools
|
|
619
|
+
3. **"Can this be simplified?"** - Choose simple solution over complex abstraction
|
|
620
|
+
4. **"Is this complexity justified?"** - Evidence-based complexity decisions
|
|
621
|
+
5. **"Is this testable?"** - Pure functions enable comprehensive testing
|
|
622
|
+
6. **"Are type hints used?"** - Type hints on all public function signatures
|
|
623
|
+
|
|
624
|
+
## When to Load Reference Files
|
|
625
|
+
|
|
626
|
+
### Deep Principles and Explanations
|
|
627
|
+
**File**: `references/core-principles.md`
|
|
628
|
+
**Load when**:
|
|
629
|
+
- Learning mode or explaining WHY behind patterns
|
|
630
|
+
- Making architectural decisions
|
|
631
|
+
- Need complete Result Type patterns
|
|
632
|
+
- Anti-pattern recognition details
|
|
633
|
+
- Python-specific FP philosophy deep-dive
|
|
634
|
+
|
|
635
|
+
### Testing Methodology
|
|
636
|
+
**File**: `references/testing-patterns.md`
|
|
637
|
+
**Load when**:
|
|
638
|
+
- Building comprehensive test suites with pytest
|
|
639
|
+
- Improving test coverage
|
|
640
|
+
- Edge case analysis and boundary testing
|
|
641
|
+
- Testing data pipelines and ML code
|
|
642
|
+
- Property-based testing with Hypothesis
|
|
643
|
+
|
|
644
|
+
### Working Examples
|
|
645
|
+
**Directory**: `examples/`
|
|
646
|
+
**Load when**:
|
|
647
|
+
- Need complete working code
|
|
648
|
+
- Integration examples
|
|
649
|
+
- Learning implementation patterns
|
|
650
|
+
|
|
651
|
+
## Integration with Domain Skills
|
|
652
|
+
|
|
653
|
+
This core skill provides the foundation for:
|
|
654
|
+
|
|
655
|
+
- **py-fp-django**: Django patterns with FP principles (future)
|
|
656
|
+
- **py-fp-fastapi**: FastAPI patterns with FP principles (future)
|
|
657
|
+
- **py-fp-ml**: ML/data science patterns with FP principles (future)
|
|
658
|
+
|
|
659
|
+
Each domain skill references this core and adds domain-specific patterns.
|
|
660
|
+
|
|
661
|
+
## Philosophy
|
|
662
|
+
|
|
663
|
+
*"Pure functions, Pythonic patterns, type hints, appropriate complexity, and comprehensive testing for maintainable, predictable code. Be functional in spirit, Pythonic in expression — don't fight the language, work with it."*
|