https://github.com/thgs/functional
(experimental) Trying to express some functional concepts in PHP
https://github.com/thgs/functional
experimental functional-programming php
Last synced: 3 months ago
JSON representation
(experimental) Trying to express some functional concepts in PHP
- Host: GitHub
- URL: https://github.com/thgs/functional
- Owner: thgs
- License: mit
- Created: 2023-10-18T06:20:16.000Z (over 2 years ago)
- Default Branch: master
- Last Pushed: 2025-07-19T14:26:00.000Z (8 months ago)
- Last Synced: 2025-07-19T18:33:07.394Z (8 months ago)
- Topics: experimental, functional-programming, php
- Language: PHP
- Homepage:
- Size: 308 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
## functional
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](https://github.com/marcosh/lamphpda). That is a more complete
library.
From then on, I have deviated considerably and certain decisions are
quite different, for reasons that either I still explore and I am not
certain about or for different goals and priorities.
### Highlight features
#### Basic structures from Haskell
```php
$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
```php
$result = c (fn ($x) => $x + 2) (123); // 125
```
Function composition
```php
$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
```php
$min = c('min');
$maybeArrayOfInt = new Maybe(new Just(range(1,4)));
$maybeMinOfArray = fmap($min, $maybeArrayOfInt);
var_dump($maybeMinOfArray->unwrap());
```
Do notation
```php
// 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
```
Or a more elaborate example
```php
dn(
writeFile("test.txt", show (123 * 123)),
appendFile("test.txt", "Hello!\n"),
readFile("test.txt"),
putStrLn(...)
)();
```
Generalised Notations
By defining the composition between the "elements"
```php
function myNotation(mixed ...$elements) {
return (new LeftToRightNotation(new CategoryOfFunctions()))->composeMany(...$elements);
}
// this will compose a function that first will evaluate the last passed "element"
$composedFunction = myNotation(
fn (int $x): bool => $x == 16,
fn (int $x): int => (int) ($x / 2),
fn (int $x): int => pow($x, 5),
fn (array $items): int => count($items),
array_filter(...)
);
$composedFunction(["one", "two", ""]); // true
```
#### Helpers for your tests
```php
class MyType implements FunctorInstance
{
public function fmap(\Closure $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
```php
/**
* Create a Wrapper with an anonymous function that calls the psr3Logger from a Tuple input.
*/
$wrapper = Wrapper::withAdjustedInput(
fn (Tuple $p) => $psr3Logger -> log ($p->fst(), $p->snd()),
);
/**
* Let's add a prefix
*/
$prefix = 'prefixHere: ';
/**
* Adjust the input
*/
$contextLogger = $wrapper -> adjustInput (fn (Tuple $p): Tuple => t ($prefix . $p->fst(), [$p->snd()]) );
/**
* Adjust the output
*/
$logger = $contextLogger -> adjustOutput (fn () => time());
$currentTime = $logger (t ("Log message", "context"));
```
#### Implement typeclass instances for types
```php
$equalsImplementation = fn (int|float $a, int|float $b): bool => ((int) $a) == ((int) $b);
Eq::register(
instanceName: 'int|float',
typePredicate: fn ($x) => is_int($x) || is_float($x),
equals: $equalsImplementation,
notEquals: null // derived
);
$result = equals(12.6, 12.1); // true
$result = notEquals(12.6, 13.1); // true
```
#### 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`](https://github.com/thgs/functional/blob/master/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](https://github.com/marcosh/lamphpda)
- [crell/fp](https://github.com/crell/fp)
- [loophp/repository-monadic-helper](https://github.com/loophp/repository-monadic-helper)
- [haskellcamargo/php-maybe-monad](https://github.com/haskellcamargo/php-maybe-monad)
- [tmciver/functional-php](https://github.com/tmciver/functional-php)
- [phel-lang/phel-lang](https://github.com/phel-lang/phel-lang)
- [fp4php/functional](https://github.com/fp4php/functional)
- [krakphp/fn](https://github.com/krakphp/fn)
- [prolic/fpp](https://github.com/prolic/fpp)
- [phpfn/curry](https://github.com/phpfn/curry)
- [mathiasverraes/lambdalicious](https://github.com/mathiasverraes/lambdalicious)
- [haskellcamargo/yay-partial-functions](https://github.com/haskellcamargo/yay-partial-functions)
- [haskellcamargo/php-partial-function-application](https://github.com/haskellcamargo/php-partial-function-application)
- [loophp/combinator](https://github.com/loophp/combinator)
- [friends-of-reactphp/partial](https://github.com/friends-of-reactphp/partial)
- [fp4php/fp4php](https://github.com/fp4php/fp4php)
- [matteosister/php-curry](https://github.com/matteosister/php-curry)
- [munusphp/munus](https://github.com/munusphp/munus)
- [kapolos/pramda](https://github.com/kapolos/pramda)
- [ace411/bingo-functional](https://github.com/ace411/bingo-functional)
- [chippyash/Monad](https://github.com/chippyash/Monad)
- [jasny/improved-php-function](https://github.com/jasny/improved-php-function)
- [pitchart/transformer](https://github.com/pitchart/transformer)
- [functional-php/fantasy-land](https://github.com/functional-php/fantasy-land)
- [haskellcamargo/php-church-encoding](https://github.com/haskellcamargo/php-church-encoding)
- [functional-php/pattern-matching](https://github.com/functional-php/pattern-matching)
- [quack/quack](https://github.com/quack/quack)
- [functional-php/trampoline](https://github.com/functional-php/trampoline)
- [phunkie/phunkie](https://github.com/phunkie/phunkie)
- [baethon/phln](https://github.com/baethon/phln)