Skip to content

Instantly share code, notes, and snippets.

@stefanofago73
Last active February 10, 2021 20:36
Show Gist options
  • Save stefanofago73/bb888da8e60c2d530e86791eb07bbb09 to your computer and use it in GitHub Desktop.
Save stefanofago73/bb888da8e60c2d530e86791eb07bbb09 to your computer and use it in GitHub Desktop.
<?php declare(strict_types = 1);
/**
* NOTE:
*
* This example is for that legacy code where it's possible to
* refactor a sequence of action using a custom DSL.
* When not possible to refactor to FP or before using Try-Monad we can
* simulate something like a functor...
*
*/
/**
* @template T
*
*
*/
class Result
{
/** @var T|null $value */
private $value;
/** @var array<\Exception> $exceptions */
private array $exceptions =[];
/**
*
* @param array<\Exception> $exceptions
*/
private function __construct(array $exceptions)
{
$this->exceptions=$exceptions;
}
public final function isError():bool
{
return $this->exceptions !== [];
}
/**
* @template R
* @param R $value
* @return Result<R>
*/
public static final function OK($value):Result
{
$result = new Result([]);
$result->value = $value;
return $result;
}
/**
* @template R
* @param array<\Exception> $exceptions
* @return Result<R>
*/
public static final function KO(array $exceptions):Result
{
return new Result($exceptions);
}
}
/**
* @template U
*/
class TryF{
/** @var array<callable> $slots */
private array $slots;
private function __construct()
{
$this->slots = [];
}
/**
* @template R
* @param callable():R $operation
* @return TryF<R>
*/
public static final function with(callable $operation):TryF
{
$instance = new TryF();
$instance-> andThen($operation);
return $instance;
}
/**
*@param callable():U $operation
*@return TryF<U>
*/
public final function andThen(callable $operation):TryF
{
$this->slots[]=$this->call($operation);
return $this;
}
/**
*@param callable():U $operation
*@param Result<U> $fallback
*@return TryF<U>
*/
public final function andThenOrFallback(callable $operation, Result $fallback):TryF
{
$this->slots[]=$this->callWithFallback($operation,$fallback);
return $this;
}
/** @return array<Result<U>> */
public final function result():array
{
/** @var array<Result<U>> $data */ $data = [];
/** @var callable():Result<U> $calling */
foreach($this->slots as $calling)
{
$data[]=$calling();
}
return $data;
}
/**
* @param callable():U $operation
* @param Result<U> $fallback
* @return callable():Result<U>
*/
private function callWithFallback(callable $operation, Result $fallback):callable
{
return function()use($operation,$fallback):Result{
try{ return Result::OK($operation());}catch(\Exception $exc){ return $fallback; }
};
}
/**
* @param callable():U $operation
* @return callable():Result<U>
*/
private function call(callable $operation):callable
{
return function()use($operation):Result{
try{ return Result::OK($operation());}catch(\Exception $exc){ return Result::KO([$exc]); }
};
}
}
class DTO
{
private int $id;
public function __construct(int $id)
{
$this->id=$id;
}
public function id():int
{
return $this->id;
}
}
$results = TryF::with(fn()=>new DTO(0))
->andThen(fn()=>throw new \Exception("Boooom!!!"))
->andThen(fn()=>new DTO(10))
->andThenOrFallback(fn()=>throw new \Exception("KAAAZZZAAAA!!!"),Result::OK(new DTO(5)))
->result();
var_export($results);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment