Введение
Функциональное программирование появилось не вчера. Но оно так и не приобрело какой-либо дикой популярности, и, вероятно, не просто так. Иногда оно может быть довольно сложным с точки зрения понимания и использования. Но у него есть много преимуществ. Одним из них является возможность избежать проверок на null и исключений.
В этой статье мы рассмотрим монаду Maybe и то, как ее можно использовать в Symfony.
Что из себя представляет монада Maybe?
Начать следует с определения самой концепции монады. Монада (monad) — это структура, предназначенная для представления вычислений в виде императивной последовательности шагов. Она является обобщением концепции функции, которая принимает аргумент и возвращает результат. Монады в функциональном программировании не является чем-то новым. Они существуют с 1960-х годов.
Монада Maybe — это монада, которая инкапсулирует необязательное значение. Значение типа Maybe a содержит либо значение типа а (представленное как Just a), либо вообще ничего (представленное как Nothing). Используя монаду Maybe, мы можем избежать null и исключений.
Как использовать монаду Maybe в Symfony?
Для начала давайте создадим класс-монаду, который будет реализовывать монаду Maybe.
// src/Utils/Maybe.php
<?php
namespace App\Utils;
/**
* @template T
*/
class Maybe
{
/**
* @var T|null
*/
private $value;
/**
* @param T|null $value
*/
private function __construct($value)
{
$this->value = $value;
}
/**
* @param T|null $value
* @return Maybe<T>
*/
public static function just($value): Maybe
{
return new self($value);
}
/**
* @return Maybe<T>
*/
public static function nothing(): Maybe
{
return new self(null);
}
/**
* @template U
* @param callable(T):U $fn
* @return Maybe<U>
*/
public function map(callable $fn): Maybe
{
if ($this->value === null) {
return self::nothing();
}
return self::just($fn($this->value));
}
/**
* @param T $defaultValue
* @return T
*/
public function getOrElse($defaultValue)
{
return $this->value ?? $defaultValue;
}
}
Класс Maybe
содержит два статических метода: just
и nothing. just
метод создает объект Maybe
со значением.
Метод nothing
создает объект Maybe
без значения. Метод map
принимает в качестве аргумента функцию и применяет ее к значению внутри объекта Maybe
. Если значение внутри объекта Maybe
null, метод map
возвращает nothing
. Метод getOrElse
возвращает значение из объекта Maybe
или значение по умолчанию, если значение внутри объекта Maybe
– null.
Давайте посмотрим, как его можно использовать в Symfony-приложении.
// src/Controller/DefaultController.php
<?php
namespace App\Controller;
use App\Entity\User;
use App\Service\UserSrvice;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class DefaultController extends AbstractController
{
#[Route('', name: 'default')]
public function getUserData(Request $request, UserSrvice $userSrvice): JsonResponse
{
$email = $request->get('email');
$maybeUser = $userSrvice->getUserByEmail($email);
$userData = $maybeUser
->map(fn(User $user) => [
'name' => $user->getName(),
'email' => $user->getEmail(),
])
->getOrElse([
'name' => 'Unknown',
'email' => 'Unavailable',
])
;
return $this->json($userData);
}
}
// src/Service/UserSrvice.php
<?php
declare(strict_types=1);
namespace App\Service;
use App\Repository\UserRepository;
use App\Utils\Maybe;
class UserSrvice
{
public function __construct(private readonly UserRepository $userRepository)
{
}
public function getUserByEmail(string $email): Maybe
{
return Maybe::just($this->userRepository->getUserByEmail($email));
}
}
В классе DefaultController
мы получаем электронное письмо из запроса. Затем мы определяем пользователя по этому электронному письму, используя класс UserSrvice
.
Класс UserSrvice
возвращает объект Maybe
. Для получения пользовательских данных мы используем метод map
. Если пользователь не найден, метод map
возвращает nothing.
Затем мы используем метод getOrElse
для получения пользовательских данных или значения по умолчанию, если пользователь не найден.
Заключение
В этой статье мы рассмотрели монаду Maybe и то, как ее можно использовать в Symfony. Мы создали класс Maybe
, реализующий монаду Maybe. Мы использовали класс Maybe
в классе DefaultController
, чтобы избежать проверки на null и необходимости использовать исключения. Используя этот подход, мы можем избежать проверки на null и исключений почти во всем нашем Symfony-приложении, что сделает код более читабельным.
Полный код вы можете найти на GitHub
На днях пройдет открытый урок «Twig и Symfony forms: создаем полноценное веб-приложение без погружения во frontend». На нем разработаем быструю и простую административную панель штатными средствами фреймворка.
Занятие пройдет в преддверии старта курса "Symfony Framework". Записаться на открытый урок можно по ссылке.