darkcore


Namedarkcore JSON
Version 0.4.0 PyPI version JSON
download
home_pagehttps://github.com/minamorl/darkcore
SummaryPractical functional programming primitives for Python: Monads, Transformers, and DSL operators for safe business logic
upload_time2025-08-22 19:40:58
maintainerNone
docs_urlNone
authorminamorl
requires_python<4.0,>=3.12
licenseMIT
keywords monad functional dsl business logic
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # darkcore

**darkcore** is a lightweight functional programming toolkit for Python.  
It brings **Functor / Applicative / Monad** abstractions, classic monads like **Maybe, Either/Result, Reader, Writer, State**,  
and an expressive **operator DSL** (`|`, `>>`, `@`) that makes Python feel almost like Haskell.

---

## ✨ Features

- Functor / Applicative / Monad base abstractions
- Core monads implemented:
  - `Maybe` — handle missing values
  - `Either` / `Result` — safe error handling
  - `Validation` — accumulate multiple errors
  - `Reader` — dependency injection / environment
  - `Writer` — accumulate logs
  - `State` — stateful computations
- Monad transformers: `MaybeT`, `ResultT`, `ReaderT`, `StateT`, `WriterT`
- Utilities: `traverse`/`sequence`, Applicative combinators
- Advanced monads: `RWST` (Reader-Writer-State)
- Operator overloads for concise DSL-style code:
  - `|` → `fmap` (map)
  - `>>` → `bind` (flatMap)
  - `@` → `ap` (applicative apply)
- High test coverage, Monad law tests included

---

## 🚀 Installation

```bash
pip install darkcore
```

(or use Poetry)

---

## 🧪 Quick Examples

### Maybe

```python
from darkcore.maybe import Maybe

m = Maybe(3) | (lambda x: x+1) >> (lambda y: Maybe(y*2))
print(m)  # Just(8)

n = Maybe(None) | (lambda x: x+1)
print(n)  # Nothing
```

---

### Result

```python
from darkcore.result import Ok, Err

def parse_int(s: str):
    try:
        return Ok(int(s))
    except ValueError:
        return Err(f"invalid int: {s}")

res = parse_int("42") >> (lambda x: Ok(x * 2))
print(res)  # Ok(84)

res2 = parse_int("foo") >> (lambda x: Ok(x * 2))
print(res2)  # Err("invalid int: foo")
```

---

### Validation: accumulate errors via Applicative

```python
from darkcore.validation import Success, Failure

def positive(x: int):
    return Failure(["non-positive"]) if x <= 0 else Success(x)

v = Success(lambda a: lambda b: a + b).ap(positive(-1)).ap(positive(0))
print(v)  # Failure(['non-positive', 'non-positive'])

# Result would stop at the first failure
```

Validation is primarily intended for Applicative composition; `bind` short-circuits like `Result` and is not recommended for error accumulation scenarios.

---

### Reader

```python
from darkcore.reader import Reader

get_user = Reader(lambda env: env["user"])
greet = get_user | (lambda u: f"Hello {u}")

print(greet.run({"user": "Alice"}))  # "Hello Alice"
```

---

### Writer

```python
from darkcore.writer import Writer

# list log by default
w = Writer.pure(3).tell(["start"]) >> (lambda x: Writer(x + 1, ["inc"]))
print(w)  # Writer(4, log=['start', 'inc'])

# for non-``list`` logs, pass ``empty`` and ``combine`` explicitly
# ``empty`` provides the identity element and ``combine`` appends logs
w2 = Writer("hi", empty=str, combine=str.__add__).tell("!")
print(w2)  # Writer('hi', log='!')

# omitting these for a non-``list`` log raises ``TypeError``
try:
    Writer("hi", "!")  # missing empty/combine
except TypeError:
    print("expected TypeError")
```

---

### State

```python
from darkcore.state import State

inc = State(lambda s: (s, s+1))
prog = inc >> (lambda x: State(lambda s: (x+s, s)))

print(prog.run(1))  # (3, 2)
```

### Traverse utilities

```python
from darkcore.traverse import traverse_result
from darkcore.result import Ok, Err

def parse_int(s: str):
    try:
        return Ok(int(s))
    except ValueError:
        return Err(f"bad: {s}")

print(traverse_result(["1", "2"], parse_int))  # Ok([1, 2])
print(traverse_result(["1", "x"], parse_int))  # Err("bad: x")
```

`Result` short-circuits on the first `Err` in `traverse_*` / `sequence_*`, whereas `Validation` accumulates errors under Applicative composition.

### RWST

```python
from darkcore.rwst import RWST
from darkcore.result import Ok

combine = lambda a, b: a + b

action = RWST.ask(Ok.pure, combine=combine, empty=list).bind(
    lambda env: RWST.tell([env], Ok.pure, combine=combine, empty=list)
)

print(action(1, 0))  # Ok(((None, 0), [1]))
```

### Operator DSL

```python
from darkcore.maybe import Maybe

mf = Maybe(lambda x: x * 2)
mx = Maybe(4)
print((mf @ mx) | (lambda x: x + 1))  # Just(9)
```

---

## 📖 Integration Example

```python
from darkcore.reader import Reader
from darkcore.writer import Writer
from darkcore.state import State
from darkcore.result import Ok, Err

# Reader: get user from environment
get_user = Reader(lambda env: env.get("user"))

# Result: validate existence
to_result = lambda user: Err("no user") if user is None else Ok(user)

# Writer: log user
log_user = lambda user: Writer(user, [f"got user={user}"])

# State: update counter
update_state = lambda user: State(lambda s: (f"{user}@{s}", s+1))

env = {"user": "alice"}

user = get_user.run(env)
res = to_result(user) >> (lambda u: Ok(log_user(u)))
writer = res.value
print(writer.log)  # ['got user=alice']

out, s2 = update_state(writer.value).run(42)
print(out, s2)  # alice@42 43
```

---

## Why?

- **Safer business code**  
  - Avoid nested `try/except` and `if None` checks  
  - Express computations declaratively with monads  
- **Educational value**  
  - Learn Haskell/FP concepts hands-on in Python  
- **Expressive DSL**  
  - `|`, `>>`, `@` make pipelines concise and clear  

---

## Development

```bash
git clone https://github.com/minamorl/darkcore
cd darkcore
poetry install
poetry run pytest -v --cov=darkcore
```

---

## License

MIT

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/minamorl/darkcore",
    "name": "darkcore",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.12",
    "maintainer_email": null,
    "keywords": "monad, functional, dsl, business logic",
    "author": "minamorl",
    "author_email": "minamorl@users.noreply.github.com",
    "download_url": "https://files.pythonhosted.org/packages/78/8b/3f4cadd10e26546948ef0d8e3a5576030b4d52f70fa938953a3c9d9b4de9/darkcore-0.4.0.tar.gz",
    "platform": null,
    "description": "# darkcore\n\n**darkcore** is a lightweight functional programming toolkit for Python.  \nIt brings **Functor / Applicative / Monad** abstractions, classic monads like **Maybe, Either/Result, Reader, Writer, State**,  \nand an expressive **operator DSL** (`|`, `>>`, `@`) that makes Python feel almost like Haskell.\n\n---\n\n## \u2728 Features\n\n- Functor / Applicative / Monad base abstractions\n- Core monads implemented:\n  - `Maybe` \u2014 handle missing values\n  - `Either` / `Result` \u2014 safe error handling\n  - `Validation` \u2014 accumulate multiple errors\n  - `Reader` \u2014 dependency injection / environment\n  - `Writer` \u2014 accumulate logs\n  - `State` \u2014 stateful computations\n- Monad transformers: `MaybeT`, `ResultT`, `ReaderT`, `StateT`, `WriterT`\n- Utilities: `traverse`/`sequence`, Applicative combinators\n- Advanced monads: `RWST` (Reader-Writer-State)\n- Operator overloads for concise DSL-style code:\n  - `|` \u2192 `fmap` (map)\n  - `>>` \u2192 `bind` (flatMap)\n  - `@` \u2192 `ap` (applicative apply)\n- High test coverage, Monad law tests included\n\n---\n\n## \ud83d\ude80 Installation\n\n```bash\npip install darkcore\n```\n\n(or use Poetry)\n\n---\n\n## \ud83e\uddea Quick Examples\n\n### Maybe\n\n```python\nfrom darkcore.maybe import Maybe\n\nm = Maybe(3) | (lambda x: x+1) >> (lambda y: Maybe(y*2))\nprint(m)  # Just(8)\n\nn = Maybe(None) | (lambda x: x+1)\nprint(n)  # Nothing\n```\n\n---\n\n### Result\n\n```python\nfrom darkcore.result import Ok, Err\n\ndef parse_int(s: str):\n    try:\n        return Ok(int(s))\n    except ValueError:\n        return Err(f\"invalid int: {s}\")\n\nres = parse_int(\"42\") >> (lambda x: Ok(x * 2))\nprint(res)  # Ok(84)\n\nres2 = parse_int(\"foo\") >> (lambda x: Ok(x * 2))\nprint(res2)  # Err(\"invalid int: foo\")\n```\n\n---\n\n### Validation: accumulate errors via Applicative\n\n```python\nfrom darkcore.validation import Success, Failure\n\ndef positive(x: int):\n    return Failure([\"non-positive\"]) if x <= 0 else Success(x)\n\nv = Success(lambda a: lambda b: a + b).ap(positive(-1)).ap(positive(0))\nprint(v)  # Failure(['non-positive', 'non-positive'])\n\n# Result would stop at the first failure\n```\n\nValidation is primarily intended for Applicative composition; `bind` short-circuits like `Result` and is not recommended for error accumulation scenarios.\n\n---\n\n### Reader\n\n```python\nfrom darkcore.reader import Reader\n\nget_user = Reader(lambda env: env[\"user\"])\ngreet = get_user | (lambda u: f\"Hello {u}\")\n\nprint(greet.run({\"user\": \"Alice\"}))  # \"Hello Alice\"\n```\n\n---\n\n### Writer\n\n```python\nfrom darkcore.writer import Writer\n\n# list log by default\nw = Writer.pure(3).tell([\"start\"]) >> (lambda x: Writer(x + 1, [\"inc\"]))\nprint(w)  # Writer(4, log=['start', 'inc'])\n\n# for non-``list`` logs, pass ``empty`` and ``combine`` explicitly\n# ``empty`` provides the identity element and ``combine`` appends logs\nw2 = Writer(\"hi\", empty=str, combine=str.__add__).tell(\"!\")\nprint(w2)  # Writer('hi', log='!')\n\n# omitting these for a non-``list`` log raises ``TypeError``\ntry:\n    Writer(\"hi\", \"!\")  # missing empty/combine\nexcept TypeError:\n    print(\"expected TypeError\")\n```\n\n---\n\n### State\n\n```python\nfrom darkcore.state import State\n\ninc = State(lambda s: (s, s+1))\nprog = inc >> (lambda x: State(lambda s: (x+s, s)))\n\nprint(prog.run(1))  # (3, 2)\n```\n\n### Traverse utilities\n\n```python\nfrom darkcore.traverse import traverse_result\nfrom darkcore.result import Ok, Err\n\ndef parse_int(s: str):\n    try:\n        return Ok(int(s))\n    except ValueError:\n        return Err(f\"bad: {s}\")\n\nprint(traverse_result([\"1\", \"2\"], parse_int))  # Ok([1, 2])\nprint(traverse_result([\"1\", \"x\"], parse_int))  # Err(\"bad: x\")\n```\n\n`Result` short-circuits on the first `Err` in `traverse_*` / `sequence_*`, whereas `Validation` accumulates errors under Applicative composition.\n\n### RWST\n\n```python\nfrom darkcore.rwst import RWST\nfrom darkcore.result import Ok\n\ncombine = lambda a, b: a + b\n\naction = RWST.ask(Ok.pure, combine=combine, empty=list).bind(\n    lambda env: RWST.tell([env], Ok.pure, combine=combine, empty=list)\n)\n\nprint(action(1, 0))  # Ok(((None, 0), [1]))\n```\n\n### Operator DSL\n\n```python\nfrom darkcore.maybe import Maybe\n\nmf = Maybe(lambda x: x * 2)\nmx = Maybe(4)\nprint((mf @ mx) | (lambda x: x + 1))  # Just(9)\n```\n\n---\n\n## \ud83d\udcd6 Integration Example\n\n```python\nfrom darkcore.reader import Reader\nfrom darkcore.writer import Writer\nfrom darkcore.state import State\nfrom darkcore.result import Ok, Err\n\n# Reader: get user from environment\nget_user = Reader(lambda env: env.get(\"user\"))\n\n# Result: validate existence\nto_result = lambda user: Err(\"no user\") if user is None else Ok(user)\n\n# Writer: log user\nlog_user = lambda user: Writer(user, [f\"got user={user}\"])\n\n# State: update counter\nupdate_state = lambda user: State(lambda s: (f\"{user}@{s}\", s+1))\n\nenv = {\"user\": \"alice\"}\n\nuser = get_user.run(env)\nres = to_result(user) >> (lambda u: Ok(log_user(u)))\nwriter = res.value\nprint(writer.log)  # ['got user=alice']\n\nout, s2 = update_state(writer.value).run(42)\nprint(out, s2)  # alice@42 43\n```\n\n---\n\n## Why?\n\n- **Safer business code**  \n  - Avoid nested `try/except` and `if None` checks  \n  - Express computations declaratively with monads  \n- **Educational value**  \n  - Learn Haskell/FP concepts hands-on in Python  \n- **Expressive DSL**  \n  - `|`, `>>`, `@` make pipelines concise and clear  \n\n---\n\n## Development\n\n```bash\ngit clone https://github.com/minamorl/darkcore\ncd darkcore\npoetry install\npoetry run pytest -v --cov=darkcore\n```\n\n---\n\n## License\n\nMIT\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Practical functional programming primitives for Python: Monads, Transformers, and DSL operators for safe business logic",
    "version": "0.4.0",
    "project_urls": {
        "Homepage": "https://github.com/minamorl/darkcore",
        "Repository": "https://github.com/minamorl/darkcore"
    },
    "split_keywords": [
        "monad",
        " functional",
        " dsl",
        " business logic"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "0c428d23d65f5a43bb5a34d82d890e037018328a326223028275fdac9348e9af",
                "md5": "41a3af5607bd67b06ba5d45bd041a654",
                "sha256": "a16f4edffc50f2518fd4e562931212bf534cfddbcd153331d3536965b42d3242"
            },
            "downloads": -1,
            "filename": "darkcore-0.4.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "41a3af5607bd67b06ba5d45bd041a654",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.12",
            "size": 19053,
            "upload_time": "2025-08-22T19:40:57",
            "upload_time_iso_8601": "2025-08-22T19:40:57.467141Z",
            "url": "https://files.pythonhosted.org/packages/0c/42/8d23d65f5a43bb5a34d82d890e037018328a326223028275fdac9348e9af/darkcore-0.4.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "788b3f4cadd10e26546948ef0d8e3a5576030b4d52f70fa938953a3c9d9b4de9",
                "md5": "e480f6c48ae4c0c109fb15baff499dba",
                "sha256": "2110ef35b4d73ca78e8aeff0235684993c3521b3fb983b36780ae5ad1c67d871"
            },
            "downloads": -1,
            "filename": "darkcore-0.4.0.tar.gz",
            "has_sig": false,
            "md5_digest": "e480f6c48ae4c0c109fb15baff499dba",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.12",
            "size": 13383,
            "upload_time": "2025-08-22T19:40:58",
            "upload_time_iso_8601": "2025-08-22T19:40:58.853563Z",
            "url": "https://files.pythonhosted.org/packages/78/8b/3f4cadd10e26546948ef0d8e3a5576030b4d52f70fa938953a3c9d9b4de9/darkcore-0.4.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-22 19:40:58",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "minamorl",
    "github_project": "darkcore",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "darkcore"
}
        
Elapsed time: 1.74483s