Skip to content

Commit 4fc9114

Browse files
committed
Merge branch 'develop'
* develop: specify next release remove useless method add proof on lazy/deferred Identity::flatMap() add Identity::toSequence() adapt the laziness of the identity based on the sequence laziness add Identity::lazy() and ::defer()
2 parents d861ab3 + cbbc8f0 commit 4fc9114

13 files changed

+444
-27
lines changed

CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## 5.8.0 - 2024-06-27
4+
5+
### Added
6+
7+
- `Innmind\Immutable\Identity::lazy()`
8+
- `Innmind\Immutable\Identity::defer()`
9+
- `Innmind\Immutable\Identity::toSequence()`
10+
11+
### Changed
12+
13+
- `Innmind\Immutable\Sequence::toIdentity()` returns a lazy, deferred or in memory `Identity` based on the kind of `Sequence`
14+
315
## 5.7.0 - 2024-06-25
416

517
### Added

proofs/identity.php

+120-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
<?php
22
declare(strict_types = 1);
33

4-
use Innmind\Immutable\Identity;
4+
use Innmind\Immutable\{
5+
Identity,
6+
Sequence,
7+
};
58
use Innmind\BlackBox\Set;
69

710
return static function() {
@@ -38,16 +41,62 @@
3841
Set\Type::any(),
3942
Set\Type::any(),
4043
),
41-
static fn($assert, $initial, $expected) => $assert->same(
42-
$expected,
43-
Identity::of($initial)
44-
->flatMap(static function($value) use ($assert, $initial, $expected) {
45-
$assert->same($initial, $value);
44+
static function($assert, $initial, $expected) {
45+
$assert->same(
46+
$expected,
47+
Identity::of($initial)
48+
->flatMap(static function($value) use ($assert, $initial, $expected) {
49+
$assert->same($initial, $value);
4650

47-
return Identity::of($expected);
48-
})
49-
->unwrap(),
50-
),
51+
return Identity::of($expected);
52+
})
53+
->unwrap(),
54+
);
55+
56+
$loaded = 0;
57+
$identity = Identity::defer(static function() use (&$loaded, $initial) {
58+
$loaded++;
59+
60+
return $initial;
61+
})->flatMap(static function($value) use ($assert, $initial, $expected) {
62+
$assert->same($initial, $value);
63+
64+
return Identity::of($expected);
65+
});
66+
$assert->same(0, $loaded);
67+
$assert->same(
68+
$expected,
69+
$identity->unwrap(),
70+
);
71+
$assert->same(1, $loaded);
72+
$assert->same(
73+
$expected,
74+
$identity->unwrap(),
75+
);
76+
$assert->same(1, $loaded);
77+
78+
$loaded = 0;
79+
$identity = Identity::lazy(static function() use (&$loaded, $initial) {
80+
$loaded++;
81+
82+
return $initial;
83+
})->flatMap(static function($value) use ($assert, $initial, $expected) {
84+
$assert->same($initial, $value);
85+
86+
return Identity::of($expected);
87+
});
88+
$assert->same(0, $loaded);
89+
$assert->same(
90+
$expected,
91+
$identity->unwrap(),
92+
);
93+
$assert->same(1, $loaded);
94+
$assert->same(
95+
$expected,
96+
$identity->unwrap(),
97+
);
98+
$assert->same(2, $loaded);
99+
},
51100
);
52101

53102
yield proof(
@@ -71,4 +120,65 @@
71120
->unwrap(),
72121
),
73122
);
123+
124+
yield proof(
125+
'Identity::toSequence()',
126+
given(Set\Sequence::of(Set\Type::any())),
127+
static function($assert, $values) {
128+
$inMemory = Sequence::of(...$values);
129+
130+
$assert->same(
131+
$values,
132+
$inMemory
133+
->toIdentity()
134+
->toSequence()
135+
->flatMap(static fn($sequence) => $sequence)
136+
->toList(),
137+
);
138+
139+
$loaded = 0;
140+
$deferred = Sequence::defer((static function() use (&$loaded, $values) {
141+
yield from $values;
142+
$loaded++;
143+
})());
144+
$sequence = $deferred
145+
->toIdentity()
146+
->toSequence()
147+
->flatMap(static fn($sequence) => $sequence);
148+
149+
$assert->same(0, $loaded);
150+
$assert->same(
151+
$values,
152+
$sequence->toList(),
153+
);
154+
$assert->same(1, $loaded);
155+
$assert->same(
156+
$values,
157+
$sequence->toList(),
158+
);
159+
$assert->same(1, $loaded);
160+
161+
$loaded = 0;
162+
$lazy = Sequence::lazy(static function() use (&$loaded, $values) {
163+
yield from $values;
164+
$loaded++;
165+
});
166+
$sequence = $lazy
167+
->toIdentity()
168+
->toSequence()
169+
->flatMap(static fn($sequence) => $sequence);
170+
171+
$assert->same(0, $loaded);
172+
$assert->same(
173+
$values,
174+
$sequence->toList(),
175+
);
176+
$assert->same(1, $loaded);
177+
$assert->same(
178+
$values,
179+
$sequence->toList(),
180+
);
181+
$assert->same(2, $loaded);
182+
},
183+
);
74184
};

proofs/sequence.php

+9-5
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55
use Innmind\BlackBox\Set;
66

77
return static function() {
8-
yield test(
8+
yield proof(
99
'Sequence::toIdentity()',
10-
static function($assert) {
11-
$sequence = Sequence::of();
10+
given(Set\Sequence::of(Set\Type::any())),
11+
static function($assert, $values) {
12+
$sequence = Sequence::of(...$values);
1213

1314
$assert->same(
14-
$sequence,
15-
$sequence->toIdentity()->unwrap(),
15+
$sequence->toList(),
16+
$sequence
17+
->toIdentity()
18+
->unwrap()
19+
->toList(),
1620
);
1721
},
1822
);

src/Identity.php

+58-11
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,28 @@
33

44
namespace Innmind\Immutable;
55

6+
use Innmind\Immutable\Identity\{
7+
Implementation,
8+
InMemory,
9+
Lazy,
10+
Defer,
11+
};
12+
613
/**
714
* @psalm-immutable
815
* @template T
916
*/
1017
final class Identity
1118
{
12-
/** @var T */
13-
private mixed $value;
19+
/** @var Implementation<T> */
20+
private Implementation $implementation;
1421

1522
/**
16-
* @param T $value
23+
* @param Implementation<T> $implementation
1724
*/
18-
private function __construct(mixed $value)
25+
private function __construct(Implementation $implementation)
1926
{
20-
$this->value = $value;
27+
$this->implementation = $implementation;
2128
}
2229

2330
/**
@@ -30,7 +37,41 @@ private function __construct(mixed $value)
3037
*/
3138
public static function of(mixed $value): self
3239
{
33-
return new self($value);
40+
return new self(new InMemory($value));
41+
}
42+
43+
/**
44+
* When using a lazy computation all transformations via map and flatMap
45+
* will be applied when calling unwrap. Each call to unwrap will call again
46+
* all transformations.
47+
*
48+
* @psalm-pure
49+
* @template A
50+
*
51+
* @param callable(): A $value
52+
*
53+
* @return self<A>
54+
*/
55+
public static function lazy(callable $value): self
56+
{
57+
return new self(new Lazy($value));
58+
}
59+
60+
/**
61+
* When using a deferred computation all transformations via map and flatMap
62+
* will be applied when calling unwrap. The value is computed once and all
63+
* calls to unwrap will return the same value.
64+
*
65+
* @psalm-pure
66+
* @template A
67+
*
68+
* @param callable(): A $value
69+
*
70+
* @return self<A>
71+
*/
72+
public static function defer(callable $value): self
73+
{
74+
return new self(new Defer($value));
3475
}
3576

3677
/**
@@ -42,8 +83,7 @@ public static function of(mixed $value): self
4283
*/
4384
public function map(callable $map): self
4485
{
45-
/** @psalm-suppress ImpureFunctionCall */
46-
return new self($map($this->value));
86+
return new self($this->implementation->map($map));
4787
}
4888

4989
/**
@@ -55,15 +95,22 @@ public function map(callable $map): self
5595
*/
5696
public function flatMap(callable $map): self
5797
{
58-
/** @psalm-suppress ImpureFunctionCall */
59-
return $map($this->value);
98+
return $this->implementation->flatMap($map);
99+
}
100+
101+
/**
102+
* @return Sequence<T>
103+
*/
104+
public function toSequence(): Sequence
105+
{
106+
return $this->implementation->toSequence();
60107
}
61108

62109
/**
63110
* @return T
64111
*/
65112
public function unwrap(): mixed
66113
{
67-
return $this->value;
114+
return $this->implementation->unwrap();
68115
}
69116
}

src/Identity/Defer.php

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
namespace Innmind\Immutable\Identity;
5+
6+
use Innmind\Immutable\{
7+
Identity,
8+
Sequence,
9+
};
10+
11+
/**
12+
* @psalm-immutable
13+
* @template T
14+
* @implements Implementation<T>
15+
*/
16+
final class Defer implements Implementation
17+
{
18+
/** @var callable(): T */
19+
private $value;
20+
private bool $loaded = false;
21+
/** @var ?T */
22+
private mixed $computed = null;
23+
24+
/**
25+
* @param callable(): T $value
26+
*/
27+
public function __construct(callable $value)
28+
{
29+
$this->value = $value;
30+
}
31+
32+
public function map(callable $map): self
33+
{
34+
/** @psalm-suppress ImpureFunctionCall */
35+
return new self(fn() => $map($this->unwrap()));
36+
}
37+
38+
public function flatMap(callable $map): Identity
39+
{
40+
/** @psalm-suppress ImpureFunctionCall */
41+
return Identity::lazy(fn() => $map($this->unwrap())->unwrap());
42+
}
43+
44+
public function toSequence(): Sequence
45+
{
46+
/** @psalm-suppress ImpureFunctionCall */
47+
return Sequence::defer((fn() => yield $this->unwrap())());
48+
}
49+
50+
public function unwrap(): mixed
51+
{
52+
if ($this->loaded) {
53+
/** @var T */
54+
return $this->computed;
55+
}
56+
57+
/**
58+
* @psalm-suppress InaccessibleProperty
59+
* @psalm-suppress ImpureFunctionCall
60+
*/
61+
$this->computed = ($this->value)();
62+
/** @psalm-suppress InaccessibleProperty */
63+
$this->loaded = true;
64+
65+
return $this->computed;
66+
}
67+
}

0 commit comments

Comments
 (0)