Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Immutable Laravel Package

innmind/immutable

Innmind/Immutable provides persistent, immutable collections for PHP, including sequences, sets, maps, and streams. It offers a functional API with lazy operations, structural sharing, and type-safe wrappers for predictable, side-effect-free data manipulation.

Deep Wiki
Context7

Getting Started

Install with Composer (PHP 8.x is required on current majors):

composer require innmind/immutable

Look first at the README for the class map, then at src/ — each class mirrors a PHP primitive or an effect type:

  • Innmind\Immutable\Str — immutable, multibyte-aware string
  • Innmind\Immutable\Seq — ordered sequence (the old Stream)
  • Innmind\Immutable\Set — unique values
  • Innmind\Immutable\Map — key/value structure with Maybe-returning lookups
  • Innmind\Immutable\Maybe / Either — optional values and disjoint results

A good first use case is replacing an array pipeline in one service with Seq, and replacing a ?User return with Maybe:

use Innmind\Immutable\Seq;
use Innmind\Immutable\Str;

$names = Seq::of('alice', 'bob', 'charlie')
    ->map(fn(string $name) => Str::of($name))
    ->filter(fn(Str $name) => $name->length() > 3)
    ->map(fn(Str $name) => $name->toUpper())
    ->toList(); // ['ALICE', 'CHARLIE']

That single refactor exercises the package's core value proposition: no defensive copying, no accidental mutation, and a small, composable API for the operations you already write by hand.

Implementation Patterns

Convert at the boundaries, stay immutable inside. Controllers, jobs, and Eloquent are array/Collection-shaped. Wrap raw input into a Seq/Map in the entry point, do the domain work with the immutable types, then ->toList() (or ->toString()) before returning JSON, filling a model, or handing data back to Laravel. This keeps the mutable framework surface at the edges and gives you predictable value semantics in the middle.

Pipeline style instead of loops. map/filter/reduce/foreach cover almost everything you'd reach for a foreach to do. Use ->reduce() to compute aggregates and ->foreach() when the side effect is the point (logging, dispatching). Because every step returns a new collection, pipelines read top-to-bottom and are trivial to unit test one stage at a time.

Use Maybe for optionality, not null. ->get($key), ->find($callback), ->first(), and ->last() return Maybe. Compose with ->map()/->flatMap() and unwrap once with ->match(fn($v) => ..., fn() => ...) at the edge. This eliminates the if ($x !== null) ladder in lookups.

Use Either for typed failures. At a service boundary, return Either::left(DomainError) or Either::right($value). Consumers pattern-match instead of catching exceptions for expected outcomes; keep exceptions for truly exceptional states.

Choose the structure for the semantics. Set gives deduplication; Map gives keyed lookup via ->get() and is the right shape for "index this list by ID"; Seq preserves order and duplicates. Model domain value objects by holding a Str or Seq internally and exposing ->toString()/->toList() — this makes the immutability viral only inside your domain, not across the whole app.

Lean on the generics. The package is annotated for Psalm/PHPStan. Configure static analysis and you get typed map/filter callbacks and useful IDE hints; without it you lose most of the ergonomic benefit.

Laravel-specific integration. Alias imports to avoid confusion with framework helpers (use Innmind\Immutable\Str as IStr;), and be explicit about which layer owns string/collection logic. Populating a Seq of Eloquent models is fine for a read-only pipeline, but convert back before passing to Blade or query builders — those expect native types.

Gotchas and Tips

  • Every method returns a new instance. $seq->map(...) on its own is a no-op; you must reassign or chain. This is the most common source of "why is my data unchanged" bugs.
  • Str is not a string. Laravel helpers, PDO, and json_encode won't accept it. Call ->toString() at the boundary; forgetting produces confusing type errors far from the cause.
  • Equality is ->equals(), not == or ===. Two Seqs with the same contents may be different objects.
  • Map key handling is version-sensitive. Depending on the major, string keys are Str objects; mixing 'foo' and Str::of('foo') can cause silent lookup misses. Pick one convention and enforce it in a factory method.
  • Lookups return Maybe. $map->get($k) does not return null; don't if ($x === null) it — use ->match() or ->otherwise().
  • Monads don't short-circuit loops. A Seq<Maybe> still iterates everything; convert to Maybe<Seq> early or collect then reduce.
  • Watch allocation in hot paths. Structural sharing makes copies cheap, but each map/filter step still allocates an intermediate. In tight loops or large datasets, prefer a single ->reduce() or ->foreach() with a mutable accumulator over a long chain.
  • Debugging is awkward with var_dump. Internals are private wrappers; dump ->toList() / ->toString(), or use ->foreach(fn($v) => dump($v)) to inspect lazily without materializing twice.
  • No JSON serialization out of the box. response()->json($seq) will not produce the array you expect; convert first.
  • Pin the major version. The API has moved across releases (notably the Stream → Seq rename). Read the upgrade notes before bumping, since this is a small-ecosystem package where the changelog is the documentation.
  • Extend by composition, not inheritance. Wrapping these types in your own small class is safer than extending Seq or implementing Collection unless you need interoperability with map/filter/reduce.
  • Avoid null inside collections. Use an empty Seq::of() for "no items" and Maybe for "maybe one item"; mixing both representations in one codebase defeats the point of the package.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource