thgs / functional
a functional experiment
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^10.4
This package is auto-updated.
Last update: 2025-03-15 00:53:43 UTC
README
This is a very experimental repository where I try to see how some functional concepts can be expressed and constructed in PHP.
I tried initially to express some concepts as good as I could understand them and very quickly realised that a very very similar approach was used in marcosh/lamphpda. That is a more complete library.
Highlight features
Basic structures from Haskell
$maybeInt = new Maybe(new Just(123)); $maybeInt = $maybeInt->fmap(fn ($x) => $x * 2); if ($maybeInt->isJust()) { print $maybeInt->unwrap(); }
Including Maybe
, Either
, IO
with instances as functors, applicative functors and monads.
Expression helpers
Composition of function calls
$result = c (fn ($x) => $x + 2) (123); // 125
Function composition
$f = fn ($x) => $x + 2; $g = fn ($x) => $x * 2; $gf = c($f)->fmap($g); // equivalent of g(f($x)) print $gf(123); // (123 + 2) * 2 = 250
Wrap any php function
$min = c('min'); $maybeArrayOfInt = new Maybe(new Just(range(1,4))); $maybeMinOfArray = fmap($min, $maybeArrayOfInt); var_dump($maybeMinOfArray->unwrap());
Do notation
// putStrLn :: String -> IO () $putStrLn = fn (string $x) => IO::inject(fn () => print $x . "\n"); // getLine :: IO String $getLine = IO::inject(fn (): string => fgets(\STDIN)); $bound = dn($getLine, $putStrLn); $bound(); // will run getLine and then bind the result to putStrLn and print it
Helpers for your tests
class MyType implements FunctorInstance { public function fmap(callable $f): FunctorInstance { // your code here } } class MyTypeTest { // use helper traits to prove your implementation of fmap abides by the Functor Law. use FunctorLawsAssertions; public function testIsAFunctor(): void { $myType = new MyType(); // Provide an instance and two functions for this assertion $this->assertInstanceIsFunctor( $myType, fn (int $x): int => $x + 2, fn (int $x): int => $x + 2 ); } }
Wrap existing code
/** * Create a CallbackWrapper that connects the psr3Logger to a Tuple input. * However here we can also pass the `fmap` method. */ $wrapper = new CallbackWrapper( fn (Tuple $p) => $psr3Logger->log($p->fst(), $p->snd()), ['fmap' => fn (callable $f) => c ($f) ->fmap ($this->wiring)] ); /** * We create the contextLogger */ $contextLogger = $wrapper ->fmap ( fn (Tuple $p): Tuple => t ($prefix . $p->fst(), [$extraContext] + $p->snd() ) ); ($contextLogger) (t ("wrapped one", ["contextt"]));
Note that the above is very draft/experimental still.
Loads of bugs and inconsistencies
phpstan is complaining, the tests are not yet fully there but I only have covered some portion of the functionality. Nevertheless, I expect there are things that might have been implemented or type hinted wrong at this point. Especially if you stress the limits of the definitions or functionality.
Contributing
Feel free to add any PR, comments, issues or discussions!
Documentation
See Documentation.org
file (Emacs org-file).
Other interesting functional programming in PHP libraries/projects
Here will maintain a list of other libraries. Feel free to let me know of one I have missed.
- marcosh/lamphpda
- crell/fp
- loophp/repository-monadic-helper
- haskellcamargo/php-maybe-monad
- tmciver/functional-php
- phel-lang/phel-lang
- fp4php/functional
- krakphp/fn
- prolic/fpp
- phpfn/curry
- mathiasverraes/lambdalicious
- haskellcamargo/yay-partial-functions
- haskellcamargo/php-partial-function-application
- loophp/combinator
- friends-of-reactphp/partial
- fp4php/fp4php
- matteosister/php-curry
- munusphp/munus
- kapolos/pramda
- ace411/bingo-functional
- chippyash/Monad
- jasny/improved-php-function
- pitchart/transformer
- functional-php/fantasy-land
- haskellcamargo/php-church-encoding
- functional-php/pattern-matching
- quack/quack
- functional-php/trampoline
- phunkie/phunkie
- baethon/phln