Bring the power of functional programming to PHP with Phunkie. Immutable data structures, type-safe operations, lazy evaluation, and a powerful REPL for interactive development.
Write safer, more maintainable code with functional programming principles
Data structures that never change, eliminating a whole class of bugs and making your code more predictable.
Strong typing with Option, Either, and Validation types to handle errors gracefully and make invalid states unrepresentable.
Work with infinite data structures and defer computation until needed, improving performance and expressiveness.
Build complex operations from simple, reusable functions using map, flatMap, filter, and other combinators.
Express complex domain logic concisely with algebraic data types and pattern matching, reducing boilerplate and cognitive load.
Comprehensive collection of functional data structures, monads, and utilities inspired by Scala and Haskell ecosystems.
An interactive REPL for exploring functional programming concepts
The Phunkie Console provides a powerful REPL (Read-Eval-Print Loop) for interactive development. Experiment with functional programming concepts, test your code, and explore type signatures in real-time.
Install:
composer require phunkie/console
Run:
./vendor/bin/phunkie
// Welcome to phunkie console.
phunkie > ImmList(1, 2, 3, 4, 5)
$var0: ImmList<Int> = List(1, 2, 3, 4, 5)
phunkie > $var0->map(fn($x) => $x * 2)
$var1: ImmList<Int> = List(2, 4, 6, 8, 10)
phunkie > Some(42)->map(fn($x) => $x + 8)
$var2: Option<Int> = Some(50)
phunkie > :type $var2
Option<Int>
phunkie > :kind Option
* -> *
Lazy, infinite, and composable stream processing
use Phunkie\Streams\Stream;
// Create an infinite stream of natural numbers
$naturals = Stream::iterate(1, fn($n) => $n + 1);
// Take first 10 even numbers
$evens = $naturals
->filter(fn($n) => $n % 2 === 0)
->take(10)
->toList();
// Stream from a file lazily
$lines = Stream::fromFile('data.txt')
->map(fn($line) => trim($line))
->filter(fn($line) => !empty($line));
Handle infinite sequences, process large files efficiently, and compose complex transformations with ease. Streams are evaluated lazily, meaning computations are only performed when needed.
Install:
composer require phunkie/streams
Manage side effects safely with the IO monad
The Effect system allows you to describe side effects as values, making them composable, testable, and referentially transparent. Separate effect description from execution for better reasoning about your code.
Install:
composer require phunkie/effect
use Phunkie\Effect\IO;
// Describe effects as values
$readFile = new IO(fn() =>
file_get_contents('config.json')
);
$parseJson = fn($content) =>
new IO(fn() => json_decode($content));
// Compose effects
$program = $readFile
->flatMap($parseJson)
->map(fn($config) => $config->database)
->handleError(fn($e) =>
defaultConfig()
);
// Execute effects
$result = $program->unsafeRun();
HTTP as functions: routes, handlers that return IO, bodies that stream
Inspired by http4s. Functional API builder built on top of Phunkie Streams.
Install:
composer require phunkie/http4p
return fn(Connection $conn): callable => Through(
new Router(HttpRoutes(
GET('/books/:id', fn(int $id) =>
findOrFail(Book::class, $id)->run($conn)->flatMap(Ok(...))),
POST('/books', fn(Request $req) =>
decode($req, Book::class)
->flatMap(fn(array $data) => create(Book::class, $data)->run($conn))
->flatMap(Created(...))),
GET('/books/export', fn() =>
all(Book::class)->stream()->run($conn)
->flatMap(fn(Stream $books) =>
Ok($books->map(fn($book) => json_encode($book) . "\n")))),
)),
Recover(RowNotFound::class, fn($e) => NotFound(['error' => $e->getMessage()])),
);
A functional data layer: queries as values, rows as streams
#[Table('books')]
final readonly class Book
{
public function __construct(
#[Generated] public int $id,
public string $title,
public Isbn $isbn,
public Format $format,
public DateTimeImmutable $publishedOn,
) {}
}
$conn = connect('sqlite:catalogue.sqlite')->unsafeRun();
create(Book::class, ['title' => 'Notes', 'isbn' => new Isbn('9780000000002'), ...])
->run($conn); // IO<Book>
where(Book::class, 'format', Format::Ebook)
->orderBy('title')->limit(10)
->run($conn); // IO<ImmList<Book>>
all(Book::class)->stream()->run($conn); // IO<Stream<Book>>, one row at a time
A Query is a function from a Connection to an
IO. Functional db layer for SQLite, MySQL or PostgreSQL
Install:
composer require phunkie/phetch
Everything you need to get started with Phunkie
Installation guide and first steps with Phunkie
ImmList, ImmSet, ImmMap, and more
Option, Either, Validation types
Understanding functors and their laws
Monadic composition and transformers
IO monad and side effect management
Routes as functions, streaming bodies, a streaming client
Queries as values, models, streaming rows, migrations
Contribute, ask questions, and help shape the future of functional PHP