Scala map2 over tuple with inner monoid (or: how to do it simple but better?)
Situation:
A stream (RxScala) of events that we fire with tumblingBuffer () and then create a complete history for debugging. In the end, I want them to be in (Seq [T], Seq [T]) of all values, so I created the following function as an accumulator for foldLeft:
def tupleConcat[S](a: (Seq[S], Seq[S]), b: (Seq[S], Seq[S])) = (a._1 ++ b._1, a._2 ++ b._2)
Now, for me, I've posted a bunch of warning bells after reading Runar and Paul's functional programming in Scala, as it looks a lot like a map2 of two monoid instances, but I'm still a little stuck on how to generalize it correctly. So far, I think it might look something like this:
def tupleConcatSG[S](a: (S,S), b: (S,S))(implicit s: Semigroup[S]) = (a._1 |+| b._1, a._2 |+| b._2)
(but I will need to promote my Seq to IndexedSeq from what I can collect).
To generalize a further approach to any applicative, I think I need an instance for the tuples, which perhaps comes from Shapeless? Or am I missing something obvious?
EDIT: I should also add that I try to avoid zipping and unzipping based on biased performance issues, but maybe I shouldn't worry about that ... (each drum buffer (Seq, Seq) will be ~ 15,000 long, and the final (Seq, Seq) should be in millions).
source to share
Part of the tuple already exists; in the worst case, you need a shapeless skalaz. Yours tupleConcatSG
is ok (you can use : Semigroup
sugar, not explicit implicit), but if you want to use |+|
you need to make an instance Semigroup
and available implicitly
implicit def tupleConcatSg[S: Semigroup] = new Semigroup[(S, S)] {
def append(f1: (S, S), f2: (S, S)) = ...
}
I suspect your real problem is that skalaz doesn't come with instances for Seq
, only for IndexedSeq
- see Why List is a semigroup but Seq is not? ... But you can write your own Monoid[Seq]
easily enough if you are not worried about the consequences of using Seq
++
on List
s:
implicit object SeqMonoid extends Monoid[Seq]{...}
I'm not sure what you mean to generalize further to any Applicator - we're pretty general already. If you're talking about getting instances Applicative
for a composition Applicative
, for example List[Writer[Vector[String], A]]
, this should happen automatically.
source to share
Following lmm's answer, is this really what I am trying to do? (types are checked and I switched from Seq to IndexedSeq to make sure we can use ++ correctly).
def tupleConcatMonoid[M: Monoid] = new Monoid[(M, M)] {
def zero: (M,M) = (Monoid[M].zero, Monoid[M].zero)
def append(f1: (M, M), f2: (M, M)) = (f1._1 |+| f2._1, f2._1 |+| f2._2)
}
val isdMonoid= tupleConcatMonoid[IndexedSeq[Double]]
val history = tumbledBuffers.foldLeft(isdMonoid.zero)(isdMonoid.append)
It has all the correct types, but I'm not 100% sure of the "magic" level. I tried to do
tumbledBuffers.suml
But it kicked vectors out of the observable ...
EDIT: As I understand it, my real question is why this monoid instance type for tuples doesn't exist yet, or if so, what is the syntax for using it so that I can add vector tuples monoidally without taking the last step monoidal addition of vectors themselves?
source to share