lstrojny / functional-php Goto Github PK
View Code? Open in Web Editor NEWPrimitives for functional programming in PHP
License: MIT License
Primitives for functional programming in PHP
License: MIT License
I'm curious why map() when called with a traversable doesn't return a traversable? Seems odd. What's the design decision there? Simplicity?
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
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?
This would let you do things like:
$trimAll = map(trim);
$trimAll([' a', 'b ', ' c ']); // results in: ['a', 'b', 'c']
Allow passing callables to F\last_index_of()
and F\first_index_of()
additionally to fixed values.
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);
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
}
Imagine where a library uses functional-php's userland implementation, but you've already installed the extension, should the userland implementation give way to the installed extension instead of overwriting it?
Why new array of groups keep old index?
foreach ($collection as $index => $element) {
...
$groups[$groupKey][$index] = $element;
The C implementation of last_index_of()
and first_index_of()
is currently slower than the userland equivalent. Look for ways to improve them.
Fatal error: Parameter $callbacks is variadic and has a type constraint (callable); variadic params with type constraints are not supported in /home/travis/build/phpbench/phpbench/vendor/lstrojny/functional-php/src/Functional/Partition.php on line 41
As encountered in: https://travis-ci.org/phpbench/phpbench/jobs/177463602#L402
1.2.4 works fine
Hi,
I am pretty interested in that extension, would you mind moving the extension into a separated repository?
It's pretty unusual behavior for compose
to execute left to right. Every other compose
implementation I've come across executes right to left. One other issues is that this behavior isn't documented in docs/functional-php.md
The C ext does not compile with 5.5 currently: https://travis-ci.org/lstrojny/functional-php/jobs/6356030
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;
}, []);
}
??
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!)
Please tag the latest 5.3 release. I am attempting to target the release with composer using "lstrojny/functional-php": "dev-master#a91a38bf25a0dbe41a29b23321ad56647389a4ca",
but I am still getting an error because it is reading the metadata from the master branch: https://getcomposer.org/doc/04-schema.md#package-links
This will not work as documented:
$f = static function ($x) { return $x + 1; };
with($f, partial_any('map', [1, 2, 3], …));
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.
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
.
The C implementation for F\memoize() is missing.
The C implementation for F\invoke_if()
is missing. Add it!
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];
};
}
$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.
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?
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)
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.
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.
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"?
Behave like usort
but for traversables and arrays and behaves deterministic
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?
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.
Just wondering if it should be part of the repo :-)
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.
Would a partition function similar to the lodash one be something useful here? I can work on it.
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.
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
A stable 1.0 version is on the way anytime soon?
Many thanks in advance. This is an awesome library!
The docs shouldn’t be part of the README but available at php.net.
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.
Is there any convenience or thought about putting the $reduction
as the last argument in the function that take reduce_right|left
?
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!
The C implementation of drop_first()
for iterators over hashes is currently slower than the userland equivalent.
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!
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?
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.
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?
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'
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!
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?
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']
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.