Reputation: 5020
I'm finding myself wanting to write this:
def allAsAndFs(node: Node): PairOfSeqs[Action, Factor] =
node.regularNames.flatMap { name => regularAsAndFs(name) } ++
node.specialNames.flatMap { name => specialAsAndFs(name) }
where:
def regularNames: Seq[String]
def specialNames: Seq[String]
def regularAsAndFs(name: String): PairOfSeqs[Action, Factor]
def specialAsAndFs(name: String): PairOfSeqs[Action, Factor]
and:
class PairOfSeqs[A, B](as: Seq[A], bs: Seq[B]) {
def ++(that: PairOfSeqs[A, B]): PairOfSeqs[A, B] =
PairOfSeqs(as ++ that.as, bs ++ that.bs)
. . .
}
What do I need to do to make flatMap
coalesce the PairsOfSeqs
by
calling the appropriate ++
method?
It looks like PairOfSeqs
should extend GenTraversableOnce
, but
GenTraversableOnce
has a rather astonishing 47 abstract methods to override.
What's the idiomatic, Scalastic way to do this?
Upvotes: 2
Views: 171
Reputation: 167901
You are using a somewhat nonstandard idea of flatMap
. The normal type signature is
class C[A] {
def flatMap[B](f: A => C[B]): C[B] = ???
}
But you want (something akin to--this will not compile):
def foo[D <: { def ++(d: D): D }](f: A => D): D
which is much more like fold
than flatMap
.
One possibility is to make PairOfSeqs
a full-fledged collection. That's going to be tough because the types really don't line up that well, and because extending collections is difficult. (The reward/effort ratio can be good because even though the effort is large the reward is huge, if you really need it.)
Perhaps the best possibility is just to have an extension method on Seq
.
case class PairOfSeqs[A, B](as: Seq[A], bs: Seq[B]) {
def ++(that: PairOfSeqs[A, B]): PairOfSeqs[A,B] =
PairOfSeqs(as ++ that.as, bs ++ that.bs)
}
implicit class SeqCanPairMap[A](val underlying: Seq[A]) extends AnyVal {
def flatPair[B,C](f: A => PairOfSeqs[B,C]): PairOfSeqs[B,C] = {
val ps = underlying.map(f)
PairOfSeqs(ps.map(_.as).fold(Seq[B]())(_ ++ _), ps.map(_.bs).fold(Seq[C]())(_ ++ _))
}
}
Seq("fish", "fowl").flatPair(s => new PairOfSeqs(s.map(_.toChar), s.map(_.toInt)))
There are a variety of other options (e.g. define a more conventional flatMap
on PairOfSeqs
, and a conversion from regular seqs to a PairOfSeqs with an empty second slot), but this probably covers your use case well enough.
Finally, to make a class flatMappable
in general, all you have to do is define flatMap
. In particular, for-comprehensions can then use it. (But you'll need a map
also or it won't be useful for for-comprehensions.)
Upvotes: 3