Skip to content

Instantly share code, notes, and snippets.

@pwm
Created August 2, 2018 18:42
Show Gist options
  • Save pwm/6d4e07b9befbcbd084762448ef5eaada to your computer and use it in GitHub Desktop.
Save pwm/6d4e07b9befbcbd084762448ef5eaada to your computer and use it in GitHub Desktop.
lenses
<?php
declare(strict_types=1);
namespace Lens;
use Closure;
class Lens
{
/** @var Closure */
private $getter;
/** @var Closure */
private $setter;
public function __construct(Closure $getter, Closure $setter)
{
$this->getter = $getter;
$this->setter = $setter;
}
public static function c(self ...$ls): self
{
return count($ls) > 0
? self::o($ls[0], self::c(...\array_slice($ls, 1)))
: new self(
function ($prop) { return $prop; },
function ($val, $_) { return $val; }
);
}
public function get($prop)
{
return ($this->getter)($prop);
}
public function set($val, $prop)
{
return ($this->setter)($val, $prop);
}
private static function o(self $l1, self $l2): self
{
return new self(
function ($prop) use ($l1, $l2) {
return $l2->get($l1->get($prop));
},
function ($val, $prop) use ($l1, $l2) {
return $l1->set($l2->set($val, $l1->get($prop)), $prop);
}
);
}
}
// ====
class A
{
/** @var B */
private $b;
public function __construct(B $b)
{
$this->b = $b;
}
public function getB(): B
{
return $this->b;
}
public static function B_(): Lens
{
return new Lens(
function (A $a): B { return $a->getB(); },
function (B $b, A $_): A { return new A($b); }
);
}
}
class B
{
/** @var C */
private $c;
public function __construct(C $c)
{
$this->c = $c;
}
public function getC(): C
{
return $this->c;
}
public static function C_(): Lens
{
return new Lens(
function (B $b): C { return $b->getC(); },
function (C $c, B $_): B { return new B($c); }
);
}
}
class C
{
/** @var string */
private $s;
public function __construct(string $s)
{
$this->s = $s;
}
public function getS(): string
{
return $this->s;
}
public static function S_(): Lens
{
return new Lens(
function (C $c): string { return $c->getS(); },
function (string $s, C $_): C { return new C($s); }
);
}
}
// ====
$a = new A(new B(new C('Hello')));
$l_S = Lens::c($a::B_(), $a->getB()::C_(), $a->getB()->getC()::S_());
\assert($l_S->get($a) === 'Hello');
\assert($l_S->get($l_S->set('World', $a)) === 'World');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment