Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
* develop:
  specify next release
  add missing documentation
  update changelog
  add identity documentation
  add Sequence::toIdentity()
  add Identity monad
  • Loading branch information
Baptouuuu committed Jun 15, 2024
2 parents 8dafdfb + c680a81 commit 2ec917c
Show file tree
Hide file tree
Showing 9 changed files with 266 additions and 1 deletion.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 5.6.0 - 2024-06-15

### Added

- `Innmind\Immutable\Identity`
- `Innmind\Immutable\Sequence::toIdentity()`

## 5.5.0 - 2024-06-02

### Changed
Expand Down
55 changes: 55 additions & 0 deletions docs/structures/identity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# `Identity`

This is the simplest monad there is. It's a simple wrapper around a value to allow chaining calls on this value.

Let's say you have a string you want to camelize, here's how you'd do it:

=== "Identity"
```php
$value = Identity::of('some value to camelize')
->map(fn($string) => \explode(' ', $string))
->map(fn($parts) => \array_map(
\ucfirst(...),
$parts,
))
->map(fn($parts) => \implode('', $parts))
->map(\lcfirst(...))
->unwrap();

echo $value; // outputs "someValueToCamelize"
```

=== "Imperative"
```php
$string = 'some value to camelize';
$parts = \explode(' ', $string);
$parts = \array_map(
\ucfirst(...),
$parts,
);
$string = \implode('', $parts);
$value = \lcfirst($string);

echo $value; // outputs "someValueToCamelize"
```

=== "Pyramid of doom"
```php
$value = \lcfirst(
\implode(
'',
\array_map(
\ucfirst(...),
\explode(
' ',
'some value to camelize',
),
),
),
);

echo $value; // outputs "someValueToCamelize"
```

!!! abstract ""
In the end this monad does not provide any behaviour, it's a different way to write and read your code.
3 changes: 2 additions & 1 deletion docs/structures/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Structures

This library provides the 10 following structures:
This library provides the following structures:

- [`Sequence`](sequence.md)
- [`Set`](set.md)
Expand All @@ -10,6 +10,7 @@ This library provides the 10 following structures:
- [`Maybe`](maybe.md)
- [`Either`](either.md)
- [`Validation`](validation.md)
- [`Identity`](identity.md)
- [`State`](state.md)
- [`Fold`](fold.md)

Expand Down
32 changes: 32 additions & 0 deletions docs/structures/sequence.md
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,38 @@ $sequence = Sequence::ints(1, 2, 3, 4);
$sequence->reverse()->equals(Sequence::ints(4, 3, 2, 1));
```

### `->toSet()`

It's like [`->distinct()`](#-distinct) except it returns a [`Set`](set.md) instead of a `Sequence`.

### `->toIdentity()`

This method wraps the sequence in an [`Identity` monad](identity.md).

Let's say you have a sequence of strings representing the parts of a file and you want to build a file object:

=== "Do"
```php
$file = Sequence::of('a', 'b', 'c', 'etc...')
->toIdentity()
->map(Content::ofChunks(...))
->map(static fn($content) => File::named('foo', $content))
->unwrap();
```

=== "Instead of..."
```php
$file = File::named(
'foo',
Content::ofChunks(
Sequence::of('a', 'b', 'c', 'etc...'),
),
);
```

??? note
Here `Content` and `File` are imaginary classes, but you can find equivalent classes in [`innmind/filesystem`](https://packagist.org/packages/innmind/filesystem).

### `->safeguard()`

This method allows you to make sure all values conforms to an assertion before continuing using the sequence.
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ nav:
- Maybe: structures/maybe.md
- Either: structures/either.md
- Validation: structures/validation.md
- Identity: structures/identity.md
- State: structures/state.md
- Fold: structures/fold.md
- Monoids: MONOIDS.md
Expand Down
74 changes: 74 additions & 0 deletions proofs/identity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php
declare(strict_types = 1);

use Innmind\Immutable\Identity;
use Innmind\BlackBox\Set;

return static function() {
yield proof(
'Identity::unwrap()',
given(Set\Type::any()),
static fn($assert, $value) => $assert->same(
$value,
Identity::of($value)->unwrap(),
),
);

yield proof(
'Identity::map()',
given(
Set\Type::any(),
Set\Type::any(),
),
static fn($assert, $initial, $expected) => $assert->same(
$expected,
Identity::of($initial)
->map(static function($value) use ($assert, $initial, $expected) {
$assert->same($initial, $value);

return $expected;
})
->unwrap(),
),
);

yield proof(
'Identity::flatMap()',
given(
Set\Type::any(),
Set\Type::any(),
),
static fn($assert, $initial, $expected) => $assert->same(
$expected,
Identity::of($initial)
->flatMap(static function($value) use ($assert, $initial, $expected) {
$assert->same($initial, $value);

return Identity::of($expected);
})
->unwrap(),
),
);

yield proof(
'Identity::map() and ::flatMap() interchangeability',
given(
Set\Type::any(),
Set\Type::any(),
Set\Type::any(),
),
static fn($assert, $initial, $intermediate, $expected) => $assert->same(
Identity::of($initial)
->flatMap(static fn() => Identity::of($intermediate))
->map(static fn() => $expected)
->unwrap(),
Identity::of($initial)
->flatMap(
static fn() => Identity::of($intermediate)->map(
static fn() => $expected,
),
)
->unwrap(),
),
);
};
18 changes: 18 additions & 0 deletions proofs/sequence.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
declare(strict_types = 1);

use Innmind\Immutable\Sequence;

return static function() {
yield test(
'Sequence::toIdentity()',
static function($assert) {
$sequence = Sequence::of();

$assert->same(
$sequence,
$sequence->toIdentity()->unwrap(),
);
},
);
};
69 changes: 69 additions & 0 deletions src/Identity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php
declare(strict_types = 1);

namespace Innmind\Immutable;

/**
* @psalm-immutable
* @template T
*/
final class Identity
{
/** @var T */
private mixed $value;

/**
* @param T $value
*/
private function __construct(mixed $value)
{
$this->value = $value;
}

/**
* @psalm-pure
* @template A
*
* @param A $value
*
* @return self<A>
*/
public static function of(mixed $value): self
{
return new self($value);
}

/**
* @template U
*
* @param callable(T): U $map
*
* @return self<U>
*/
public function map(callable $map): self
{
/** @psalm-suppress ImpureFunctionCall */
return new self($map($this->value));
}

/**
* @template U
*
* @param callable(T): self<U> $map
*
* @return self<U>
*/
public function flatMap(callable $map): self
{
/** @psalm-suppress ImpureFunctionCall */
return $map($this->value);
}

/**
* @return T
*/
public function unwrap(): mixed
{
return $this->value;
}
}
8 changes: 8 additions & 0 deletions src/Sequence.php
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,14 @@ public function toSet(): Set
return $this->implementation->toSet();
}

/**
* @return Identity<self<T>>
*/
public function toIdentity(): Identity
{
return Identity::of($this);
}

/**
* @return list<T>
*/
Expand Down

0 comments on commit 2ec917c

Please sign in to comment.