Savage Reader
Savage Reader

Reputation: 417

Define your own semigroup in scalaz

I want to merge two maps in Scala, giving a specific function for combining values.

I really like map1 |+| map2 in scalaz, however I can't find a way how to substitute the + with something else. For example, I would like to pick the max value, or the min value for each key. Ideally, the operator would be provided as an external function, so something of the sort:

def op (a, b) = min(a,b)
map1 |op| map2

Which would give all the keys with the corresponding min values from both maps.

Upvotes: 0

Views: 323

Answers (1)

Hugh
Hugh

Reputation: 8932

The Map semigroup uses the semigroup for its value type to merge the values for each key. If you want different behaviour, you can just use a type wrapper for the values whose semigroup has different behaviour.

import scalaz._, Scalaz._

val map1 = Map("foo" → 1, "bar" → 2)
val map2 = Map("foo" → 3)

val default = map1 |+| map2
// Map(foo → 4, bar → 2): Map[String, Int]

val min = map1.mapValues(Tags.MinVal(_)) |+| map2.mapValues(Tags.MinVal(_))
// Map(foo → 1, bar → 2): Map[String, Int @@ Tags.MinVal]

You can “unwrap” the tagged values with .unwrap

import scalaz.syntax.tag._
min.mapValues(_.unwrap)
// Map(foo → 1, bar → 2): Map[String, Int]

Creating your own type wrapper is straightforward. You simply wrap your type in another type which has an appropriately defined type class instance. For example, if you wanted to create your own version of a Tags.MaxVal you could write:

case class Max[A: Order](unwrap: A)
object Max {
  implicit def semigroup[A: Order]: Semigroup[Max[A]] =
    new Semigroup[Max[A]] {
      def append(f1: Max[A], f2: ⇒ Max[A]) =
        Max(Order[A].max(f1.unwrap, f2.unwrap))
    }
}
val max = map1.mapValues(Max(_)) |+| map2.mapValues(Max(_))
max.mapValues(_.unwrap)
// Map(foo → 3, bar → 2): Map[String, Int]

Alternatively, use can use the Scalaz Tag stuff to define wrappers in a similar fashion; see scalaz.Tag, scalaz.Tags, and scalaz.std.AnyValInstances for examples of this.

Upvotes: 3

Related Questions