I have this:
Set[ValidatedNel[String, Double]]
and I would like to sum the Doubles in it to get:
ValidatedNel[String, Double]
If some elements in the values are then I would like to have matching strings.
I played with Set.sum and Numeric to no avail...
Here is the test of what I would like to achieve:
test("Summing ValidatedNel works") {
val val1: ValidatedNel[String, Double] = Valid(1.0)
val val2: ValidatedNel[String, Double] = Valid(2.0)
val values: Set[ValidatedNel[String, Double]] = Set(val1, val2)
val validatedNelNumeric: Numeric[ValidatedNel[String, Double]] = ???
val sum = values.sum(validatedNelNumeric)
assert(sum == Valid(3.0))
}
I don't manage to create the validatedNelNumeric...
To start with: it feels a little weird to use a set in this case (for a collection of Validated[..., Double]
values). What part of the Set
semantics do you care about? The unorderedness? Uniqueness?
In general the most straightforward way to sum up elements that have a Monoid
instance is to use the combineAll
method for things with a Foldable
instance—for example a List
(but not Set
).
import cats.data.{ Validated, ValidatedNel }
import cats.instances.double._, cats.instances.list._
import cats.syntax.foldable._
// or just import cats.implicits._
val val1: ValidatedNel[String, Double] = Validated.valid(1.0)
val val2: ValidatedNel[String, Double] = Validated.valid(2.0)
val bad1: ValidatedNel[String, Double] = Validated.invalidNel("foo")
val bad2: ValidatedNel[String, Double] = Validated.invalidNel("bar")
val values = Set(val1, val2)
val withSomeBadOnes = Set(val1, bad1, val2, bad2)
And then:
scala> values.toList.combineAll
res0: cats.data.ValidatedNel[String,Double] = Valid(3.0)
scala> withSomeBadOnes.toList.combineAll
res1: cats.data.ValidatedNel[String,Double] = Invalid(NonEmptyList(foo, bar))
I'm guessing that's what you mean by "If some elements in the values are then I would like to have matching strings"?
You could also use SortedSet
, since Cats provides a Foldable
instance for SortedSet
, but it's not as convenient:
scala> import cats.implicits._
import cats.implicits._
scala> import scala.collection.immutable.SortedSet
import scala.collection.immutable.SortedSet
scala> (SortedSet.empty[ValidatedNel[String, Double]] ++ values).combineAll
res2: cats.data.ValidatedNel[String,Double] = Valid(3.0)
scala> (SortedSet.empty[ValidatedNel[String, Double]] ++ withSomeBadOnes).combineAll
res3: cats.data.ValidatedNel[String,Double] = Invalid(NonEmptyList(bar, foo))
You could also use the standard fold
and the |+|
operator for monoids:
scala> values.fold(Validated.valid(0.0))(_ |+| _)
res4: cats.data.ValidatedNel[String,Double] = Valid(3.0)
To sum up: you can't call combineAll
directly on your Set
, since Cats doesn't provide a Foldable
for Set
. I'd suggest carefully reconsidering your use of the Set
in any case, but if you decide to stick with it, you have a few options: convert to List
or SortedSet
like I have above, use the standard fold
on Set
, or finally write your own Foldable[Set]
or use the one from alleycats.