Scala map2 над кортежем с внутренним моноидом (или как сделать это просто, но лучше?)

Ситуация:

Поток (RxScala) событий, которые мы группируем с помощью tumblingBuffer(), а затем строим полную историю для отладки. В конечном итоге я хочу, чтобы они были в (Seq[T], Seq[T]) всех значений, поэтому я создал следующую функцию в качестве аккумулятора для foldLeft:

def tupleConcat[S](a: (Seq[S], Seq[S]), b: (Seq[S], Seq[S])) = (a._1 ++ b._1, a._2 ++ b._2)

Теперь это вызвало у меня кучу предупреждений после прочтения книги Рунара и Пола «Функциональное программирование в Scala», так как это очень похоже на map2 двух экземпляров моноида, но я все еще немного застрял в том, как правильно его обобщить. . Пока я думаю, что это может выглядеть примерно так:

def tupleConcatSG[S](a: (S,S), b: (S,S))(implicit s: Semigroup[S]) = (a._1 |+| b._1, a._2 |+| b._2)

(но мне пришлось бы повысить свой Seq до IndexedSeq из того, что я могу собрать).

Чтобы обобщить дальше на любой Applicative, я думаю, мне понадобится экземпляр для кортежей, который, возможно, исходит от Shapeless? Или я упускаю что-то очевидное?

РЕДАКТИРОВАТЬ: я также должен добавить, что я пытаюсь избежать архивирования и распаковывания, основываясь на предвзятых соображениях производительности, но, возможно, мне не следует беспокоиться об этом... (каждая ценность tumblingBuffer (Seq,Seq) будет иметь длину ~ 15 000, и окончательный (Seq,Seq) должен быть в миллионах).


person experquisite    schedule 15.12.2014    source источник


Ответы (2)


Часть кортежа уже существует; в худшем случае вам понадобится бесформенный скалаз. Ваш tupleConcatSG в порядке (вы можете использовать сахар : Semigroup вместо явного неявного), но если вы хотите иметь возможность использовать |+|, вам нужно сделать его экземпляром Semigroup и доступным неявно:

implicit def tupleConcatSg[S: Semigroup] = new Semigroup[(S, S)] {
  def append(f1: (S, S), f2: (S, S)) = ...
}

Я подозреваю, что ваша настоящая проблема заключается в том, что scalaz не имеет экземпляров для Seq, только для IndexedSeq - см. Почему List является полугруппой, а Seq — нет? . Но вы можете написать свой собственный Monoid[Seq] достаточно легко, если вас не беспокоит влияние на производительность использования ++ Seq на Lists:

implicit object SeqMonoid extends Monoid[Seq]{...}

Я не уверен, что вы имеете в виду, говоря о дальнейшем обобщении любого аппликатива - мы уже довольно общие. Если вы говорите о получении Applicative экземпляров для композиции из Applicative, таких как List[Writer[Vector[String], A]], это должно происходить автоматически.

person lmm    schedule 15.12.2014

Следуя ответу lmm, действительно ли это то, что я пытаюсь сделать? (типы проверены, и я переключился с Seq на IndexedSeq, чтобы убедиться, что мы можем правильно использовать ++).

    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)

Здесь есть все правильные типы, но я не уверен на 100% на уровне «волшебства». я пытался сделать

    tumbledBuffers.suml

Но это выкинуло векторы из Observable...

РЕДАКТИРОВАТЬ: Я думаю, что мой реальный вопрос заключается в том, почему такого экземпляра моноида для кортежей еще не существует, или если он существует, каков синтаксис для его использования, чтобы я мог моноидально добавлять кортежи векторов, не делая последний шаг моноидального добавления самих векторов?

person experquisite    schedule 15.12.2014
comment
Я не совсем понимаю вопрос (кажется, пропущено слово) - возможно, вы могли бы привести конкретный пример того, что вы хотите, когда кортежи, например. (Vector(2, 3), Vector(4)) и (Vector(), Vector(5)) или что-то в этом роде? Вы не можете объединять кортежи в более широкие кортежи моноидальным способом (по крайней мере, кортежи достигают только размера 22). Если вы хотите лениво собирать свои вещи, ничего не вычисляя, это случай для Free Monoid, также известного как List или Vector. - person lmm; 16.12.2014
comment
Если вы хотите использовать tupleConcatMonoid с suml, вы должны объявить его implicit, но то же самое уже существует в TupleInstances, поэтому при правильном импорте (например, scalaz._, Scalaz._) он уже должен быть там. Является ли tumbledBuffers типом, для которого существует экземпляр scalaz Foldable? (это опять та же самая проблема Seq/IndexedSeq?) - person lmm; 16.12.2014
comment
Я окончательно исправил проблему IndexedSeq, и мой вышеприведенный tupleConcatMonoid, кажется, делает правильную вещь - единственный вопрос теперь в том, нужно ли это делать вообще? Учитывая ваши векторы, я бы хотел: (Вектор (2,3), Вектор (4,5)) - person experquisite; 16.12.2014
comment
Это должно работать без его объявления, и это работает на scastie: scastie.org/7699 - person lmm; 16.12.2014
comment
Да, хорошо, так что моя оставшаяся проблема связана исключительно с RxScala. Спасибо за вашу помощь! - person experquisite; 16.12.2014