isn't `reduce(items.map(prepare(_)))` the same thing here?  Maybe something like:

```scala
def appendAll(old: B, items: TraversableOnce[A]): B =
  if (items.isEmpty) old else reduce(old, reduce(items.map(prepare(_))))
```
That could also be in the base trait, right?

I kind of regret that have `reduce(items: TraversableOnce[B]): B` because it assumes the empty list has a sane result, which might not be true of all Aggregators (not true for Semigroups, for instance). So, I don't want to repeat that for append, BUT as you have it here, there is always a sensible default (0 or 1 for Monoid and Ring).  These could call `appendAll(monoid.zero, items)` for instance on the base trait.