PHP Functional Programming library. Monads and common use functions.

Overview

Functional PHP

PHP Functional Programming library. Monads and common use functions.

psalm level psalm type coverage phpunit coverage

Documentation

Installation

Composer

$ composer require whsv26/functional

Enable psalm plugin (optional)

To improve type inference for particular functions

$ vendor/bin/psalm-plugin enable Fp\\Psalm\\FunctionalPlugin

Contribution

Build documentation

  1. Install dependencies
[email protected]:~$ sudo apt install pandoc
  1. Generate doc from src
Issues
  • Improve type inference for Option and Either assertion

    Improve type inference for Option and Either assertion

    This PR aim to fix wrong type inference for Option and Either assertion methods. Example of correct inference:

    /**
     * @return Option<int>
     */
    function getOption(): Option
    {
        return Option::none();
    }
    
    /** @psalm-param (Some<int>) $_some */
    function assertIsSome($_some): void {}
    
    /** @psalm-param (None) $_none */
    function assertIsNone($_none): void {}
    
    function test(): void
    {
        $opt = getOption();
    
        if ($opt->isSome()) {
            assertIsSome($opt); // $opt is Some<int> at this point
        } else {
            assertIsNone($opt); // $opt is None, before it was Option<int>
        }
    }
    
    opened by klimick 4
  • Q: why does Option::get() may return null?

    Q: why does Option::get() may return null?

    Just noticed that Option::get() may return null value. Are there any specific reasons for this behavior?

    I mean, there are other implementations of Option and non of them do return null value.

    Java

    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
    
        return value;
    }
    

    Scala

    def get: Nothing = throw new NoSuchElementException("None.get")
    

    Rust

    pub fn unwrap(self) -> T {
        match self {
            Some(val) => val,
            None => panic!("called `Option::unwrap()` on a `None` value"),
        }
    }
    

    Maybe there is a contract for Option to have that kind of behavior?

    opened by nzour 3
  • Add method match on Option and Either?

    Add method match on Option and Either?

    Greetings, sir. Just got a few thoughts about implementing method match on Option and Either.

    Like C# functional lib has

    This would look like

    Option:

    $hello = Option::of('Hello');
    
    $result = $hello->match(
        some: fn(string $v) => "{$v} world",
        none: fn() => 'Hello from none'
    );
    

    Either:

    /** @var Either<string, FooInterface> $found */
    $found = Some::of(new FooImpl());
    
    $found->match(
        right: \Fp\id,
        left: function(string $errorMessage) {
            // sorry for side effect here
            $this->logger->logWarning($errorMessage);
    
            return new BarImpl();
        },
    );
    

    So the method signature would look like:

    Option:

    /**
     * @template TR
     * @param callable(A): TR $some
     * @param callable(): TR $none
     * @return TR
     */
    public function match(callable $some, callable $none): mixed
    {
    

    Either:

    /**
     * @template TR
     * @param callable(R): TR $right
     * @param callable(L): TR $left
     * @return TR
     */
    public function match(callable $right, callable $left): mixed
    {
    

    I think this may be useful. What do you think?

    opened by nzour 3
  • Do we need function 'but_last' ?

    Do we need function 'but_last' ?

    Been thinking about function but_last, like this

    I am not sure if this function is necessary. Also we just may code something like this:

    $withoutLast = \Fp\Collection\reverse(
        \Fp\Collection\tail(
            \Fp\Collection\reverse($collection)
        )
    );
    
    // or simplified version
    
    $withoutLast = reverse(tail(reverse($collection)));
    

    Maybe native function but_last would be more understandable and less resource consuming? I would be pleasured to hear your opinion.

    opened by nzour 3
  • Any cases to add @psalm-pure for really pure functions?

    Any cases to add @psalm-pure for really pure functions?

    In some cases it may be useful to use few functions inside pure context.

    Example:

    /**
     * @psalm-immutable
     */
    class Foo
    {
        /** 
         * @var list<Bar>
         */
        public array $bars;
    
        public function __construct(Bar $first, Bar ...$rest)
        {
            // Impure method call issue occurred here (because Foo is immutable, so requires pure context)
            $this->bars = asList([$first], $rest);
        }
    }
    

    Of course, there are more ways to compile list, but if we have asList why wouldn't we use it whenever we want?

    I was just thinking about function asList, maybe there are few others function that are actually pure but not annotated yet

    opened by nzour 2
  • Option::do implementation incorrect

    Option::do implementation incorrect

    This snippet type checks by psalm, but at runtime it fails:

    /**
     * @return Option<stdClass>
     */
    function toSomething(mixed $_): Option
    {
        return Option::none();
    }
    
    $items = [];
    
    Option::do(function() use ($items) {
        $mapped = [];
    
        /** @psalm-suppress MixedAssignment */
        foreach ($items as $item) {
            $mapped[] = yield toSomething($item);
        }
    
        return $mapped;
    });
    
    PHP Fatal error:  Uncaught Error: Call to a member function isEmpty() on null in /home/app/vendor/whsv26/functional/src/Fp/Functional/Option/Option.php:82
    Stack trace:
    #0 /home/app/run.php(26): Fp\Functional\Option\Option::do()
    #1 {main}
      thrown in /home/app/vendor/whsv26/functional/src/Fp/Functional/Option/Option.php on line 82
    
    Process finished with exit code 255
    

    This only happens if no yield has been called in Option::do.

    opened by klimick 1
  • Allow `null` values for `Option`

    Allow `null` values for `Option`

    /**
     * Returns value from $array by $path if present
     *
     * @param non-empty-list<string> $path
     * @psalm-pure
     */
    function dotAccess(array $path, array $array): Option
    {
        $key = $path[0];
        $rest = array_slice($path, offset: 1);
    
        if (empty($rest) && array_key_exists($key, $array)) {
            return Option::of($array[$key]);
        }
    
        if (array_key_exists($key, $array) && is_array($array[$key])) {
            return dotAccess($rest, $array[$key]);
        }
    
        return new None();
    }
    
    $arr = [
        'person' => [
            'name' => 'Jhon',
            'address' => null,
        ],
    ];
    
    // Prints Some('Jhon') - good
    print_r(dotAccess(['person', 'name'], $arr));
    
    // Prints None - not good
    print_r(dotAccess(['person', 'address'], $arr));
    
    
    opened by klimick 1
  • Add proveNonEmptyString

    Add proveNonEmptyString

    It would be great if someone implement proveNonEmptyString

    opened by klimick 1
  • Add Either::mapLeft

    Add Either::mapLeft

    Currently there is no way to change left side of the Either.

    Syntactic example where it can be useful:

    /**
     * @return Either<ApplicationError, OrderOutput>
     */
    function createOrder(OrderInput $input, OrderService $service): Either
    {
        return $service->create($input)
            ->map(fn(Order $o) => OrderOutput::from($o))
            ->mapLeft(fn(OrderError $e) => ApplicationError::from($e));
            // map concrete domain error to more generic error
    }
    
    
    opened by klimick 1
  • Fp\Json\jsonDecode is unsafe

    Fp\Json\jsonDecode is unsafe

    If you run the following piece of code, it will lead to a runtime error:

    <?php
    
    use Fp\Functional\Either\Right;
    use Fp\Functional\Either\Left;
    use function Fp\Json\jsonDecode;
    
    require __DIR__ . '/vendor/autoload.php';
    
    $decoded = jsonDecode('"kek"')
        ->map(fn(array $val) => array_merge($val, ['lol' => '_']));
    
    
    
    PHP Fatal error:  Uncaught TypeError: {closure}(): Argument #1 ($val) must be of type array, string given, called in /home/php/whsv26/functional/src/Fp/Functional/Either/Either.php on line 83 and defined in /home/php/whsv26/functional/run.php:12
    Stack trace:
    #0 /home/php/whsv26/functional/src/Fp/Functional/Either/Either.php(83): {closure}()
    #1 /home/php/whsv26/functional/run.php(15): Fp\Functional\Either\Either->flatMap()
    #2 {main}
      thrown in /home/php/whsv26/functional/run.php on line 12
    
    

    Because json_decode can deserialize literals like numbers, strings, booleans.

    bug 
    opened by klimick 1
Releases(v2.0.3)
  • v2.0.3(Jun 9, 2021)

    • RegExpMatch function
    • ProveListOfScalar function
    • Option filter method and psalm plugin for this method
    • Semigroup, Monoid interfaces and base instances
    • Validated refactoring
    Source code(tar.gz)
    Source code(zip)
  • v2.0.2(May 29, 2021)

    • Full runtime unit test coverage
    • Option and Either "orElse" method has been added
    • REPL documentation for every method and function
    • CI/CD pipeline: psalm -> unit testing -> unit tests coverage level
    • New function "butLast"
    • Improved type inference
    • Method "getOrElse" can be called with callable fallback value now
    Source code(tar.gz)
    Source code(zip)
Owner
Alexander Sv.
Alexander Sv.
Phan is a static analyzer for PHP. Phan prefers to avoid false-positives and attempts to prove incorrectness rather than correctness.

Phan is a static analyzer for PHP that prefers to minimize false-positives. Phan attempts to prove incorrectness rather than correctness. Phan looks f

null 5.1k Jun 11, 2021
A full-scale PHP 5.3.2+ sandbox class that utilizes PHPParser to prevent sandboxed code from running unsafe code.

##DEPRECATED: The PHPSandbox project has transfered to Corveda/PHPSandbox and will be actively maintained there. This branch is no longer being active

Elijah Horton 219 May 7, 2021
A tool for quickly measuring the size of a PHP project.

PHPLOC phploc is a tool for quickly measuring the size and analyzing the structure of a PHP project. Installation This tool is distributed as a PHP Ar

Sebastian Bergmann 2.1k Jun 9, 2021
PHP Compatibility check for PHP_CodeSniffer

PHP Compatibility Coding Standard for PHP CodeSniffer This is a set of sniffs for PHP CodeSniffer that checks for PHP cross-version compatibility. It

PHPCompatibility 1.6k Jun 14, 2021
:crystal_ball: Better Reflection is a reflection API that aims to improve and provide more features than PHP's built-in reflection API.

Better Reflection Better Reflection is a reflection API that aims to improve and provide more features than PHP's built-in reflection API. Why is it b

Roave, LLC 905 Jun 14, 2021
Keep your architecture clean.

Deptrac What is Deptrac Deptrac is a static code analysis tool that helps to enforce rules for dependencies between software layers in your PHP projec

QOSSMIC GmbH 1.6k Jun 16, 2021
PHP Benchmarking Framework

Athletic Athletic is a benchmarking framework. It allows developers to benchmark their code without littering microtime() calls everywhere. Athletic w

Zachary Tong 315 Jun 7, 2021
Shade/Inject composer dependencies into PocketMine-MP plugins.

ComposerShader README for v0.2.0-dev Important Note: This is not perfect, nor will it ever be, with several checks for common uses of certain function

Jack Honour 7 May 19, 2021
A PHP VM implementation in PHP

PHPPHP A PHP VM implementation written in PHP. This is a basic VM implemented in PHP using the AST generating parser developed by @nikic To see what's

Anthony Ferrara 788 Jun 20, 2021
Analyze PHP code with one command

PHPQA Analyze PHP code with one command. Requirements PHP >= 5.4.0 xsl extension for HTML reports Why? Every analyzer has different arguments and opti

edgedesign/phpqa 518 Jun 14, 2021
Instant Upgrades and Instant Refactoring of any PHP 5.3+ code

Rector - Speedup Your PHP Development Rector helps you with 2 areas - major code changes and in daily work. Do you have a legacy code base? Do you wan

RectorPHP 4.2k Jun 13, 2021
A static analyzer for PHP version migration

PHP Migration Readme in Chinese 中文 This is a static analyzer for PHP version migration and compatibility checking. It can suppose your current code ru

Yuchen Wang 188 Jun 19, 2021
Tool helping us to analyze software projects

Qafoo Quality Analyzer This software is a tool to visualize metrics and source code. We use this software for Code Reviews together with our customers

Qafoo GmbH 494 May 21, 2021
A PHP code-quality tool

GrumPHP Sick and tired of defending code quality over and over again? GrumPHP will do it for you! This composer plugin will register some git hooks in

PHPro 3.5k Jun 11, 2021