Collections: pipe and tap keep a chain readable

A collection chain reads well until it needs one step that is not a collection method — a log line, a total computed from the whole set — at which point it gets broken into three statements and a temporary variable.

$total = $orders
    ->filter->isPaid()
    ->tap(function ($c) { Log::info('paid orders', ['n' => $c->count()]); })
    ->pipe(function ($c) { return $c->sum->total; });

tap() hands the collection to a callback and returns the collection, so it is for side effects and cannot change the chain. pipe() returns whatever the callback returns, so it is for the step that leaves collection-land. Both are on the base Collection, and tap() is also a global helper that works on any object, which makes it useful for a fluent builder that forgot to return $this.