Question

I have a for-comprehension with a generator from a Set[MyType] This MyType has a lazy val variable called factsPair which returns a pair of sets: (Set[MyFact], Set[MyFact]).

I wish to loop through all of them and unify the facts into one flattened pair (Set[MyFact], Set[MyFact]) as follows, however I am getting No implicit view available ... and not enough arguments for flatten: implicit (asTraversable ... errors. (I am a bit new to Scala so still trying to get used to the errors).

lazy val allFacts  =
(for {
  mytype <- mytypeList
} yield mytype.factsPair).flatten

What do I need to specify to flatten for this to work?

Was it helpful?

Solution

You won't be able to use flatten for this, because flatten on a collection returns a collection, and a tuple is not a collection.

You can, of course, just split, flatten, and join again:

val pairs = for {
  mytype <- mytypeList
} yield mytype.factsPair
val (first, second) = pairs.unzip
val allFacts = (first.flatten, second.flatten)

OTHER TIPS

Scala flatten works on same types. You have a Seq[(Set[MyFact], Set[MyFact])], which can't be flattened.

I would recommend learning the foldLeft function, because it's very general and quite easy to use as soon as you get the hang of it:

lazy val allFacts = myTypeList.foldLeft((Set[MyFact](), Set[MyFact]())) {
  case (accumulator, next) =>
    val pairs1 = accumulator._1 ++ next.factsPair._1
    val pairs2 = accumulator._2 ++ next.factsPair._2
    (pairs1, pairs2)
}

The first parameter takes the initial element it will append the other elements to. We start with an empty Tuple[Set[MyFact], Set[MyFact]] initialized like this: (Set[MyFact](), Set[MyFact]()).

Next we have to specify the function that takes the accumulator and appends the next element to it and returns with the new accumulator that has the next element in it. Because of all the tuples, it doesn't look nice, but works.

A tuple isn't traverable, so you can't flatten over it. You need to return something that can be iterated over, like a List, for example:

List((1,2), (3,4)).flatten          // bad
List(List(1,2), List(3,4)).flatten  // good

I'd like to offer a more algebraic view. What you have here can be nicely solved using monoids. For each monoid there is a zero element and an operation to combine two elements into one.

In this case, sets for a monoid: the zero element is an empty set and the operation is a union. And if we have two monoids, their Cartesian product is also a monoid, where the operations are defined pairwise (see examples on Wikipedia).

Scalaz defines monoids for sets as well as tuples, so we don't need to do anything there. We'll just need a helper function that combines multiple monoid elements into one, which is implemented easily using folding:

def msum[A](ps: Iterable[A])(implicit m: Monoid[A]): A =
  ps.foldLeft(m.zero)(m.append(_, _))

(perhaps there already is such a function in Scala, I didn't find it). Using msum we can easily define

def pairs(ps: Iterable[MyType]): (Set[MyFact], Set[MyFact]) =
  msum(ps.map(_.factsPair))

using Scalaz's implicit monoids for tuples and sets.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top