Skip to content

Commit

Permalink
TrinaryLogic - lazy evaluation methods
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 30, 2022
1 parent 493284c commit f8f6e2a
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 89 deletions.
8 changes: 4 additions & 4 deletions src/Reflection/Type/IntersectionTypeMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public function getVariants(): array

public function isDeprecated(): TrinaryLogic
{
return TrinaryLogic::maxMin(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->isDeprecated(), $this->methods));
return TrinaryLogic::lazyMaxMin($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->isDeprecated());
}

public function getDeprecatedDescription(): ?string
Expand All @@ -114,12 +114,12 @@ public function getDeprecatedDescription(): ?string

public function isFinal(): TrinaryLogic
{
return TrinaryLogic::maxMin(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->isFinal(), $this->methods));
return TrinaryLogic::lazyMaxMin($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->isFinal());
}

public function isInternal(): TrinaryLogic
{
return TrinaryLogic::maxMin(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->isInternal(), $this->methods));
return TrinaryLogic::lazyMaxMin($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->isInternal());
}

public function getThrowType(): ?Type
Expand All @@ -144,7 +144,7 @@ public function getThrowType(): ?Type

public function hasSideEffects(): TrinaryLogic
{
return TrinaryLogic::maxMin(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->hasSideEffects(), $this->methods));
return TrinaryLogic::lazyMaxMin($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->hasSideEffects());
}

public function getDocComment(): ?string
Expand Down
4 changes: 2 additions & 2 deletions src/Reflection/Type/IntersectionTypePropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public function isPublic(): bool

public function isDeprecated(): TrinaryLogic
{
return TrinaryLogic::maxMin(...array_map(static fn (PropertyReflection $property): TrinaryLogic => $property->isDeprecated(), $this->properties));
return TrinaryLogic::lazyMaxMin($this->properties, static fn (PropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isDeprecated());
}

public function getDeprecatedDescription(): ?string
Expand All @@ -88,7 +88,7 @@ public function getDeprecatedDescription(): ?string

public function isInternal(): TrinaryLogic
{
return TrinaryLogic::maxMin(...array_map(static fn (PropertyReflection $property): TrinaryLogic => $property->isInternal(), $this->properties));
return TrinaryLogic::lazyMaxMin($this->properties, static fn (PropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isInternal());
}

public function getDocComment(): ?string
Expand Down
8 changes: 4 additions & 4 deletions src/Reflection/Type/UnionTypeMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public function getVariants(): array

public function isDeprecated(): TrinaryLogic
{
return TrinaryLogic::extremeIdentity(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->isDeprecated(), $this->methods));
return TrinaryLogic::lazyExtremeIdentity($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->isDeprecated());
}

public function getDeprecatedDescription(): ?string
Expand All @@ -108,12 +108,12 @@ public function getDeprecatedDescription(): ?string

public function isFinal(): TrinaryLogic
{
return TrinaryLogic::extremeIdentity(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->isFinal(), $this->methods));
return TrinaryLogic::lazyExtremeIdentity($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->isFinal());
}

public function isInternal(): TrinaryLogic
{
return TrinaryLogic::extremeIdentity(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->isInternal(), $this->methods));
return TrinaryLogic::lazyExtremeIdentity($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->isInternal());
}

public function getThrowType(): ?Type
Expand All @@ -138,7 +138,7 @@ public function getThrowType(): ?Type

public function hasSideEffects(): TrinaryLogic
{
return TrinaryLogic::extremeIdentity(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->hasSideEffects(), $this->methods));
return TrinaryLogic::lazyExtremeIdentity($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->hasSideEffects());
}

public function getDocComment(): ?string
Expand Down
4 changes: 2 additions & 2 deletions src/Reflection/Type/UnionTypePropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public function isPublic(): bool

public function isDeprecated(): TrinaryLogic
{
return TrinaryLogic::extremeIdentity(...array_map(static fn (PropertyReflection $property): TrinaryLogic => $property->isDeprecated(), $this->properties));
return TrinaryLogic::lazyExtremeIdentity($this->properties, static fn (PropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isDeprecated());
}

public function getDeprecatedDescription(): ?string
Expand All @@ -88,7 +88,7 @@ public function getDeprecatedDescription(): ?string

public function isInternal(): TrinaryLogic
{
return TrinaryLogic::extremeIdentity(...array_map(static fn (PropertyReflection $property): TrinaryLogic => $property->isInternal(), $this->properties));
return TrinaryLogic::lazyExtremeIdentity($this->properties, static fn (PropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isInternal());
}

public function getDocComment(): ?string
Expand Down
99 changes: 99 additions & 0 deletions src/TrinaryLogic.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,59 @@ public function and(self ...$operands): self
return self::create(min($operandValues));
}

/**
* @template T
* @param T[] $objects
* @param callable(T): self $callback
*/
public function lazyAnd(
array $objects,
callable $callback,
): self
{
$results = [];
foreach ($objects as $object) {
$result = $callback($object);
if ($result->no()) {
return $result;
}

$results[] = $result;
}

return $this->and(...$results);
}

public function or(self ...$operands): self
{
$operandValues = array_column($operands, 'value');
$operandValues[] = $this->value;
return self::create(max($operandValues));
}

/**
* @template T
* @param T[] $objects
* @param callable(T): self $callback
*/
public function lazyOr(
array $objects,
callable $callback,
): self
{
$results = [];
foreach ($objects as $object) {
$result = $callback($object);
if ($result->yes()) {
return $result;
}

$results[] = $result;
}

return $this->or(...$results);
}

public static function extremeIdentity(self ...$operands): self
{
if ($operands === []) {
Expand All @@ -102,6 +148,36 @@ public static function extremeIdentity(self ...$operands): self
return self::create($min === $max ? $min : self::MAYBE);
}

/**
* @template T
* @param T[] $objects
* @param callable(T): self $callback
*/
public static function lazyExtremeIdentity(
array $objects,
callable $callback,
): self
{
$lastResult = null;
$results = [];
foreach ($objects as $object) {
$result = $callback($object);
if ($lastResult === null) {
$lastResult = $result;
$results[] = $result;
continue;
}
if ($lastResult->equals($result)) {
$results[] = $result;
continue;
}

return self::createMaybe();
}

return self::extremeIdentity(...$results);
}

public static function maxMin(self ...$operands): self
{
if ($operands === []) {
Expand All @@ -111,6 +187,29 @@ public static function maxMin(self ...$operands): self
return self::create(max($operandValues) > 0 ? 1 : min($operandValues));
}

/**
* @template T
* @param T[] $objects
* @param callable(T): self $callback
*/
public static function lazyMaxMin(
array $objects,
callable $callback,
): self
{
$results = [];
foreach ($objects as $object) {
$result = $callback($object);
if ($result->yes()) {
return $result;
}

$results[] = $result;
}

return self::maxMin(...$results);
}

public function negate(): self
{
return self::create(-$this->value);
Expand Down
10 changes: 2 additions & 8 deletions src/Type/BenevolentUnionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use PHPStan\TrinaryLogic;
use PHPStan\Type\Generic\TemplateTypeMap;
use function array_map;
use function count;

/** @api */
Expand Down Expand Up @@ -46,17 +45,12 @@ protected function unionTypes(callable $getType): Type

protected function unionResults(callable $getResult): TrinaryLogic
{
return TrinaryLogic::createNo()->or(...array_map($getResult, $this->getTypes()));
return TrinaryLogic::createNo()->lazyOr($this->getTypes(), $getResult);
}

public function isAcceptedBy(Type $acceptingType, bool $strictTypes): TrinaryLogic
{
$results = [];
foreach ($this->getTypes() as $innerType) {
$results[] = $acceptingType->accepts($innerType, $strictTypes);
}

return TrinaryLogic::createNo()->or(...$results);
return TrinaryLogic::createNo()->lazyOr($this->getTypes(), static fn (Type $innerType) => $acceptingType->accepts($innerType, $strictTypes));
}

public function inferTemplateTypes(Type $receivedType): TemplateTypeMap
Expand Down
7 changes: 1 addition & 6 deletions src/Type/Constant/ConstantArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -495,12 +495,7 @@ public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
$offsetType = ArrayType::castToArrayKeyType($offsetType);
if ($offsetType instanceof UnionType) {
$results = [];
foreach ($offsetType->getTypes() as $innerType) {
$results[] = $this->hasOffsetValueType($innerType);
}

return TrinaryLogic::extremeIdentity(...$results);
return TrinaryLogic::lazyExtremeIdentity($offsetType->getTypes(), fn (Type $innerType) => $this->hasOffsetValueType($innerType));
}

$result = TrinaryLogic::createNo();
Expand Down
38 changes: 4 additions & 34 deletions src/Type/IntersectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,17 +144,7 @@ public function isSuperTypeOf(Type $otherType): TrinaryLogic
return TrinaryLogic::createYes();
}

$results = [];
foreach ($this->getTypes() as $innerType) {
$result = $innerType->isSuperTypeOf($otherType);
if ($result->no()) {
return $result;
}

$results[] = $result;
}

return TrinaryLogic::createYes()->and(...$results);
return TrinaryLogic::createYes()->lazyAnd($this->getTypes(), static fn (Type $innerType) => $innerType->isSuperTypeOf($otherType));
}

public function isSubTypeOf(Type $otherType): TrinaryLogic
Expand All @@ -163,31 +153,12 @@ public function isSubTypeOf(Type $otherType): TrinaryLogic
return $otherType->isSuperTypeOf($this);
}

$results = [];
foreach ($this->getTypes() as $innerType) {
$result = $otherType->isSuperTypeOf($innerType);
if ($result->yes()) {
return $result;
}
$results[] = $result;
}

return TrinaryLogic::maxMin(...$results);
return TrinaryLogic::lazyMaxMin($this->getTypes(), static fn (Type $innerType) => $otherType->isSuperTypeOf($innerType));
}

public function isAcceptedBy(Type $acceptingType, bool $strictTypes): TrinaryLogic
{
$results = [];
foreach ($this->getTypes() as $innerType) {
$result = $acceptingType->accepts($innerType, $strictTypes);
if ($result->yes()) {
return $result;
}

$results[] = $result;
}

return TrinaryLogic::maxMin(...$results);
return TrinaryLogic::lazyMaxMin($this->getTypes(), static fn (Type $innerType) => $acceptingType->accepts($innerType, $strictTypes));
}

public function equals(Type $type): bool
Expand Down Expand Up @@ -669,8 +640,7 @@ public static function __set_state(array $properties): Type
*/
private function intersectResults(callable $getResult): TrinaryLogic
{
$operands = array_map($getResult, $this->types);
return TrinaryLogic::maxMin(...$operands);
return TrinaryLogic::lazyMaxMin($this->types, $getResult);
}

/**
Expand Down
Loading

1 comment on commit f8f6e2a

@herndlm
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this is what I roughly was thinking of in the other PRs discussion :)

Please sign in to comment.