Skip to content

Commit

Permalink
Merge pull request #173 from boesing/bugfix/remove-purity
Browse files Browse the repository at this point in the history
Remove purity of provided callbacks
  • Loading branch information
boesing committed Jun 13, 2022
2 parents 34c5f65 + a539007 commit 45ba7f4
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 48 deletions.
8 changes: 4 additions & 4 deletions src/ArrayInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ public function isEmpty(): bool;
/**
* Tests if all elements satisfy the given predicate.
*
* @psalm-param pure-callable(TValue):bool $callback
* @psalm-param callable(TValue):bool $callback
*/
public function allSatisfy(callable $callback): bool;

/**
* Tests for the existence of an element that satisfies the given predicate.
*
* @psalm-param pure-callable(TValue):bool $callback
* @psalm-param callable(TValue):bool $callback
*/
public function exists(callable $callback): bool;

Expand All @@ -71,8 +71,8 @@ public function count(): int;

/**
* @template TReducedValue
* @param pure-callable(TReducedValue,TValue):TReducedValue $callback
* @param TReducedValue $initial
* @param callable(TReducedValue,TValue):TReducedValue $callback
* @param TReducedValue $initial
* @return TReducedValue
*/
public function reduce(callable $callback, $initial);
Expand Down
14 changes: 13 additions & 1 deletion src/Array_.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ protected function valueComparator(): callable
public function allSatisfy(callable $callback): bool
{
foreach ($this->data as $value) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
if (! $callback($value)) {
return false;
}
Expand All @@ -118,6 +122,10 @@ public function allSatisfy(callable $callback): bool
public function exists(callable $callback): bool
{
foreach ($this->data as $value) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
if ($callback($value)) {
return true;
}
Expand All @@ -131,7 +139,11 @@ public function reduce(callable $callback, $initial)
{
$instance = clone $this;

/** @psalm-suppress MixedReturnStatement The return value of the callback ensures the return type. */
/**
* @psalm-suppress MixedReturnStatement The return value of the callback ensures the return type.
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
return array_reduce($instance->data, $callback, $initial);
}
}
58 changes: 52 additions & 6 deletions src/Map.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ public function sort(?callable $callback = null): MapInterface
return $instance;
}

/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
uasort($data, $callback);
$instance->data = $data;

Expand All @@ -84,10 +88,14 @@ public function diffKeys(MapInterface $other, ?callable $keyComparator = null):

/**
* @psalm-var array<TKey,TValue> $diff1
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$diff1 = array_diff_ukey($instance->data, $otherData, $keyComparator);
/**
* @psalm-var array<TKey,TValue> $diff2
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$diff2 = array_diff_ukey($otherData, $instance->data, $keyComparator);
$merged = array_merge(
Expand All @@ -101,7 +109,7 @@ public function diffKeys(MapInterface $other, ?callable $keyComparator = null):
}

/**
* @psalm-return pure-callable(TKey,TKey):int
* @psalm-return callable(TKey,TKey):int
*/
private function keyComparator(): callable
{
Expand All @@ -118,6 +126,10 @@ public function toOrderedList(?callable $sorter = null): OrderedListInterface

$data = $this->data;

/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
usort($data, $sorter);

return new GenericOrderedList($data);
Expand All @@ -128,6 +140,10 @@ public function filter(callable $callback): MapInterface
$instance = clone $this;
$filtered = [];
foreach ($instance->data as $key => $value) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
if (! $callback($value, $key)) {
continue;
}
Expand Down Expand Up @@ -179,8 +195,8 @@ public function intersect(MapInterface $other, ?callable $valueComparator = null

/**
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-param (pure-callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (pure-callable(TKey,TKey):int)|null $keyComparator
* @psalm-param (callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (callable(TKey,TKey):int)|null $keyComparator
* @psalm-return array<TKey,TValue>
* @phpcsSuppress SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedMethod
*/
Expand All @@ -189,6 +205,8 @@ private function intersection(MapInterface $other, ?callable $valueComparator, ?
if ($valueComparator && $keyComparator) {
/**
* @psalm-var array<TKey,TValue> $intersection
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$intersection = array_uintersect_uassoc(
$this->data,
Expand All @@ -203,6 +221,8 @@ private function intersection(MapInterface $other, ?callable $valueComparator, ?
if ($keyComparator) {
/**
* @psalm-var array<TKey,TValue> $intersection
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$intersection = array_intersect_ukey($this->data, $other->toNativeArray(), $keyComparator);

Expand All @@ -215,6 +235,8 @@ private function intersection(MapInterface $other, ?callable $valueComparator, ?

/**
* @psalm-var array<TKey,TValue> $intersection
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$intersection = array_uintersect($this->data, $other->toNativeArray(), $valueComparator);

Expand Down Expand Up @@ -252,6 +274,8 @@ public function diff(MapInterface $other, ?callable $valueComparator = null): Ma
{
/**
* @psalm-var array<TKey,TValue> $diff1
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$diff1 = array_udiff(
$this->toNativeArray(),
Expand All @@ -261,6 +285,8 @@ public function diff(MapInterface $other, ?callable $valueComparator = null): Ma

/**
* @psalm-var array<TKey,TValue> $diff2
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$diff2 = array_udiff(
$other->toNativeArray(),
Expand Down Expand Up @@ -303,13 +329,17 @@ public function removeElement($element): MapInterface

/**
* @template TNewValue
* @psalm-param pure-callable(TValue,TKey):TNewValue $callback
* @psalm-param callable(TValue,TKey):TNewValue $callback
* @psalm-return MapInterface<TKey,TNewValue>
*/
public function map(callable $callback): MapInterface
{
$data = [];
foreach ($this->data as $key => $value) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$data[$key] = $callback($value, $key);
}

Expand All @@ -326,6 +356,10 @@ public function partition(callable $callback): array
$filtered = $unfiltered = [];

foreach ($this->data as $key => $element) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
if ($callback($element)) {
$filtered[$key] = $element;
continue;
Expand All @@ -344,7 +378,7 @@ public function partition(callable $callback): array

/**
* @template TGroup of non-empty-string
* @psalm-param pure-callable(TValue):TGroup $callback
* @psalm-param callable(TValue):TGroup $callback
*
* @psalm-return MapInterface<TGroup,MapInterface<TKey,TValue>>
*/
Expand All @@ -356,6 +390,10 @@ public function group(callable $callback): MapInterface
$groups = new GenericMap([]);

foreach ($this->data as $key => $value) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$groupIdentifier = $callback($value);
try {
$group = $groups->get($groupIdentifier);
Expand Down Expand Up @@ -411,6 +449,10 @@ public function sortByKey(?callable $sorter = null): MapInterface
$sorter = $sorter ?? $this->keyComparator();
$data = $this->data;
$instance = clone $this;
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
uksort($data, $sorter);
$instance->data = $data;

Expand All @@ -429,7 +471,7 @@ public function join(string $separator = ''): string

/**
* @template TNewKey of string
* @param pure-callable(TKey,TValue):TNewKey $keyGenerator
* @param callable(TKey,TValue):TNewKey $keyGenerator
*
* @return MapInterface<TNewKey,TValue>
* @throws RuntimeException if a new key is being generated more than once.
Expand All @@ -440,6 +482,10 @@ public function keyExchange(callable $keyGenerator): MapInterface
$exchanged = new GenericMap();

foreach ($this->data as $key => $value) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$newKey = $keyGenerator($key, $value);
if ($exchanged->has($newKey)) {
throw new RuntimeException(sprintf(
Expand Down
32 changes: 16 additions & 16 deletions src/MapInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface MapInterface extends ArrayInterface, JsonSerializable
* Filters out all values not matched by the callback.
* This method is the equivalent of `array_filter`.
*
* @psalm-param pure-callable(TValue,TKey):bool $callback
* @psalm-param callable(TValue,TKey):bool $callback
* @psalm-return MapInterface<TKey,TValue>
*/
public function filter(callable $callback): MapInterface;
Expand All @@ -29,7 +29,7 @@ public function filter(callable $callback): MapInterface;
* Sorts the items by using either the given callback or the native `SORT_NATURAL` logic of PHP.
* This method is the equivalent of `sort`/`usort`.
*
* @psalm-param (pure-callable(TValue,TValue):int)|null $callback
* @psalm-param (callable(TValue,TValue):int)|null $callback
* @psalm-return MapInterface<TKey,TValue>
*/
public function sort(?callable $callback = null): MapInterface;
Expand All @@ -51,7 +51,7 @@ public function merge(MapInterface ...$stack): MapInterface;
* This method is the equivalent of `array_diff`.
*
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-param (pure-callable(TKey,TKey):int)|null $keyComparator
* @psalm-param (callable(TKey,TKey):int)|null $keyComparator
* @psalm-return MapInterface<TKey,TValue>
*/
public function diffKeys(MapInterface $other, ?callable $keyComparator = null): MapInterface;
Expand All @@ -61,7 +61,7 @@ public function diffKeys(MapInterface $other, ?callable $keyComparator = null):
* This method is the equivalent of `array_map`.
*
* @template TNewValue
* @psalm-param pure-callable(TValue,TKey):TNewValue $callback
* @psalm-param callable(TValue,TKey):TNewValue $callback
* @psalm-return MapInterface<TKey,TNewValue>
*/
public function map(callable $callback): MapInterface;
Expand All @@ -74,7 +74,7 @@ public function map(callable $callback): MapInterface;
* This method is the equivalent of `array_intersect`.
*
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-param (pure-callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (callable(TValue,TValue):int)|null $valueComparator
* @psalm-return MapInterface<TKey,TValue>
*/
public function intersect(MapInterface $other, ?callable $valueComparator = null): MapInterface;
Expand All @@ -87,7 +87,7 @@ public function intersect(MapInterface $other, ?callable $valueComparator = null
* This method is the equivalent of `array_diff`.
*
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-param (pure-callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (callable(TValue,TValue):int)|null $valueComparator
* @psalm-return MapInterface<TKey,TValue>
*/
public function diff(MapInterface $other, ?callable $valueComparator = null): MapInterface;
Expand All @@ -97,7 +97,7 @@ public function diff(MapInterface $other, ?callable $valueComparator = null): Ma
* The items are being sorted by using the provided sorter. In case there is no sorter provided, the values
* are just passed in the order they werestored in this map.
*
* @psalm-param (pure-callable(TValue,TValue):int)|null $sorter
* @psalm-param (callable(TValue,TValue):int)|null $sorter
* @psalm-return OrderedListInterface<TValue>
*/
public function toOrderedList(?callable $sorter = null): OrderedListInterface;
Expand Down Expand Up @@ -163,7 +163,7 @@ public function get(string $key);
*
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-return MapInterface<TKey,TValue>
* @psalm-param (pure-callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (callable(TValue,TValue):int)|null $valueComparator
*/
public function intersectAssoc(MapInterface $other, ?callable $valueComparator = null): MapInterface;

Expand All @@ -174,7 +174,7 @@ public function intersectAssoc(MapInterface $other, ?callable $valueComparator =
*
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-return MapInterface<TKey,TValue>
* @psalm-param (pure-callable(TKey,TKey):int)|null $keyComparator
* @psalm-param (callable(TKey,TKey):int)|null $keyComparator
*/
public function intersectUsingKeys(MapInterface $other, ?callable $keyComparator = null): MapInterface;

Expand All @@ -186,8 +186,8 @@ public function intersectUsingKeys(MapInterface $other, ?callable $keyComparator
* This method is the equivalent of `array_intersect_assoc`/`array_intersect_uassoc`/`array_intersect_key`/`array_intersect_ukey`.
*
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-param (pure-callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (pure-callable(TKey,TKey):int)|null $keyComparator
* @psalm-param (callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (callable(TKey,TKey):int)|null $keyComparator
* @psalm-return MapInterface<TKey,TValue>
*/
public function intersectUserAssoc(
Expand All @@ -207,7 +207,7 @@ public function has(string $key): bool;
/**
* Partitions the current map into those items which are filtered by the callback and those which don't.
*
* @psalm-param pure-callable(TValue):bool $callback
* @psalm-param callable(TValue):bool $callback
* @psalm-return array{0:MapInterface<TKey,TValue>,1:MapInterface<TKey,TValue>}
*/
public function partition(callable $callback): array;
Expand All @@ -216,7 +216,7 @@ public function partition(callable $callback): array;
* Groups the items of this object by using the callback.
*
* @template TGroup of non-empty-string
* @psalm-param pure-callable(TValue):TGroup $callback
* @psalm-param callable(TValue):TGroup $callback
*
* @psalm-return MapInterface<TGroup,MapInterface<TKey,TValue>>
*/
Expand All @@ -235,15 +235,15 @@ public function slice(int $length): MapInterface;
* After the method has been executed properly, an `MappedErrorCollection` is being thrown so one can
* see what item key actually failed executing the callback.
*
* @param pure-callable(TValue,TKey):void $callback
* @param callable(TValue,TKey):void $callback
* @throws MappedErrorCollection If an error occured during execution.
*/
public function forAll(callable $callback): ForAllPromiseInterface;

/**
* Sorts the map by sorting its keys.
*
* @param (pure-callable(TKey,TKey):int)|null $sorter
* @param (callable(TKey,TKey):int)|null $sorter
*
* @psalm-return MapInterface<TKey,TValue>
*/
Expand All @@ -261,7 +261,7 @@ public function join(string $separator = ''): string;
* Creates a new map where the keys of items might have been exchanged with another key.
*
* @template TNewKey of string
* @param pure-callable(TKey,TValue):TNewKey $keyGenerator
* @param callable(TKey,TValue):TNewKey $keyGenerator
*
* @return MapInterface<TNewKey,TValue>
* @throws RuntimeException if a new key is being generated more than once.
Expand Down

0 comments on commit 45ba7f4

Please sign in to comment.