Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
* 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()
  • Loading branch information
Baptouuuu committed Feb 5, 2023
2 parents 2f1ebbd + 4a74b9c commit 2e8df7e
Show file tree
Hide file tree
Showing 16 changed files with 507 additions and 92 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## 4.10.0 - 2023-02-05

### Added

- `Innmind\Immutable\Str::maybe()`
- `Innmind\Immutable\Maybe::defer()`
- `Innmind\Immutable\Either::defer()`

### Changed

- `->get()`, `->first()`, `->last()`, `->indexOf()` and `->find()` calls on a deferred or lazy `Innmind\Immutable\Sequence` will now return a deferred `Innmind\Immutable\Maybe`

### Fixed

- `Innmind\Immutable\Sequence::last()` returned `Innmind\Immutable\Maybe::nothing()` when the last value was `null`, now it returns `Innmind\Immutable\Maybe::just(null)`

## 4.9.0 - 2022-12-17

### Changed
Expand Down
20 changes: 20 additions & 0 deletions docs/EITHER.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,26 @@ $either = Either::right($anyValue);

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

## `::defer()`

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.

```php
$either = Either::defer(static function() {
try {
$value = /* wait for some IO operation like an http call */;

return Either::right($value);
} catch (\Throwable $e) {
return Either::left($e);
}
});
```

Methods called (except `match`) on a deferred `Either` will not be called immediately but will be composed to be executed once you call `match`.

**Important**: this means that if you never call `match` on a deferred `Either` it will do nothing.

## `->map()`

This will apply the map transformation on the right value if there is one, otherwise it's only a type change.
Expand Down
16 changes: 16 additions & 0 deletions docs/MAYBE.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ $kernel = Maybe::all(env('ENV'), env('DEBUG'))
);
```

## `::defer()`

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.

```php
$maybe = Maybe::defer(static function() {
$value = /* wait for some IO operation like an http call */;

return Maybe::of($value);
});
```

Methods called (except `match`) on a deferred `Maybe` will not be called immediately but will be composed to be executed once you call `match`.

**Important**: this means that if you never call `match` on a deferred `Maybe` it will do nothing.

## `->map()`

This function allows you to transform the value into another value that will be wrapped in a `Maybe` object.
Expand Down
9 changes: 9 additions & 0 deletions docs/STR.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,3 +406,12 @@ $str = Str::of('foo|bar|baz')->flatMap(
);
$str->equals(Str::of('foo,bar,baz')); // true
```

## `->maybe()`

The is a shortcut method, the 2 examples below do the same thing.

```php
Str::of('foobar')->maybe(static fn($str) => $str->startsWith('foo')); // Maybe<Str>
Maybe::of(Str::of('foobar'))->filter(static fn($str) => $str->startsWith('foo')); // Maybe<Str>
```
17 changes: 17 additions & 0 deletions src/Either.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
Implementation,
Left,
Right,
Defer,
};

/**
Expand Down Expand Up @@ -55,6 +56,22 @@ public static function right($value): self
return new self(new Right($value));
}

/**
* This method is to be used for IO operations
*
* @template A
* @template B
* @psalm-pure
*
* @param callable(): self<A, B> $deferred
*
* @return self<A, B>
*/
public static function defer(callable $deferred): self
{
return new self(new Defer($deferred));
}

/**
* @template T
*
Expand Down
79 changes: 79 additions & 0 deletions src/Either/Defer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
declare(strict_types = 1);

namespace Innmind\Immutable\Either;

use Innmind\Immutable\{
Either,
Maybe,
};

/**
* @template L1
* @template R1
* @implements Implementation<L1, R1>
* @psalm-immutable
* @internal
*/
final class Defer implements Implementation
{
/** @var callable(): Either<L1, R1> */
private $deferred;
/** @var ?Either<L1, R1> */
private ?Either $value = null;

/**
* @param callable(): Either<L1, R1> $deferred
*/
public function __construct($deferred)
{
$this->deferred = $deferred;
}

public function map(callable $map): self
{
return new self(fn() => $this->unwrap()->map($map));
}

public function flatMap(callable $map): Either
{
return Either::defer(fn() => $this->unwrap()->flatMap($map));
}

public function leftMap(callable $map): self
{
return new self(fn() => $this->unwrap()->leftMap($map));
}

public function match(callable $right, callable $left)
{
return $this->unwrap()->match($right, $left);
}

public function otherwise(callable $otherwise): Either
{
return Either::defer(fn() => $this->unwrap()->otherwise($otherwise));
}

public function filter(callable $predicate, callable $otherwise): Implementation
{
return new self(fn() => $this->unwrap()->filter($predicate, $otherwise));
}

public function maybe(): Maybe
{
return Maybe::defer(fn() => $this->unwrap()->maybe());
}

/**
* @return Either<L1, R1>
*/
private function unwrap(): Either
{
/**
* @psalm-suppress InaccessibleProperty
* @psalm-suppress ImpureFunctionCall
*/
return $this->value ??= ($this->deferred)();
}
}
16 changes: 16 additions & 0 deletions src/Maybe.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
Implementation,
Just,
Nothing,
Defer,
};

/**
Expand Down Expand Up @@ -64,6 +65,21 @@ public static function of($value): self
return self::just($value);
}

/**
* This method is to be used for IO operations
*
* @template V
* @psalm-pure
*
* @param callable(): self<V> $deferred
*
* @return self<V>
*/
public static function defer(callable $deferred): self
{
return new self(new Defer($deferred));
}

/**
* The comprehension is called only when all values exist
*
Expand Down
73 changes: 73 additions & 0 deletions src/Maybe/Defer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
declare(strict_types = 1);

namespace Innmind\Immutable\Maybe;

use Innmind\Immutable\{
Maybe,
Either,
};

/**
* @template V
* @implements Implementation<V>
* @psalm-immutable
* @internal
*/
final class Defer implements Implementation
{
/** @var callable(): Maybe<V> */
private $deferred;
/** @var ?Maybe<V> */
private ?Maybe $value = null;

/**
* @param callable(): Maybe<V> $deferred
*/
public function __construct(callable $deferred)
{
$this->deferred = $deferred;
}

public function map(callable $map): self
{
return new self(fn() => $this->unwrap()->map($map));
}

public function flatMap(callable $map): Maybe
{
return Maybe::defer(fn() => $this->unwrap()->flatMap($map));
}

public function match(callable $just, callable $nothing)
{
return $this->unwrap()->match($just, $nothing);
}

public function otherwise(callable $otherwise): Maybe
{
return Maybe::defer(fn() => $this->unwrap()->otherwise($otherwise));
}

public function filter(callable $predicate): Implementation
{
return new self(fn() => $this->unwrap()->filter($predicate));
}

public function either(): Either
{
return Either::defer(fn() => $this->unwrap()->either());
}

/**
* @return Maybe<V>
*/
private function unwrap(): Maybe
{
/**
* @psalm-suppress InaccessibleProperty
* @psalm-suppress ImpureFunctionCall
*/
return $this->value ??= ($this->deferred)();
}
}
Loading

0 comments on commit 2e8df7e

Please sign in to comment.