https://github.com/franzose/lemonad
Yet another monads implementation written in PHP
https://github.com/franzose/lemonad
algorithms datastructures maybe maybe-monad monad monads optional php try try-monad
Last synced: 6 months ago
JSON representation
Yet another monads implementation written in PHP
- Host: GitHub
- URL: https://github.com/franzose/lemonad
- Owner: franzose
- License: mit
- Created: 2019-01-28T10:24:49.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2019-02-01T01:35:53.000Z (almost 7 years ago)
- Last Synced: 2025-02-22T18:50:45.626Z (11 months ago)
- Topics: algorithms, datastructures, maybe, maybe-monad, monad, monads, optional, php, try, try-monad
- Language: PHP
- Homepage:
- Size: 42 KB
- Stars: 2
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Lemonad
It is a small repository containing implementations of some monads.
1. [Optional](https://github.com/franzose/lemonad#optional)
2. [Maybe](https://github.com/franzose/lemonad#maybe)
3. [Try](https://github.com/franzose/lemonad#try)
## Optional
`Optional` is useful when you don‘t exactly sure if you‘re dealing with an empty value or some meaningful one. It provides a clean and expressive API.
```php
isPresent(); // false
$empty->isAbsent(); // true
$optOf42 = Optional::of(42);
$optOf42->isPresent(); // true
$optOf42->isAbsent(); // false
$null = optional(null);
$null->equals(Optional::empty()); // true
$null->isAbsent(); // true
$null->isPresent(); // false
$present = Optional::of(42)->filter(function (int $value) {
return 42 === $value;
});
$present->isPresent(); // true
$present->isAbsent(); // false
$absent = Optional::of(42)->filter(function (int $value) {
return 43 === $value;
});
$absent->isPresent(); // false
$absent->isAbsent(); // true
optional(42)->map(function (int $value) {
return $value + 1;
})->get(); // 43
Optional::of(42)->flatMap(function (int $value) {
return Optional::of($value + 1);
})->get(); // 43
Optional::of(42)->ifPresent(function (int $value) {
echo $value;
}); // should output "42"
Optional::ofNullable(null)->ifPresentOrElse(function (int $value) {
echo $value;
}, function () {
echo '999';
}); // should output "999"
Optional::ofNullable(null)->or(function () {
return Optional::of(42);
})->get(); // 42
Optional::ofNullable(null)->orElse(42); // 42
Optional::ofNullable(null)->orElseGet(function () {
return 42;
}); // 42
Optional::ofNullable(null)->orElseThrow(function () {
return new \InvalidArgumentException('No! No! No!');
}); // it will throw \InvalidArgumentException exception
```
## Maybe
Here are some examples of the Maybe monad:
```php
isKnown(); // false
$known = Maybe::definitely(42);
$known->isKnown(); // true
Maybe::of(null)->isKnown(); // false
Maybe::of(42)->isKnown(); // true
Maybe::of(42)->or(999); // 42
Maybe::of(42)->orElse(Maybe::definitely(999)); // same instance with value of 42
Maybe::of(42)->to(function (int $value) {
return $value + 1;
}); // Maybe with value of 43
maybe(42)->query(function (int $value) {
return 42 === $value;
}); // Maybe with value of boolean true
Maybe::of(42)->query(function (int $value) {
return 43 === $value;
}); // Maybe with value of boolean false
Maybe::of(null)->or(42); // 42
Maybe::of(null)->orElse(Maybe::definitely(42)); // Maybe with value of 42
Maybe::of(null)->to(function () {}); // always a new instance of 'unknown' Maybe
Maybe::of(null)->query(function () {}); // always a new instance of 'unknown' Maybe
Maybe::of(null)->equals(Maybe::unknown()); // equals is always false
echo Maybe::definitely('Brian')->to(function (string $name) {
return $name . ' Adams';
}); // will output "Brian Adams"
```
## Try
A Try represents a computation that may either throw an exception or return a value. As `try` is a reserved keyword in PHP and class names are case insensitive, I could not use `Try` for the class name, so I named it `LetsTry`.
```php
getOrElse(noop()); // 42
lets_try(function () {
throw new RuntimeException('Argh!');
})->getOrElse(function () {
return 42;
}); // 42
LetsTry::successful(42)->getOrElse(noop()); // 42
LetsTry::successful(null); // will throw Lemonad\Exception\NullValueException
LetsTry::failure(new RuntimeException('Argh!'))
->getOrElse(function () {
return 42;
}); // 42
// Successful, do mapping
lets_try(function () {
return 42;
})->map(function (int $value) {
return $value + 1;
})->getOrElse(noop()); // 43
lets_try(function () {
throw new RuntimeException('Argh!');
})->map(function () {
// this callback will not be called,
// new instance of `Failure` will be returned instead
});
// Successful, do mapping
lets_try(function () {
return 42;
})->flatMap(function (int $value) {
return lets_try(function () use ($value) {
return $value + 1;
});
})->getOrElse(noop()); // 43
lets_try(function () {
throw new RuntimeException('Argh!');
})->flatMap(function () {
// again, this callback will not be called,
// new instance of `Failure` will be returned instead
});
// There's nothing to recover,
// so the callback will not be called
lets_try(function () {
return 42;
})->recover(function () {
return 43;
})->getOrElse(noop()); // 42
// Again, there's nothing to recover,
// so the callback will not be called
lets_try(function () {
return 42;
})->recoverWith(function () {
return lets_try(function () {
return 43;
});
})->getOrElse(noop()); // 42
// Here, exception was thrown,
// so we need to recover from that
lets_try(function () {
throw new RuntimeException('Argh!');
})->recover(function (RuntimeException $exception) {
return $exception->getMessage();
}); // Argh!
// Again, exception was thrown,
// so we need to recover from that
lets_try(function () {
throw new RuntimeException('Argh!');
})->recoverWith(function (RuntimeException $exception) {
return lets_try(function () use ($exception) {
return $exception->getMessage();
});
}); // Argh!
lets_try(function () {
return 42;
})->orElse(function () {
return lets_try(function () {
return 43;
});
})->getOrElse(noop()); // 42 as the Try is `Success`
lets_try(function () {
throw new RuntimeException('Argh!');
})->orElse(function () {
return lets_try(function () {
return 43;
});
})->getOrElse(noop()); // 43 as the Try was `Failure`
// Will perform another Try
lets_try(function () {
return 42;
})->filterOrElse(
function (int $value) {
return 42 === $value;
},
function () {
return new RuntimeException('Argh!');
}
);
// Will throw provided RuntimeException,
// as the predicate is unsatisfied
lets_try(function () {
return 42;
})->filterOrElse(
function (int $value) {
return 99 === $value;
},
function () {
return new RuntimeException('Argh!');
}
);
// Will just return another `Failure` Try
lets_try(function () {
throw new RuntimeException('Argh!');
})->filterOrElse(
function (int $value) {
return 99 === $value;
},
function () {
return new RuntimeException('Argh!');
}
);
lets_try(function () {
return 42;
})->fold(noop(), function (int $value) {
return $value + 1;
}); // 43
lets_try(function () {
throw new RuntimeException('Argh!');
})->fold(
function (RuntimeException $exception) {
return $exception->getMessage();
},
function (int $value) {
return $value + 1;
}
); // Argh!
lets_try(function () {
return 42;
})->toOptional(); // Optional which contains value of 42
lets_try(function () {
throw new RuntimeException('Argh!');
})->toOptional(); // An empty Optional
lets_try(function () {
return 42;
})->forEach(function (int $value) {
// do something
});
lets_try(function () {
throw new RuntimeException('Argh!');
})->forEach(function () {
// for `Failure`s it is a noop
});
lets_try(function () {
return 42;
})->isSuccess(); // true
lets_try(function () {
throw new RuntimeException('Argh!');
})->isFailure(); // true
```