在scalaz中定义自己的半群

时间:2016-03-15 18:46:08

标签: scala scalaz

我想在Scala中合并两个地图,给出一个组合值的特定功能。

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

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

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

1 个答案:

答案 0 :(得分:3)

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]

创建自己的类型包装器非常简单。您只需将您的类型包装在另一个具有适当定义的类型类实例的类型中。例如,如果您想创建自己的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]

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