{"id":50263419,"url":"https://github.com/sawirstudio/effectphp","last_synced_at":"2026-05-27T12:05:40.471Z","repository":{"id":331905092,"uuid":"1132227576","full_name":"sawirstudio/effectphp","owner":"sawirstudio","description":"Build Production ready applications with PHP","archived":false,"fork":false,"pushed_at":"2026-01-11T15:24:22.000Z","size":25,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-11T18:42:15.527Z","etag":null,"topics":["effect","effect-php","effect-ts","error-handling","functional","php","workflow"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sawirstudio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-11T15:16:13.000Z","updated_at":"2026-01-11T17:55:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sawirstudio/effectphp","commit_stats":null,"previous_names":["sawirstudio/effectphp"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/sawirstudio/effectphp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sawirstudio%2Feffectphp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sawirstudio%2Feffectphp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sawirstudio%2Feffectphp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sawirstudio%2Feffectphp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sawirstudio","download_url":"https://codeload.github.com/sawirstudio/effectphp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sawirstudio%2Feffectphp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33564967,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-27T02:00:06.184Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["effect","effect-php","effect-ts","error-handling","functional","php","workflow"],"created_at":"2026-05-27T12:05:37.214Z","updated_at":"2026-05-27T12:05:40.461Z","avatar_url":"https://github.com/sawirstudio.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Effect PHP\n\nA functional effects library for PHP 8.1+ inspired by [Effect-TS](https://effect.website/). Provides typed error handling, composable computations, and resource safety.\n\n## Installation\n\n```bash\ncomposer require effectphp/effect\n```\n\nRequires PHP 8.1 or higher.\n\n## Quick Start\n\n```php\nuse EffectPHP\\Effect\\Effect;\nuse EffectPHP\\Runtime\\SyncRuntime;\nuse function EffectPHP\\{succeed, fail, trySync, gen, runSync};\n\n// Create effects\n$effect = succeed(42)\n    -\u003emap(fn($n) =\u003e $n * 2)\n    -\u003eflatMap(fn($n) =\u003e succeed($n + 1));\n\n// Run synchronously\n$result = runSync($effect); // 85\n\n// Handle errors\n$safe = fail('something went wrong')\n    -\u003ecatchAll(fn($error) =\u003e succeed('recovered'))\n    -\u003emap(fn($msg) =\u003e strtoupper($msg));\n\necho runSync($safe); // \"RECOVERED\"\n```\n\n## Core Concepts\n\n### Effect\u003cR, E, A\u003e\n\nThe `Effect` type represents a lazy, composable computation that:\n- **R** - Requires an environment/context (dependencies)\n- **E** - May fail with an error of type E (expected failures)\n- **A** - May succeed with a value of type A\n\nEffects are lazy - they describe computations but don't execute until you run them.\n\n```php\n// Succeeds with a value\n$success = Effect::succeed(42);\n\n// Fails with an error\n$failure = Effect::fail('error message');\n\n// Wraps a side effect\n$effect = Effect::sync(fn() =\u003e file_get_contents('file.txt'));\n\n// Wraps a fallible operation\n$safe = Effect::trySync(\n    fn() =\u003e json_decode($input, true, 512, JSON_THROW_ON_ERROR),\n    fn(\\Throwable $e) =\u003e new JsonParseError($e-\u003egetMessage())\n);\n```\n\n### Cause\u003cE\u003e\n\n`Cause` represents the full story of why an effect failed. It distinguishes between:\n\n- **Fail** - Expected, recoverable errors (your error type E)\n- **Defect** - Unexpected errors (thrown exceptions)\n- **Interrupt** - Fiber interruption\n\n```php\n$exit = runSyncExit($effect);\n\nif ($exit-\u003eisFailure()) {\n    $cause = $exit-\u003ecauseOption();\n\n    if ($cause-\u003eisFailure()) {\n        $error = $cause-\u003efailureOption(); // Your typed error\n    } elseif ($cause-\u003eisDie()) {\n        $defect = $cause-\u003edefectOption(); // Throwable\n    }\n}\n```\n\n### Exit\u003cE, A\u003e\n\n`Exit` represents the result of running an effect - either `Success\u003cA\u003e` or `Failure\u003cE\u003e`.\n\n```php\nuse EffectPHP\\Runtime\\SyncRuntime;\n\n$runtime = new SyncRuntime();\n$exit = $runtime-\u003erunSyncExit($effect);\n\n$result = $exit-\u003ematch(\n    onSuccess: fn($value) =\u003e \"Got: $value\",\n    onFailure: fn($cause) =\u003e \"Failed: \" . $cause-\u003esquash()-\u003egetMessage()\n);\n```\n\n## Transformations\n\n### map / flatMap\n\n```php\n$effect = succeed(5)\n    -\u003emap(fn($n) =\u003e $n * 2)           // Transform success value\n    -\u003eflatMap(fn($n) =\u003e succeed($n)); // Chain effects\n```\n\n### tap\n\nExecute a side effect without changing the value:\n\n```php\n$effect = succeed(42)\n    -\u003etap(fn($n) =\u003e print(\"Value: $n\"));\n```\n\n### zip\n\nCombine effects:\n\n```php\n$effect = succeed(1)-\u003ezip(succeed(2));\n// Result: [1, 2]\n\n$effect = succeed(1)-\u003ezipWith(succeed(2), fn($a, $b) =\u003e $a + $b);\n// Result: 3\n```\n\n## Error Handling\n\n### catchAll\n\nRecover from all errors:\n\n```php\n$effect = fail('error')\n    -\u003ecatchAll(fn($e) =\u003e succeed('default'));\n```\n\n### catchTag\n\nRecover from specific error types:\n\n```php\nclass NotFoundError extends Exception {}\nclass ValidationError extends Exception {}\n\n$effect = fetchUser($id)\n    -\u003ecatchTag(NotFoundError::class, fn($e) =\u003e succeed($defaultUser))\n    -\u003ecatchTag(ValidationError::class, fn($e) =\u003e fail(new BadRequest()));\n```\n\n### mapError\n\nTransform error type:\n\n```php\n$effect = fail('raw error')\n    -\u003emapError(fn($e) =\u003e new DomainError($e));\n```\n\n### orElse / orElseSucceed\n\nFallback on error:\n\n```php\n$effect = fail('error')-\u003eorElse(succeed('fallback'));\n$effect = fail('error')-\u003eorElseSucceed('default value');\n```\n\n### orDie\n\nConvert expected errors to defects:\n\n```php\n$effect = fetchUser($id)-\u003eorDie(); // Throws on error\n```\n\n## Do-Notation\n\nUse generators for sequential composition:\n\n```php\nuse function EffectPHP\\gen;\n\n$program = gen(function () {\n    $user = yield fetchUser($userId);\n    $posts = yield fetchPosts($user-\u003eid);\n    $validated = yield validatePosts($posts);\n\n    return [\n        'user' =\u003e $user,\n        'posts' =\u003e $validated,\n    ];\n});\n```\n\n## Dependency Injection\n\nUse `Context` and `Tag` for dependency injection:\n\n```php\nuse EffectPHP\\Context\\Tag;\nuse EffectPHP\\Context\\Context;\n\n// Define service tags\n$dbTag = Tag::of(Database::class);\n$loggerTag = Tag::of(Logger::class);\n\n// Access services in effects\n$program = Effect::getService($dbTag)\n    -\u003eflatMap(fn($db) =\u003e Effect::trySync(fn() =\u003e $db-\u003equery('SELECT * FROM users')));\n\n// Provide services at runtime\n$context = Context::empty()\n    -\u003eadd($dbTag, new PostgresDatabase())\n    -\u003eadd($loggerTag, new FileLogger());\n\n$runtime = (new SyncRuntime())-\u003ewithContext($context);\n$result = $runtime-\u003erunSync($program);\n```\n\n## Combinators\n\n### All\n\nRun multiple effects:\n\n```php\nuse EffectPHP\\Combinators\\All;\n\n// Sequential execution\n$results = All::seq([\n    fetchUser(1),\n    fetchUser(2),\n    fetchUser(3),\n]);\n\n// First success\n$result = All::firstSuccess([\n    fetchFromCache($key),\n    fetchFromDatabase($key),\n    fetchFromRemote($key),\n]);\n```\n\n### Retry\n\nRetry failed effects:\n\n```php\nuse EffectPHP\\Combinators\\Retry;\nuse EffectPHP\\Combinators\\RetryPolicy;\n\n// Retry 3 times with exponential backoff\n$effect = Retry::retry(\n    fetchData(),\n    RetryPolicy::exponential(retries: 3, baseDelayMs: 100)\n);\n\n// Simple retry\n$effect = Retry::retryN(fetchData(), times: 5);\n```\n\n### Timing\n\n```php\nuse EffectPHP\\Combinators\\Timing;\n\n// Delay execution\n$effect = Timing::delay(1000)-\u003eflatMap(fn() =\u003e doSomething());\n\n// Measure duration\n$effect = Timing::timed(fetchData());\n// Result: ['value' =\u003e $data, 'durationMs' =\u003e 123.45]\n\n// Repeat\n$effect = Timing::repeatN(ping(), times: 5);\n```\n\n## Resource Management\n\nSafely acquire and release resources:\n\n```php\nuse EffectPHP\\Resource\\AcquireRelease;\n\n$program = AcquireRelease::bracket(\n    acquire: Effect::sync(fn() =\u003e fopen('file.txt', 'r')),\n    release: fn($handle) =\u003e Effect::sync(fn() =\u003e fclose($handle)),\n    use: fn($handle) =\u003e Effect::trySync(fn() =\u003e fread($handle, 1024))\n);\n```\n\nThe release function is guaranteed to run even if the use function fails.\n\n## Runtimes\n\n### SyncRuntime\n\nTraditional synchronous execution:\n\n```php\nuse EffectPHP\\Runtime\\SyncRuntime;\n\n$runtime = new SyncRuntime();\n$result = $runtime-\u003erunSync($effect);      // Returns value or throws\n$exit = $runtime-\u003erunSyncExit($effect);    // Returns Exit\u003cE, A\u003e\n```\n\n### FiberRuntime\n\nFiber-based execution with async support:\n\n```php\nuse EffectPHP\\Runtime\\FiberRuntime;\n\n$runtime = new FiberRuntime();\n$result = $runtime-\u003erunSync($effect);\n\n// With callback\n$runtime-\u003erunCallback($effect, function ($exit) {\n    // Handle result\n});\n```\n\n## Helper Functions\n\nGlobal functions for convenience:\n\n```php\nuse function EffectPHP\\{\n    succeed,      // Effect::succeed()\n    fail,         // Effect::fail()\n    defect,       // Effect::defect()\n    sync,         // Effect::sync()\n    trySync,      // Effect::trySync()\n    suspend,      // Effect::suspend()\n    async,        // Effect::async()\n    service,      // Effect::getService()\n    all,          // All::all()\n    traverse,     // Map and collect\n    delay,        // Timing::delay()\n    sleep,        // Timing::sleep()\n    retry,        // Retry::retry()\n    bracket,      // AcquireRelease::bracket()\n    gen,          // Do-notation\n    pipe,         // Pipe helper\n    runSync,      // Quick run with SyncRuntime\n    runSyncExit,  // Quick run returning Exit\n    runFiber,     // Quick run with FiberRuntime\n};\n```\n\n## Example: HTTP Client\n\n```php\nuse function EffectPHP\\{gen, trySync, fail, succeed, retry, runSync};\n\nclass HttpError extends Exception {\n    public function __construct(public int $status, string $message) {\n        parent::__construct($message);\n    }\n}\n\nfunction httpGet(string $url): Effect {\n    return trySync(\n        fn() =\u003e file_get_contents($url),\n        fn($e) =\u003e new HttpError(0, $e-\u003egetMessage())\n    )-\u003eflatMap(fn($body) =\u003e $body === false\n        ? fail(new HttpError(404, 'Not found'))\n        : succeed($body)\n    );\n}\n\nfunction fetchJson(string $url): Effect {\n    return httpGet($url)\n        -\u003eflatMap(fn($body) =\u003e trySync(\n            fn() =\u003e json_decode($body, true, 512, JSON_THROW_ON_ERROR),\n            fn($e) =\u003e new HttpError(0, 'Invalid JSON')\n        ));\n}\n\n// Usage\n$program = gen(function () {\n    $users = yield retry(fetchJson('https://api.example.com/users'), 3);\n    $posts = yield fetchJson(\"https://api.example.com/posts?userId={$users[0]['id']}\");\n\n    return ['user' =\u003e $users[0], 'posts' =\u003e $posts];\n});\n\ntry {\n    $result = runSync($program);\n    print_r($result);\n} catch (HttpError $e) {\n    echo \"HTTP Error {$e-\u003estatus}: {$e-\u003egetMessage()}\";\n}\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsawirstudio%2Feffectphp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsawirstudio%2Feffectphp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsawirstudio%2Feffectphp/lists"}