在 scalaz 中定义你自己的半群

Define your own semigroup in scalaz

我想在 Scala 中合并两个映射,给出一个用于组合值的特定函数。

我很喜欢map1 |+| scalaz 中的 map2,但是我找不到用其他东西替换 + 的方法。例如,我想为每个键选择最大值或最小值。理想情况下,运算符将作为外部函数提供,因此类似于:

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

这将为所有键提供来自两个映射的相应最小值。

Map 半群使用半群作为其值类型来合并每个键的值。如果您想要不同的行为,您可以只对半群具有不同行为的值使用类型包装器。

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]

您可以使用 .unwrap

“展开”标记值
import scalaz.syntax.tag._
min.mapValues(_.unwrap)
// Map(foo → 1, bar → 2): Map[String, Int]

创建您自己的类型包装器非常简单。您只需将您的类型包装在另一个具有适当定义的类型 class 实例的类型中。例如,如果您想创建自己的 Tags.MaxVal 版本,您可以这样写:

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]

或者,用户可以使用 Scalaz Tag 东西以类似的方式定义包装器;有关此示例,请参阅 scalaz.Tagscalaz.Tagsscalaz.std.AnyValInstances