{"id":33974429,"url":"https://github.com/thgs/functional","last_synced_at":"2025-12-13T01:44:58.415Z","repository":{"id":279042981,"uuid":"706532002","full_name":"thgs/functional","owner":"thgs","description":"(experimental) Trying to express some functional concepts in PHP","archived":false,"fork":false,"pushed_at":"2025-07-19T14:26:00.000Z","size":315,"stargazers_count":3,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-19T18:33:07.394Z","etag":null,"topics":["experimental","functional-programming","php"],"latest_commit_sha":null,"homepage":"","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/thgs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2023-10-18T06:20:16.000Z","updated_at":"2025-07-19T14:26:04.000Z","dependencies_parsed_at":"2025-04-05T01:21:31.048Z","dependency_job_id":"fcb1dc26-dbcf-4117-9475-da8fc78dd5dc","html_url":"https://github.com/thgs/functional","commit_stats":null,"previous_names":["thgs/functional"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/thgs/functional","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thgs%2Ffunctional","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thgs%2Ffunctional/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thgs%2Ffunctional/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thgs%2Ffunctional/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thgs","download_url":"https://codeload.github.com/thgs/functional/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thgs%2Ffunctional/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27697900,"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","status":"online","status_checked_at":"2025-12-12T02:00:06.775Z","response_time":129,"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":["experimental","functional-programming","php"],"created_at":"2025-12-13T01:44:54.623Z","updated_at":"2025-12-13T01:44:58.399Z","avatar_url":"https://github.com/thgs.png","language":"PHP","readme":"## functional\n\nThis is a very experimental repository where I try to see how some functional\nconcepts can be expressed and constructed in PHP.\n\nI tried initially to express some concepts as good as I could understand them\nand very quickly realised that a very very similar approach was used in\n[marcosh/lamphpda](https://github.com/marcosh/lamphpda). That is a more complete\nlibrary.\n\nFrom then on, I have deviated considerably and certain decisions are\nquite different, for reasons that either I still explore and I am not\ncertain about or for different goals and priorities.\n\n### Highlight features\n\n#### Basic structures from Haskell\n\n```php\n\n$maybeInt = new Maybe(new Just(123));\n$maybeInt = $maybeInt-\u003efmap(fn ($x) =\u003e $x * 2);\n\nif ($maybeInt-\u003eisJust()) {\n    print $maybeInt-\u003eunwrap();\n}\n\n```\n\nIncluding `Maybe`, `Either`, `IO` with instances as functors, applicative functors and monads.\n\n#### Expression helpers\n\nComposition of function calls\n\n```php\n\n$result = c (fn ($x) =\u003e $x + 2) (123); // 125\n\n```\n\nFunction composition\n\n```php\n\n$f = fn ($x) =\u003e $x + 2;\n$g = fn ($x) =\u003e $x * 2;\n$gf = c($f)-\u003efmap($g); // equivalent of g(f($x))\n\nprint $gf(123);  // (123 + 2) * 2 = 250\n\n```\n\nWrap any php function\n\n```php\n$min = c('min');\n\n$maybeArrayOfInt = new Maybe(new Just(range(1,4)));\n\n$maybeMinOfArray = fmap($min, $maybeArrayOfInt);\n\nvar_dump($maybeMinOfArray-\u003eunwrap());\n\n```\n\nDo notation\n\n```php\n\n// putStrLn :: String -\u003e IO ()\n$putStrLn = fn (string $x) =\u003e IO::inject(fn () =\u003e print $x . \"\\n\");\n\n// getLine :: IO String\n$getLine = IO::inject(fn (): string =\u003e fgets(\\STDIN));\n\n$bound = dn($getLine, $putStrLn);\n$bound(); // will run getLine and then bind the result to putStrLn and print it\n\n```\n\nOr a more elaborate example\n\n```php\n\ndn(\n    writeFile(\"test.txt\", show (123 * 123)),\n    appendFile(\"test.txt\", \"Hello!\\n\"),\n    readFile(\"test.txt\"),\n    putStrLn(...)\n)();\n\n```\n\nGeneralised Notations\n\nBy defining the composition between the \"elements\"\n\n```php\n\nfunction myNotation(mixed ...$elements) {\n    return (new LeftToRightNotation(new CategoryOfFunctions()))-\u003ecomposeMany(...$elements);\n}\n\n// this will compose a function that first will evaluate the last passed \"element\"\n$composedFunction = myNotation(\n     fn (int $x): bool =\u003e $x == 16,\n     fn (int $x): int  =\u003e (int) ($x / 2),\n     fn (int $x): int =\u003e pow($x, 5),\n     fn (array $items): int =\u003e count($items),\n     array_filter(...)\n);\n\n$composedFunction([\"one\", \"two\", \"\"]); // true\n```\n\n#### Helpers for your tests\n\n```php\n\nclass MyType implements FunctorInstance\n{\n    public function fmap(\\Closure $f): FunctorInstance\n    {\n        // your code here\n    }\n}\n\nclass MyTypeTest\n{\n    // use helper traits to prove your implementation of fmap abides by the Functor Law.\n    use FunctorLawsAssertions;\n\n    public function testIsAFunctor(): void\n    {\n        $myType = new MyType();\n\n        // Provide an instance and two functions for this assertion\n\n        $this-\u003eassertInstanceIsFunctor(\n            $myType,\n            fn (int $x): int =\u003e $x + 2,\n            fn (int $x): int =\u003e $x + 2\n        );\n    }\n}\n\n```\n\n#### Wrap existing code\n\n```php\n\n/**\n * Create a Wrapper with an anonymous function that calls the psr3Logger from a Tuple input.\n */\n$wrapper = Wrapper::withAdjustedInput(\n    fn (Tuple $p) =\u003e $psr3Logger -\u003e log ($p-\u003efst(), $p-\u003esnd()),\n);\n\n/**\n * Let's add a prefix\n */\n$prefix = 'prefixHere: ';\n\n/**\n * Adjust the input\n */\n$contextLogger = $wrapper -\u003e adjustInput (fn (Tuple $p): Tuple =\u003e t ($prefix . $p-\u003efst(), [$p-\u003esnd()]) );\n\n/**\n * Adjust the output\n */\n$logger = $contextLogger -\u003e adjustOutput (fn () =\u003e time());\n\n$currentTime = $logger (t (\"Log message\", \"context\"));\n\n```\n\n#### Implement typeclass instances for types\n\n```php\n$equalsImplementation = fn (int|float $a, int|float $b): bool  =\u003e ((int) $a) == ((int) $b);\n\nEq::register(\n    instanceName: 'int|float',\n    typePredicate: fn ($x) =\u003e is_int($x) || is_float($x),\n    equals: $equalsImplementation,\n    notEquals: null // derived\n);\n\n$result = equals(12.6, 12.1); // true\n$result = notEquals(12.6, 13.1); // true\n```\n\n#### Loads of bugs and inconsistencies\n\nphpstan is complaining, the tests are not yet fully there but I only\nhave covered some portion of the functionality. Nevertheless, I expect\nthere are things that might have been implemented or type hinted wrong\nat this point. Especially if you stress the limits of the definitions\nor functionality.\n\n\n### Contributing\n\nFeel free to add any PR, comments, issues or discussions!\n\n### Documentation\n\nSee [`Documentation.org`](https://github.com/thgs/functional/blob/master/Documentation.org) file (Emacs org-file).\n\n\n### Other interesting functional programming in PHP libraries/projects\n\nHere will maintain a list of other libraries. Feel free to let me know of one I have missed.\n\n- [marcosh/lamphpda](https://github.com/marcosh/lamphpda)\n- [crell/fp](https://github.com/crell/fp)\n- [loophp/repository-monadic-helper](https://github.com/loophp/repository-monadic-helper)\n- [haskellcamargo/php-maybe-monad](https://github.com/haskellcamargo/php-maybe-monad)\n- [tmciver/functional-php](https://github.com/tmciver/functional-php)\n- [phel-lang/phel-lang](https://github.com/phel-lang/phel-lang)\n- [fp4php/functional](https://github.com/fp4php/functional)\n- [krakphp/fn](https://github.com/krakphp/fn)\n- [prolic/fpp](https://github.com/prolic/fpp)\n- [phpfn/curry](https://github.com/phpfn/curry)\n- [mathiasverraes/lambdalicious](https://github.com/mathiasverraes/lambdalicious)\n- [haskellcamargo/yay-partial-functions](https://github.com/haskellcamargo/yay-partial-functions)\n- [haskellcamargo/php-partial-function-application](https://github.com/haskellcamargo/php-partial-function-application)\n- [loophp/combinator](https://github.com/loophp/combinator)\n- [friends-of-reactphp/partial](https://github.com/friends-of-reactphp/partial)\n- [fp4php/fp4php](https://github.com/fp4php/fp4php)\n- [matteosister/php-curry](https://github.com/matteosister/php-curry)\n- [munusphp/munus](https://github.com/munusphp/munus)\n- [kapolos/pramda](https://github.com/kapolos/pramda)\n- [ace411/bingo-functional](https://github.com/ace411/bingo-functional)\n- [chippyash/Monad](https://github.com/chippyash/Monad)\n- [jasny/improved-php-function](https://github.com/jasny/improved-php-function)\n- [pitchart/transformer](https://github.com/pitchart/transformer)\n- [functional-php/fantasy-land](https://github.com/functional-php/fantasy-land)\n- [haskellcamargo/php-church-encoding](https://github.com/haskellcamargo/php-church-encoding)\n- [functional-php/pattern-matching](https://github.com/functional-php/pattern-matching)\n- [quack/quack](https://github.com/quack/quack)\n- [functional-php/trampoline](https://github.com/functional-php/trampoline)\n- [phunkie/phunkie](https://github.com/phunkie/phunkie)\n- [baethon/phln](https://github.com/baethon/phln)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthgs%2Ffunctional","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthgs%2Ffunctional","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthgs%2Ffunctional/lists"}