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).

+3


source to share


2 answers


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.

+6


source


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?

+1


source







All Articles