GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GitHub - thgs/functional: (experimental) Trying to express some functional concepts in PHP

BranchesTags
Open more actions menu

Latest commit

 

History

318 Commits
318 Commits

Folders and files

NameName
Last commit message
Last commit date
.phive
.phive
 
 
examples
examples
 
 
src
src
 
 
tests
tests
 
 
.gitignore
.gitignore
 
 
Documentation.org
Documentation.org
 
 
LICENSE
LICENSE
 
 
Notes.org
Notes.org
 
 
README.md
README.md
 
 
composer.json
composer.json
 
 
composer.lock
composer.lock
 
 
infection.json5
infection.json5
 
 
phpbench.json
phpbench.json
 
 
phpstan.neon
phpstan.neon
 
 
phpunit.xml
phpunit.xml
 
 

Repository files navigation

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. 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

$maybeInt = Maybe::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 = Maybe::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

Or a more elaborate example

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"

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

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

/**
 * 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

$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 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

About

(experimental) Trying to express some functional concepts in PHP

Topics

experimentalfunctional-programmingphp

Resources

Readme
MIT license
Activity

Stars

3 stars

Watchers

1 watching

Forks

0 forks
Report repository

Releases

Used by

Contributors

Languages

You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.