{"id":16721102,"url":"https://github.com/khalyomede/monad","last_synced_at":"2025-04-05T05:40:46.958Z","repository":{"id":48053417,"uuid":"393780126","full_name":"khalyomede/monad","owner":"khalyomede","description":"Maybe, Option and Result monads for PHP.","archived":false,"fork":false,"pushed_at":"2022-06-05T20:52:05.000Z","size":58,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-02T01:35:16.052Z","etag":null,"topics":["adt","functional-programming","monad"],"latest_commit_sha":null,"homepage":"https://packagist.org/packages/khalyomede/monad","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/khalyomede.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-08-07T19:59:13.000Z","updated_at":"2025-03-25T12:03:30.000Z","dependencies_parsed_at":"2022-08-12T17:31:23.443Z","dependency_job_id":null,"html_url":"https://github.com/khalyomede/monad","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khalyomede%2Fmonad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khalyomede%2Fmonad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khalyomede%2Fmonad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khalyomede%2Fmonad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/khalyomede","download_url":"https://codeload.github.com/khalyomede/monad/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247294459,"owners_count":20915337,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["adt","functional-programming","monad"],"created_at":"2024-10-12T22:28:46.788Z","updated_at":"2025-04-05T05:40:46.938Z","avatar_url":"https://github.com/khalyomede.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# khalyomede/monad\r\n\r\nMaybe, Option and Result monads for PHP.\r\n\r\n## Summary\r\n\r\n- [About](#about)\r\n- [Features](#features)\r\n- [Prerequisites](#prerequisites)\r\n- [Installation](#installation)\r\n- [Examples](#examples)\r\n- [API](#api)\r\n\r\n## About\r\n\r\nI published on [reddit](https://www.reddit.com/r/PHP/comments/oxcmw9/rfc_proposal_enums_constructor/) an RFC proposal for what I called \"enums constructor\" (which happened to be an existing concept called \"tagged unions\"). Out of [one comment](https://www.reddit.com/r/PHP/comments/oxcmw9/rfc_proposal_enums_constructor/h7qgo1n?utm_source=share\u0026utm_medium=web2x\u0026context=3), I shown how to chain calls to various ADT outcomes, in order to reduces pyramidal matches.\r\n\r\nThis got me into wanting to use more monads right now on my next projects. After searching on packagist.org, I did not found something that fits what I expected, so I decided to make my own version of how I think monads should look like in PHP.\r\n\r\n## Features\r\n\r\n- Supports theses monads:\r\n  - Option\r\n  - Maybe\r\n  - Result\r\n- PHPStan and static analyzers friendly\r\n\r\n## Prerequisites\r\n\r\n- PHP 8+ installed\r\n\r\n## Installation\r\n\r\n```bash\r\ncomposer require khalyomede/monad\r\n```\r\n\r\n## Examples\r\n\r\n- [1. Getting a file content](#1-getting-a-file-content)\r\n- [2. Get users from an SQL table](#2-get-users-from-an-sql-table)\r\n- [3. Chain multiple outcomes](#3-chain-multiple-outcomes)\r\n\r\n### 1. Getting a file content\r\n\r\nIn this example, we will use the Maybe monad as result, and use it in our main code.\r\n\r\n```php\r\nuse Khalyomede\\Monad\\Maybe;\r\n\r\nfunction getFileContent(string $filePath): Maybe\r\n{\r\n  $content = file_get_contents($filePath);\r\n\r\n  return $content === false ? Maybe::nothing() : Maybe::just($content);\r\n}\r\n\r\n// main\r\n$content = getFileContent(\"data.txt\")\r\n  -\u003ethen(fn (string $data): string =\u003e $data)\r\n  -\u003ecatch(fn (): string =\u003e \"N/A\");\r\n\r\necho $content;\r\n```\r\n\r\n### 2. Get users from an SQL table\r\n\r\nIn this example, we will use the Result monad to return the list of users or an error from PDO.\r\n\r\n```php\r\nuse Khalyomede\\Monad\\Result;\r\n\r\nfunction getUsers(): Result\r\n{\r\n  $pdo = new PDO(\"sqlite::memory:\");\r\n\r\n  $statement = $pdo-\u003eprepare(\"SELECT * FROM users\");\r\n\r\n  if ($statement === false) {\r\n    $message = $pdo-\u003eerrorInfo()[2];\r\n\r\n    return Result::error($message);\r\n  }\r\n\r\n  $result = $statement-\u003eexecute();\r\n\r\n  if ($result === false) {\r\n    $message = $statement-\u003eerrorInfo()[2];\r\n\r\n    return Result::error($message);\r\n  }\r\n\r\n  return Result::ok($statement-\u003efetchAll());\r\n}\r\n\r\n// main\r\n$users = getUsers()\r\n  -\u003ethen(fn (array $records): array =\u003e $records)\r\n  -\u003ecatch(function (string $error): array {\r\n    error_log($error);\r\n\r\n    return [];\r\n  });\r\n```\r\n\r\n### 3. Chain multiple outcomes\r\n\r\nIn this example, we will chain multiple times \"then\", similar to what is possible in other languages through \"map\".\r\n\r\n```php\r\nuse Khalyomede\\Monad\\Result;\r\n\r\nfunction getFileContent(string $path): Result\r\n{\r\n  $content = file_get_contents($path);\r\n\r\n  return $content === false ?\r\n    Result::error(new Exception(\"Cannot get content of file $path.\")) :\r\n    Result::ok($content);\r\n}\r\n\r\nfunction saveFileContent(string $path, string $content): Result\r\n{\r\n  $result = file_put_contents($path, $content);\r\n\r\n  return $result === false ?\r\n    Result::error(new Exception(\"Cannot write file content of file $path.\")) :\r\n    Result::ok(true);\r\n}\r\n\r\n// main\r\n$result = fileGetContent(\"composer.json\")\r\n  -\u003ethen(fn (string $content): Result =\u003e saveFileContent(\"composer.json.save\", $content))\r\n  -\u003ethen(fn (): string =\u003e \"composer.json backup finished.\")\r\n  -\u003ecatch(fn (Exception $exception): string =\u003e $exception-\u003egetMessage());\r\n\r\necho $result . PHP_EOL;\r\n```\r\n\r\n```bash\r\nkhalyomede@pc \u003e php index.php\r\ncomposer.json backup finished.\r\n```\r\n\r\nIf you remove the file, the result becomes\r\n\r\n```bash\r\nkhalyomede@pc \u003e php index.php\r\nCannot get content of file composer.json.\r\n```\r\n\r\nIf you remove the write permission on this file, the result becomes\r\n\r\n```bash\r\nkhalyomede@pc \u003e php index.php\r\nCannot write file content of file composer.json.save.\r\n```\r\n\r\n## API\r\n\r\nFor a list of all possible functions, see the _tests\\unit_ folder.\r\n\r\n## Tests\r\n\r\n```bash\r\ncomposer run install-security-checker\r\ncomposer run test\r\ncomposer run mutate\r\ncomposer run analyse\r\ncomposer run check\r\ncomposer outdated --direct\r\n```\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhalyomede%2Fmonad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkhalyomede%2Fmonad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhalyomede%2Fmonad/lists"}