The Reader monad represents computations that depend on some environment or configuration. It provides a way to compose operations that read from a shared context.
A Reader is a function that takes some environment A
and produces a result B
. In Phunkie, it’s implemented as Reader<A, B>
.
class Reader {
public function __construct(callable $run) {
// run: A -> B
}
}
use function Phunkie\Functions\reader\Reader;
$getConfig = Reader(fn(array $config) => $config['database']);
$result = $getConfig->run(['database' => 'mysql']); // 'mysql'
The ask
function creates a Reader that returns the environment itself:
use function Phunkie\Functions\reader\ask;
$reader = ask();
$result = $reader->run('environment'); // 'environment'
Executes the Reader with a given environment:
$reader = Reader(fn($x) => $x * 2);
$result = $reader->run(21); // 42
Transform the result while keeping the environment:
$reader = Reader(fn(string $s) => strrev($s))
->map(fn(string $s) => strtoupper($s));
$result = $reader->run("hello"); // "OLLEH"
Chain Readers together:
$getHost = Reader(fn($config) => $config['host']);
$getPort = Reader(fn($config) => $config['port']);
$getAddress = $getHost->flatMap(
fn($host) => $getPort->map(
fn($port) => "$host:$port"
)
);
$config = ['host' => 'localhost', 'port' => 8080];
$result = $getAddress->run($config); // "localhost:8080"
Compose Readers sequentially:
$first = Reader(fn($x) => strrev($x));
$second = Reader(fn($x) => strtoupper($x));
$combined = $first->andThen($second);
$result = $combined->run("hello"); // "OLLEH"
Kleisli arrows (ReaderT) allow composition of functions that return monadic values:
use Phunkie\Cats\Kleisli;
use function Phunkie\Functions\kleisli\kleisli;
$validateUser = kleisli(fn($input) =>
Option(strlen($input) >= 3 ? $input : null)
);
$validateEmail = kleisli(fn($input) =>
Option(filter_var($input, FILTER_VALIDATE_EMAIL) ? $input : null)
);
$validate = $validateUser->andThen($validateEmail);
$result = $validate->run("invalid"); // None
$result = $validate->run("user@example.com"); // Some("user@example.com")
$getDatabaseConfig = Reader(fn($config) => $config['database']);
$getConnection = $getDatabaseConfig->map(fn($dbConfig) =>
new DatabaseConnection($dbConfig)
);
$getLogger = Reader(fn($container) => $container->get('logger'));
$logError = $getLogger->map(fn($logger) =>
$logger->error("Something went wrong")
);
$isDevelopment = Reader(fn($env) => $env['APP_ENV'] === 'development');
$getDebugInfo = $isDevelopment->flatMap(fn($isDev) =>
Reader(fn($env) => $isDev ? $env['debug_info'] : null)
);