简体   繁体   中英

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM