Skip to content

Commit

Permalink
Merge pull request #14 from boesing/feature/immutability
Browse files Browse the repository at this point in the history
  • Loading branch information
boesing committed Nov 6, 2020
2 parents 38ab698 + 6b5efa1 commit 8d7e501
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 78 deletions.
8 changes: 1 addition & 7 deletions src/ArrayInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,31 @@
* @template TKey of array-key
* @template TValue
* @template-extends IteratorAggregate<TKey,TValue>
* @psalm-immutable
*/
interface ArrayInterface extends IteratorAggregate, Countable
{
/**
* @psalm-param TValue $element
* @psalm-mutation-free
*/
public function contains($element): bool;

/**
* @psalm-return TValue
* @psalm-mutation-free
* @throws OutOfBoundsException if there are no values available.
*/
public function first();

/**
* @psalm-return TValue
* @psalm-mutation-free
* @throws OutOfBoundsException if there are no values available.
*/
public function last();

/**
* @psalm-mutation-free
*/
public function isEmpty(): bool;

/**
* @psalm-return array<TKey,TValue>
* @psalm-mutation-free
*/
public function toNativeArray(): array;
}
21 changes: 2 additions & 19 deletions src/Array_.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
* @template TKey of array-key
* @template TValue
* @template-implements ArrayInterface<TKey,TValue>
* @psalm-immutable
*/
abstract class Array_ implements ArrayInterface
{
Expand All @@ -31,7 +32,7 @@ abstract class Array_ implements ArrayInterface
/**
* @psalm-param array<TKey,TValue> $data
*/
public function __construct(array $data)
protected function __construct(array $data)
{
$this->data = $data;
}
Expand All @@ -44,17 +45,11 @@ public function getIterator(): Traversable
return new ArrayIterator($this->data);
}

/**
* @psalm-mutation-free
*/
public function contains($element): bool
{
return in_array($element, $this->data, true);
}

/**
* @psalm-mutation-free
*/
public function first()
{
if ($this->isEmpty()) {
Expand All @@ -64,9 +59,6 @@ public function first()
return reset($this->data);
}

/**
* @psalm-mutation-free
*/
public function last()
{
if ($this->isEmpty()) {
Expand All @@ -76,25 +68,16 @@ public function last()
return end($this->data);
}

/**
* @psalm-mutation-free
*/
public function isEmpty(): bool
{
return $this->count() === 0;
}

/**
* @psalm-mutation-free
*/
public function count(): int
{
return count($this->data);
}

/**
* @psalm-mutation-free
*/
public function toNativeArray(): array
{
return $this->data;
Expand Down
1 change: 1 addition & 0 deletions src/GenericMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* @template TKey of string
* @template TValue
* @template-extends Map<TKey,TValue>
* @psalm-immutable
*/
final class GenericMap extends Map
{
Expand Down
1 change: 1 addition & 0 deletions src/GenericOrderedList.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/**
* @template TValue
* @template-extends OrderedList<TValue>
* @psalm-immutable
*/
final class GenericOrderedList extends OrderedList
{
Expand Down
77 changes: 53 additions & 24 deletions src/Map.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,18 @@
* @template TValue
* @template-extends Array_<TKey,TValue>
* @template-implements MapInterface<TKey,TValue>
* @psalm-immutable
*/
abstract class Map extends Array_ implements MapInterface
{
/**
* @psalm-param array<TKey,TValue> $data
*/
public function __construct(array $data = [])
{
parent::__construct($data);
}

public function merge(...$stack): MapInterface
{
$instance = clone $this;
Expand All @@ -46,15 +55,16 @@ public function merge(...$stack): MapInterface

public function sort(?callable $callback = null): MapInterface
{
$data = $this->data;
$instance = clone $this;
$data = $instance->data;
if ($callback === null) {
asort($data, SORT_NATURAL);
$instance->data = $data;

return $instance;
}

/** @psalm-suppress ImpureFunctionCall */
uasort($data, $callback);
$instance->data = $data;

Expand All @@ -63,13 +73,20 @@ public function sort(?callable $callback = null): MapInterface

public function diffKeys(MapInterface $other, ?callable $keyComparator = null): MapInterface
{
$instance = clone $this;
$otherData = $other->toNativeArray();
$instance = clone $this;
$otherData = $other->toNativeArray();
$keyComparator = $keyComparator ?? $this->keyComparator();

/** @psalm-var array<TKey,TValue> $diff1 */
$diff1 = array_diff_ukey($this->data, $otherData, $keyComparator ?? $this->keyComparator());
/** @psalm-var array<TKey,TValue> $diff2 */
$diff2 = array_diff_ukey($otherData, $this->data, $keyComparator ?? $this->keyComparator());
/**
* @psalm-var array<TKey,TValue> $diff1
* @psalm-suppress ImpureFunctionCall
*/
$diff1 = array_diff_ukey($instance->data, $otherData, $keyComparator);
/**
* @psalm-var array<TKey,TValue> $diff2
* @psalm-suppress ImpureFunctionCall
*/
$diff2 = array_diff_ukey($otherData, $instance->data, $keyComparator);
$merged = array_merge(
$diff1,
$diff2
Expand Down Expand Up @@ -97,22 +114,22 @@ public function toOrderedList(?callable $sorter = null): OrderedListInterface
}

$data = $this->data;

/** @psalm-suppress ImpureFunctionCall */
usort($data, $sorter);

return new GenericOrderedList($data);
}

public function filter(callable $callback): MapInterface
{
$instance = clone $this;
$instance->data = array_filter($this->data, $callback, ARRAY_FILTER_USE_BOTH);
$instance = clone $this;
/** @psalm-suppress ImpureFunctionCall */
$instance->data = array_filter($instance->data, $callback, ARRAY_FILTER_USE_BOTH);

return $instance;
}

/**
* @psalm-mutation-free
*/
public function keys(): OrderedListInterface
{
$keys = array_keys($this->data);
Expand All @@ -128,9 +145,6 @@ public function put($key, $value): MapInterface
return $instance;
}

/**
* @psalm-mutation-free
*/
public function get(string $key)
{
if (! $this->has($key)) {
Expand All @@ -157,7 +171,10 @@ public function intersect(MapInterface $other, ?callable $valueComparator = null
private function intersection(MapInterface $other, ?callable $valueComparator, ?callable $keyComparator): array
{
if ($valueComparator && $keyComparator) {
/** @psalm-var array<TKey,TValue> $intersection */
/**
* @psalm-var array<TKey,TValue> $intersection
* @psalm-suppress ImpureFunctionCall
*/
$intersection = array_uintersect_uassoc(
$this->data,
$other->toNativeArray(),
Expand All @@ -169,7 +186,10 @@ private function intersection(MapInterface $other, ?callable $valueComparator, ?
}

if ($keyComparator) {
/** @psalm-var array<TKey,TValue> $intersection */
/**
* @psalm-var array<TKey,TValue> $intersection
* @psalm-suppress ImpureFunctionCall
*/
$intersection = array_intersect_ukey($this->data, $other->toNativeArray(), $keyComparator);

return $intersection;
Expand All @@ -179,7 +199,10 @@ private function intersection(MapInterface $other, ?callable $valueComparator, ?
$valueComparator = $this->valueComparator();
}

/** @psalm-var array<TKey,TValue> $intersection */
/**
* @psalm-var array<TKey,TValue> $intersection
* @psalm-suppress ImpureFunctionCall
*/
$intersection = array_uintersect($this->data, $other->toNativeArray(), $valueComparator);

return $intersection;
Expand Down Expand Up @@ -214,14 +237,20 @@ public function intersectUserAssoc(

public function diff(MapInterface $other, ?callable $valueComparator = null): MapInterface
{
/** @psalm-var array<TKey,TValue> $diff1 */
/**
* @psalm-var array<TKey,TValue> $diff1
* @psalm-suppress ImpureFunctionCall
*/
$diff1 = array_udiff(
$this->toNativeArray(),
$other->toNativeArray(),
$valueComparator ?? $this->valueComparator()
);

/** @psalm-var array<TKey,TValue> $diff2 */
/**
* @psalm-var array<TKey,TValue> $diff2
* @psalm-suppress ImpureFunctionCall
*/
$diff2 = array_udiff(
$other->toNativeArray(),
$this->toNativeArray(),
Expand Down Expand Up @@ -263,12 +292,12 @@ public function removeElement($element): MapInterface

public function map(callable $callback): MapInterface
{
return new GenericMap(array_map($callback, $this->data));
$instance = clone $this;

/** @psalm-suppress ImpureFunctionCall */
return new GenericMap(array_map($callback, $instance->data));
}

/**
* @psalm-mutation-free
*/
public function has(string $key): bool
{
return array_key_exists($key, $this->data);
Expand Down
3 changes: 0 additions & 3 deletions src/MapInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ public function unset($key): MapInterface;

/**
* @psalm-return OrderedListInterface<TKey>
* @psalm-mutation-free
*/
public function keys(): OrderedListInterface;

Expand All @@ -96,7 +95,6 @@ public function put($key, $value): MapInterface;
* @psalm-param TKey $key
* @psalm-return TValue
* @throws OutOfBoundsException if key does not exist.
* @psalm-mutation-free
*/
public function get(string $key);

Expand Down Expand Up @@ -128,7 +126,6 @@ public function intersectUserAssoc(

/**
* @psalm-param TKey $key
* @psalm-mutation-free
*/
public function has(string $key): bool;

Expand Down
Loading

0 comments on commit 8d7e501

Please sign in to comment.