Scalaz自动类​​型类分辨率

时间:2016-01-11 13:06:11

标签: scala functional-programming typeclass scalaz

我有几个Map[String, String]并希望合并它们,因此值保持不变。默认情况下,Map semigroup将连接字符串,所以我使用Tags.LastVal。 目前我正在做以下事情:

m1.mapValues(Tags.LastVal) |+| m2.mapValues(Tags.LastVal)

这种映射看起来冗长乏味。 我可以以某种方式告诉编译器在范围内使用特定的半群,如Semigroup[Map[K, V @@ LastVal]而不是默认的?

1 个答案:

答案 0 :(得分:2)

是的,您可以在需要的范围内隐藏您想要的实例,编译器可以找到它:

import scalaz._, Scalaz._

def mergeWithReplace(m1: Map[String, String], m2: Map[String, String]) = {
  implicit val stringInstance: Semigroup[String] = Semigroup.lastSemigroup
  m1 |+| m2
}

然后:

scala> mergeWithReplace(Map("a" -> "foo", "b" -> "qux"), Map("a" -> "bar"))
res0: Map[String,String] = Map(a -> bar, b -> qux)

然而,除了关于连贯性的理论论证之外,这是令人烦恼和脆弱的。例如,隐式的名称必须是stringInstance,以便隐藏从scalaz.std.string导入的隐式,因为那个具有更具体的类型,即使您没有要求更多具体类型。我不记得是否有一些优先行为的借口,我真的不想关心,所以我倾向于避免使用像这样的本地实例。

在这种情况下,碰巧有一个更好的解决方案。 Scalaz为地图提供Plus个实例,而Plus不知道值类型,因此它通过选择最后一个值来组合值。这意味着您可以使用<+>中的Plus运算符来获得所需内容:

def mergeWithReplace(m1: Map[String, String], m2: Map[String, String]) =
  m1 <+> m2

然后:

scala> mergeWithReplace(Map("a" -> "foo", "b" -> "qux"), Map("a" -> "bar"))
res0: scala.collection.immutable.Map[String,String] = Map(a -> bar, b -> qux)
但是,你通常不会那么幸运。