Skip to content

Instantly share code, notes, and snippets.

@ghostwriter
Last active September 28, 2024 18:32
Show Gist options
  • Save ghostwriter/0882ad50ac55776ba2e22eb4b563398c to your computer and use it in GitHub Desktop.
Save ghostwriter/0882ad50ac55776ba2e22eb4b563398c to your computer and use it in GitHub Desktop.
Polynomial Functors in PHP
<?php
interface Functor
{
/**
* Maps a function over the functor.
*
* @template A
* @template B
* @param callable(A): B $f
* @return Functor<B>
*/
public function map(callable $f): Functor;
}
object(PolynomialFunctors\Polynomial)#1 (2) {
  ["tag":"PolynomialFunctors\Polynomial":private]=>
  int(0)
  ["value":"PolynomialFunctors\Polynomial":private]=>
  NULL
}
object(PolynomialFunctors\Polynomial)#5 (2) {
  ["tag":"PolynomialFunctors\Polynomial":private]=>
  int(1)
  ["value":"PolynomialFunctors\Polynomial":private]=>
  int(10)
}
object(PolynomialFunctors\Polynomial)#6 (2) {
  ["tag":"PolynomialFunctors\Polynomial":private]=>
  int(2)
  ["value":"PolynomialFunctors\Polynomial":private]=>
  array(2) {
    [0]=>
    int(4)
    [1]=>
    int(8)
  }
}

https://3v4l.org/aEnTYH

<?php
$unit = Polynomial::unit(); // Represents `1`
$linear = Polynomial::linear(5); // Represents `x`
$quadratic = Polynomial::quadratic(3, 7); // Represents `x^2`
// Mapping over the functor
$mappedLinear = $linear->map(static fn(int $x): int => $x * 2); // Maps over the single value
$mappedQuadratic = $quadratic->map(static fn(int $x): int => $x + 1); // Maps over both values
// Output results
var_dump($unit); // Nothing to map
var_dump($mappedLinear); // Polynomial with value 10
var_dump($mappedQuadratic); // Polynomial with values 4 and 8
<?php #BlackLivesMatter - github.com/ghostwriter
namespace PolynomialFunctors;
use InvalidArgumentException;
/**
* Represents a polynomial functor of the form `1 + x + x^2`.
*
* @template T
* @implements Functor<T>
*/
final class Polynomial implements Functor
{
private int $tag;
private mixed $value;
private function __construct(int $tag, mixed $value = null)
{
$this->tag = $tag;
$this->value = $value;
}
/**
* Represents the `1` case.
*
* @return self
*/
public static function unit(): self
{
return new self(0);
}
/**
* Represents the `x` case with one value.
*
* @template T
* @param T $value
* @return self<T>
*/
public static function linear(mixed $value): self
{
return new self(1, $value);
}
/**
* Represents the `x^2` case with two values.
*
* @template T
* @param T $first
* @param T $second
* @return self<T>
*/
public static function quadratic(mixed $first, mixed $second): self
{
return new self(2, [$first, $second]);
}
/**
* Maps a function over the polynomial structure.
*
* @template A
* @template B
* @param callable(A): B $f
* @return self<B>
*/
public function map(callable $f): Polynomial
{
return match ($this->tag) {
0 => self::unit(),
1 => self::linear($f($this->value)),
2 => self::quadratic($f($this->value[0]), $f($this->value[1])),
default => throw new InvalidArgumentException('Unknown polynomial tag')
};
}
}
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
use PolynomialFunctors\Polynomial;
final class PolynomialTest extends TestCase
{
public function testUnit(): void
{
$unit = Polynomial::unit();
$this->assertInstanceOf(Polynomial::class, $unit);
$this->assertNull($unit->getValue());
}
public function testLinearMapping(): void
{
$linear = Polynomial::linear(5);
$mapped = $linear->map(fn(int $x): int => $x * 2);
$this->assertSame(10, $mapped->getValue());
}
public function testQuadraticMapping(): void
{
$quadratic = Polynomial::quadratic(3, 7);
$mapped = $quadratic->map(fn(int $x): int => $x + 1);
$this->assertSame([4, 8], $mapped->getValue());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment