Git Product home page Git Product logo

functional-php's Introduction

Functional PHP: Functional primitives for PHP

Test

NOTE: functional-php used to come with a C extension that implemented most of the functions natively. As the performance differences weren’t that huge compared to the maintenance cost it has been removed.

A set of functional primitives for PHP, heavily inspired by Scala’s traversable collection, Dojo’s array functions and Underscore.js

  • Works with arrays and everything implementing interface Traversable
  • Consistent interface: for functions taking collections and callbacks, first parameter is always the collection, then the callback. Callbacks are always passed $value, $index, $collection. Strict comparison is the default but can be changed
  • Calls 5.3 closures as well as usual callbacks
  • All functions reside in namespace Functional to not raise conflicts with any other extension or library

Functional Comic

Installation

Run the following command in your project root:

composer require lstrojny/functional-php

Docs

Read the docs

Contributing

  1. Fork and git clone the project
  2. Install dependencies via composer install
  3. Run the tests via composer run tests
  4. Write code and create a PR

Mailing lists

Thank you

functional-php's People

Contributors

adrienbrault avatar alexeyshockov avatar bor0 avatar colinodell avatar dsp avatar elazar avatar erikvv avatar gabrielhomsi avatar jasonmm avatar krtek4 avatar leocavalcante avatar lstrojny avatar mattjmattj avatar mortalflesh avatar mschwager avatar naderman avatar oguzdumanoglu avatar peter279k avatar phanan avatar pierrejoye avatar povilasb avatar pyldin601 avatar rodnaph avatar royopa avatar rquadling avatar scrutinizer-auto-fixer avatar someonewithpc avatar soutoner avatar szepeviktor avatar trebi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

functional-php's Issues

More efficient autoloading?

This is a great library, but unfortunately we may end up having to uninstall it because installing it with Composer has added a huge difference to the page load, as profiled with BlackFire.

(Sadly autoloading functions won't be added to PHP anytime soon).

One solution I thought of it adding generation of the functions to Symfony's bootstrap.php.cache file.

However I'm wondering if this library would benefit from providing its own solution for optimizing the loading of functions?

screen shot 2016-03-31 at 11 59 28

screen shot 2016-03-31 at 11 59 54

Collection pipeline feature

Collectione pipeline is a nice design pattern to transform your collections: http://martinfowler.com/articles/collection-pipeline/.

It would be really nice if this library supported java 8 like streams (https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html):

int sum = widgets.stream()
                  .filter(w -> w.getColor() == RED)
                  .mapToInt(w -> w.getWeight())
                  .sum();

I guess it should't be too hard. The transformation functions should return some kind of Stream instance instead of an array.

with has undocumented behaviour

This will not work as documented:

$f = static function ($x) { return $x + 1; };
with($f, partial_any('map', [1, 2, 3], …));

zip: keys are taken from the first array

I would expect it to look in all arrays like it does in lodash. And fill the missing indexes with null like it does now.

I can provide a fix if you want, or maybe we can define another function, maybe 'zipLongest'

Introduce autocurring and function composition

What do you think about introducing to functional php autocurring and function composition?

I have created working prototype of library with autocurring, function composition and making$collection as last argument: https://github.com/psliwa/rfp

In my opinion it looks promising, allows to take advantage of functional programming in even better way. There is the chance to make new release, aka "functional php 2"?

Comparison operations API

About implementing comparison operations as functions, before I'd like to discuss the API first.
Operations: http://php.net/manual/en/language.operators.comparison.php

Suggestions are:

PHP Doc Name API
Equal equal, equals, eq
Identical identical, strict_eq, seq
Not equal not_equal, not_equals, neq
Not identical not_identical, not_strict_eq, nseq
Less than less_than, lt
Greater than greater_than, gt
Less than or equal to less_than_or_eq, lte
Greater than or equal to greater_than_or_eq, gte
Spaceship spaceship, lteqgt

I personally prefer the abbreviations. e.g.:

// using cond/always from #123

$message = cond([
    [lte(16), always('Can drive in USA')],
    [lte(18), always('Can drive and drink in Brazil')],
    [lte(21), always('Can drink in USA')],
    [gte(60), always('Getting old')],
]);

echo $message(18); // ~> Can drive and drink in Brazil
echo $message(60); // ~> Getting old

Compose, partial application, currying?

Are there any plans for things like partial function application? Anything like currying or composition? Would pull requests be welcome for something like this or does it go against some design goal?

Cool library btw.

partition function

Would a partition function similar to the lodash one be something useful here? I can work on it.

Older stable releases on packagist?

There are missing older stable releases on packagist. Do you recommend using newer alfa version in production? If not please add them to packagist. It will make our live easier. Thanks!

Shouldn't moving to PHP7 be a major version release? (1.0 = php5, 1.2 = php7)

According to most semantic versioning guidelines (and primarily the ones used by composer on most projects) it should go:

major.minor.bug

Where major allows non-backwards compatible changes, minor is always backwards compatible but offers new features, and bug is for general bugfix/maintenance.

In the case of the lstrojny/functional-php package, it is possible to have a composer.json with something like ~1.0 for the pinned functional-php version, run it on a PHP7 machine and end up with a composer.lock file that is no longer compatible on a PHP5.x machine (therefore requiring manually figuring out the last version that worked with PHP5.x and pinning to an exact version like 1.0.0).

For this reason, I strongly advocate in favor of making the PHP7+ release lstrojny/functional-php 2.x.x (don't be afraid to increment that major version!)

Unable to install C extension on PHP 5.5.20

Both sudo pecl install functional-0.6.0 and sudo pear upgrade -f pecl/functional end with ERROR:make' failed`.

Based on the stack trace, I think it's incompatabilities with the new PHP ZendEngine. The log filtering for 'error':

/tmp/pear/temp/functional/functional.c:451:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:451:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:492:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:492:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:539:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:539:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:585:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:585:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:629:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:629:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:675:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:675:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:719:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:719:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:777:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:777:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:830:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:830:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:880:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:880:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:941:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:941:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:999:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:999:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:1045:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:1045:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:1098:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:1098:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:1153:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:1153:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:1200:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:1200:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:1333:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:1333:4: error: void value not ignored as it ought to be
/tmp/pear/temp/functional/functional.c:1554:4: error: too many arguments to function ‘zend_user_it_get_current_key’
/tmp/pear/temp/functional/functional.c:1554:4: error: void value not ignored as it ought to be

I can post the full logs if you want.

[RFC] provide a `not` function

Is it me or one currently can't invert a boolean-returning function ?

I'd like a not function looking like:

namespace Functional;

function not(callable $callback) {
    return function($value) use ($callback) {
        return !$callback($value);
    };
}

see https://3v4l.org/G3vtd for a working impl.

Do you think it's a good idea?
If yes, I can try to make a PR.

Thanks!

New function: F\indexes_of()

Returns a list of array indexes, either matching the predicate or strictly equal to the the passed value.

array Functional\indexes_of(Traversable|array $collection, mixed|callable $predicate)

HHVM usage broken

Started getting this error on our Travis builds, first seen today:

Fatal error: Parameter $functions is variadic and has a type constraint (callable); variadic params with type constraints are not supported in /home/travis/build/equip/assist/vendor/lstrojny/functional-php/src/Functional/Compose.php on line 54

Full build is here https://travis-ci.org/equip/assist/jobs/141634731

sort is not reliable

Every few (between 3 and 10 times) I use sort() comparing floats the returned array is in backwards order.

$sortedRates = F\sort($rates, function($r1, $r2) {
    if($r2->weight === $r1->weight) {
        return 0;
    }

    return ($r1->weight < $r2->weight) ? -1 : 1;
});

weight are floats. I expect the returned array to be in ascending order. It unexpectedly comes back in descending order every so often.

Possibility of C Extension in future releases

Some background. I found out about this library via the Functional Programming in PHP book, which is a great read. The author recommends this library and rightly so — it looks really great! I'm excited to start using this more in my work.

I'm opening this issue because I was a little confused. The Functional Programming in PHP author recommended using this library via the C-extension but when I came to this repo I couldn't find the C-source. Then I saw the PECL link in the repo's description, which is clearly out of date and was quite confused if I even had the correct lib. It took a little digging to find the Removing C extension commit and the 1.0 Roadmap issue which illuminate the deprecation of the C-extension.

I'm opening this issue partly to suggest updating the readme to highlight the deprecation but also curious if you'd consider implementing the C-extension at some point in the future? I was curious to test out the performance difference between the C-extension and the pure-PHP implementations but it seems like the difference was maybe negligible?

In situations where I have the option I'd tend to prefer using a C-extension if it I knew it had measurable performance benefits, but I suspect that maintaining both code-bases in tandem is more headache than help.

Is it possible to tag a release for composer?

Currently you can only install dev-master which means in composer you have to set the minimum-stability flag to dev (at least for that package). If you could create a tag that would be awesome.

First and last should throw an error if the collection is empty.

I my opinion fist and last should throw sth like a NoSuchElementException if the collection is empty instead of returning null. This would avoid the ambiguity with null as a value.
Further, I would prefer removing the callable argument and introduce a find function for that purpose.

P.S.: Great library. Thanks!

[RFC] `pick` should be renamed `picker` to be consistent with `invoker`

hey there :)

I'd like to have a function that returns the value of an array at a given index for each element of a collection. like invoke does currently for collections of objects).

I think pick should be renamed to picker (like invoker respectively)
and a new pick function would map picker to a collection.

Now I know renaming things is a big BC break so I wouldn't bother naming it differently.
Thoughts?

Implode/Join for lists

I may be missing something here, but is there some kind of join function that behaves like implode but returns a list?

join('-', ['a', 'b', 'c']) === ['a', '-', 'b', '-', 'c']

filter/select by property

$persons = ....
$lars = 'Lars'
$larses = selectByProperty($persons, ['name' => $lars]);

much less verbose than:

$persons = ....
$lars = 'Lars'
$larses = select($persons, function ($person) use ($lars) {
    return $person->name == $lars;
});

It could work with multiple properties too, plus nesting. Like graphQL.

The name, alternatively, could be 'where'.

Good idea?

Ive created an implementation here: https://github.com/Erikvv/array-utils-php/blob/master/src/Ve/Where.php

It's a bit too large to be contained in one function.

Add sort function

Behave like usort but for traversables and arrays and behaves deterministic

Documentation for drop_first and drop_last

The examples show a return value $index === 2 which should be $index <= 2. The records are dropped as long as the callback returns true. Records are kept when the callback returns false.

Why does \filter sometime return an array of arrays?

Say we have a $towns array:

[
  {
    town: "Easton",
    customers: 5
  }, {
    town: "Andover",
    customers: 9
  }
]

The following

$result = F\filter($towns, function($town) {
  return $town->customers > 5;
});

returns an array of items keyed by the original position:

{
  1: {
    town: "Andover",
    customers: 9
  }
}

but comparing greater than -1

$result = F\filter($towns, function($town) {
  return $town->customers > -1;
});

returns the expected structure

[
  {
    town: "Easton",
    customers: 5
  }, {
    town: "Andover",
    customers: 9
  }
]

Any thoughts?

Flat map with key preservation

I found myself in need of such a function

So you could transform a list to a dictionary like so:

$dictionary = flatMapWithKeys($list, function($element) {
    $key = ...
    $value = ...
    return [
        $key => $value,
    ];
});

Naive implementation would be something like this:

function flatMapWithKeys(array $array, callable $fn)
{
    $result = [];
    foreach ($array as $key => $value)
    {
        $result += $fn($value, $key);
    }
    return $result;
}

Would this be useful?

Documentation is hard to use

I used to be able to search the usage of functions easily since they were all on the same page. After the docs have bee divided into multiple pages, I find it's rather inconvenient when finding usage for a specify function.

I propose that this repo should revert back to the old way of organization for documentations.

Add poll() and retry()

mixed poll(callable $function, $timeout, Traversable $delay = F\delay_constant(0, 0))
mixed retry(callable $function, $maxRetries, Traversable $delay = F\delay_constant(0, 0))
Traversable delay_constant($start = 0, $increment = 1)
Traversable delay_linear($start = 0, $increment = 1)
Traversable delay_fibonicci($start);

Examples:

Poll for 1000ms with a linear increase:

if (F\poll([$promise, 'isResolved'], 1000, F\linear())) {
   // Promise eventually resolved
}

Retry calling a function ten times with a 10ms delay in between:

if (!F\retry(function() {throw new Exception('Will never work');}, 10, F\constant(0, 10))) {
   // Retry didn’t work
}

the removed C extension

Hi,

I am pretty interested in that extension, would you mind moving the extension into a separated repository?

flip function

Similar to ramda's flip. Sometimes it is beneficial to flip the argument order of a function. Is this something that would be useful in this project?

I would be willing to implement this. Thoughts?

hashMap

For my own use I wrote a function called hashMap to create associative arrays:

>>> hashMap(
...     ['abc', 'foobar'],
...     function ($value) {
...         return [$value[0], $value];
...     }
... )
=> [
     "a" => "abc",
     "f" => "foobar",
   ]
function hashMap($collection, callable $callback)
{
    InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);

    $aggregation = [];

    foreach ($collection as $index => $element) {
        list($key, $value) = $callback($element, $index, $collection);
        $aggregation[$key] = $value;
    }

    return $aggregation;
}

Is this something that would also be useful to others? If yes, I can prepare a proper pull request. Of course it can be named differently if there is a better term for what this function does.

Covinience of the reduce?

Is there any convenience or thought about putting the $reduction as the last argument in the function that take reduce_right|left?

flatten?

why flatten isn't like this:

function flatten($ls)
{
    if ($ls instanceof \ArrayIterator) {
        $ls = flatten($ls->getArrayCopy());
    }

    return array_reduce($ls, function ($carry, $item) {
        if (is_array($item)) {
            return array_merge($carry, flatten($item));
        }

        $carry[] = $item;
        return $carry;
    }, []);
}

??

Bundled functions in single file for production

Hi,
I reckon that including ~70 files on every script execution might be a bit of a performance hit. Would you consider adding a bundled file with all functions inside as some kind of dist "build" and autoload just that file in composer.json definition?

Memoize as a high order function

What do you think about transforming the memoize function into a high order function? I think it would become easier to use from the client perspective and it may simplify its internal implementation. I'm thinking of something like

function memoize(callable $function, callable $hashGenerator = null) {
    if (null === $hashGenerator) {
        $hashGenerator = function(array $arguments) {
            // ...
        }
    }

    return function (...$arguments) use ($function, $hashGenerator) {
        static $localCache = [];

        $key = $hashGenerator($arguments);

        if (!array_key_exists($key, $localCache)) {
            $localCache[$key] = $callback(...$arguments);
        }

        return $localCache[$key];
    };
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.