Skip to content

Commit 2e8df7e

Browse files
committed
Merge branch 'develop'
* develop: specify next release fix deferred/lazy Sequence::last() defer the values for ->get(), ->first(), ->last(), ->indexOf() and ->find() on a lazy/deferred Sequence add Maybe::defer() and Either::defer() add shortcut method Str::maybe()
2 parents 2f1ebbd + 4a74b9c commit 2e8df7e

16 files changed

+507
-92
lines changed

CHANGELOG.md

+16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
# Changelog
22

3+
## 4.10.0 - 2023-02-05
4+
5+
### Added
6+
7+
- `Innmind\Immutable\Str::maybe()`
8+
- `Innmind\Immutable\Maybe::defer()`
9+
- `Innmind\Immutable\Either::defer()`
10+
11+
### Changed
12+
13+
- `->get()`, `->first()`, `->last()`, `->indexOf()` and `->find()` calls on a deferred or lazy `Innmind\Immutable\Sequence` will now return a deferred `Innmind\Immutable\Maybe`
14+
15+
### Fixed
16+
17+
- `Innmind\Immutable\Sequence::last()` returned `Innmind\Immutable\Maybe::nothing()` when the last value was `null`, now it returns `Innmind\Immutable\Maybe::just(null)`
18+
319
## 4.9.0 - 2022-12-17
420

521
### Changed

docs/EITHER.md

+20
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,26 @@ $either = Either::right($anyValue);
5454

5555
**Note**: usually this side is used for valid values.
5656

57+
## `::defer()`
58+
59+
This is used to return an `Either` early with known data type but with the value that will be extracted from the callable when calling `->match()`. The main use case is for IO operations.
60+
61+
```php
62+
$either = Either::defer(static function() {
63+
try {
64+
$value = /* wait for some IO operation like an http call */;
65+
66+
return Either::right($value);
67+
} catch (\Throwable $e) {
68+
return Either::left($e);
69+
}
70+
});
71+
```
72+
73+
Methods called (except `match`) on a deferred `Either` will not be called immediately but will be composed to be executed once you call `match`.
74+
75+
**Important**: this means that if you never call `match` on a deferred `Either` it will do nothing.
76+
5777
## `->map()`
5878

5979
This will apply the map transformation on the right value if there is one, otherwise it's only a type change.

docs/MAYBE.md

+16
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,22 @@ $kernel = Maybe::all(env('ENV'), env('DEBUG'))
4848
);
4949
```
5050

51+
## `::defer()`
52+
53+
This is used to return a `Maybe` early with known data type but with the value that will be extracted from the callable when calling `->match()`. The main use case is for IO operations.
54+
55+
```php
56+
$maybe = Maybe::defer(static function() {
57+
$value = /* wait for some IO operation like an http call */;
58+
59+
return Maybe::of($value);
60+
});
61+
```
62+
63+
Methods called (except `match`) on a deferred `Maybe` will not be called immediately but will be composed to be executed once you call `match`.
64+
65+
**Important**: this means that if you never call `match` on a deferred `Maybe` it will do nothing.
66+
5167
## `->map()`
5268

5369
This function allows you to transform the value into another value that will be wrapped in a `Maybe` object.

docs/STR.md

+9
Original file line numberDiff line numberDiff line change
@@ -406,3 +406,12 @@ $str = Str::of('foo|bar|baz')->flatMap(
406406
);
407407
$str->equals(Str::of('foo,bar,baz')); // true
408408
```
409+
410+
## `->maybe()`
411+
412+
The is a shortcut method, the 2 examples below do the same thing.
413+
414+
```php
415+
Str::of('foobar')->maybe(static fn($str) => $str->startsWith('foo')); // Maybe<Str>
416+
Maybe::of(Str::of('foobar'))->filter(static fn($str) => $str->startsWith('foo')); // Maybe<Str>
417+
```

src/Either.php

+17
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Implementation,
88
Left,
99
Right,
10+
Defer,
1011
};
1112

1213
/**
@@ -55,6 +56,22 @@ public static function right($value): self
5556
return new self(new Right($value));
5657
}
5758

59+
/**
60+
* This method is to be used for IO operations
61+
*
62+
* @template A
63+
* @template B
64+
* @psalm-pure
65+
*
66+
* @param callable(): self<A, B> $deferred
67+
*
68+
* @return self<A, B>
69+
*/
70+
public static function defer(callable $deferred): self
71+
{
72+
return new self(new Defer($deferred));
73+
}
74+
5875
/**
5976
* @template T
6077
*

src/Either/Defer.php

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
namespace Innmind\Immutable\Either;
5+
6+
use Innmind\Immutable\{
7+
Either,
8+
Maybe,
9+
};
10+
11+
/**
12+
* @template L1
13+
* @template R1
14+
* @implements Implementation<L1, R1>
15+
* @psalm-immutable
16+
* @internal
17+
*/
18+
final class Defer implements Implementation
19+
{
20+
/** @var callable(): Either<L1, R1> */
21+
private $deferred;
22+
/** @var ?Either<L1, R1> */
23+
private ?Either $value = null;
24+
25+
/**
26+
* @param callable(): Either<L1, R1> $deferred
27+
*/
28+
public function __construct($deferred)
29+
{
30+
$this->deferred = $deferred;
31+
}
32+
33+
public function map(callable $map): self
34+
{
35+
return new self(fn() => $this->unwrap()->map($map));
36+
}
37+
38+
public function flatMap(callable $map): Either
39+
{
40+
return Either::defer(fn() => $this->unwrap()->flatMap($map));
41+
}
42+
43+
public function leftMap(callable $map): self
44+
{
45+
return new self(fn() => $this->unwrap()->leftMap($map));
46+
}
47+
48+
public function match(callable $right, callable $left)
49+
{
50+
return $this->unwrap()->match($right, $left);
51+
}
52+
53+
public function otherwise(callable $otherwise): Either
54+
{
55+
return Either::defer(fn() => $this->unwrap()->otherwise($otherwise));
56+
}
57+
58+
public function filter(callable $predicate, callable $otherwise): Implementation
59+
{
60+
return new self(fn() => $this->unwrap()->filter($predicate, $otherwise));
61+
}
62+
63+
public function maybe(): Maybe
64+
{
65+
return Maybe::defer(fn() => $this->unwrap()->maybe());
66+
}
67+
68+
/**
69+
* @return Either<L1, R1>
70+
*/
71+
private function unwrap(): Either
72+
{
73+
/**
74+
* @psalm-suppress InaccessibleProperty
75+
* @psalm-suppress ImpureFunctionCall
76+
*/
77+
return $this->value ??= ($this->deferred)();
78+
}
79+
}

src/Maybe.php

+16
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Implementation,
88
Just,
99
Nothing,
10+
Defer,
1011
};
1112

1213
/**
@@ -64,6 +65,21 @@ public static function of($value): self
6465
return self::just($value);
6566
}
6667

68+
/**
69+
* This method is to be used for IO operations
70+
*
71+
* @template V
72+
* @psalm-pure
73+
*
74+
* @param callable(): self<V> $deferred
75+
*
76+
* @return self<V>
77+
*/
78+
public static function defer(callable $deferred): self
79+
{
80+
return new self(new Defer($deferred));
81+
}
82+
6783
/**
6884
* The comprehension is called only when all values exist
6985
*

src/Maybe/Defer.php

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

0 commit comments

Comments
 (0)