如果没有烦人的虚拟参数来标记类型,Scala 隐式歧义就无法解决。

Scala implicit ambiguity doesn't get resolved without annoying dummy argument to mark the type.

在学习了 this 教程中的一些 Scala 高级类型示例之后,我开始尝试使用 (a) 隐式和 (b) 编写方法的技术,这些方法一般处理特征的子类 被定义为更高种类的类型。我已经在 (b) 上得到了很好的答案,这让我离解决以下问题更近了一步:

我想创建一个工厂来创建包装的单例容器(集合、列表、数组) 我传递给工厂函数的项目。 [ 即使已经有一个 Scala 做这个的库,我正在做这个作为一个学习练习。但是,嘿...如果有,请告诉我!]

最终结果应该让我这样做:

scala> var j: Set[String] =  wrapItemInContainer("foo")
j: Set[String] = Set(foo)

scala> var k: List[Int] =  wrapItemInContainer(9)
k: List[Int] = List(9)

我想到的解决方案如下所示。我必须提供一个烦人的虚拟最终参数,这样我才能 帮助编译器弄清楚我想要什么样的容器。这行得通,但令我感到困惑的是 我想将结果分配给 (j, k..etc.) 的变量的类型签名没有给出 编译器有足够的信息来确定需要使用哪个隐式定义的 ContainerFactory。

这个笨拙的解决方案有效:

trait ContainerFactory[M[_]]  { def put[A](x: A): M[A]  }


implicit val factory = new ContainerFactory[List] { def put[A](x: A) = List(x)  }   // factory for List containers
implicit val factory2 = new ContainerFactory[Set] { def put[A](x: A) = Set(x)}    // factory for Set containers

def wrapItemInContainer[ M[A]: ContainerFactory, A](item: A,  helper: M[A]) : M[A] = { 
    val c = implicitly[ContainerFactory[M]]                             
    c.put(item)
}

var j: List[Int] =  wrapItemInContainer(9, List(0))

但我真的想要一些没有嘈杂的第二个参数的东西:

def wrapItemInContainer[ M[A]: ContainerFactory, A](item: A) : M[A] = { 
    val c = implicitly[ContainerFactory[M[A]]]                             
    c.put(item)
}

var j: List[Int] =  wrapItemInContainer(9)    // this does not work.

我收到此错误:

<console>:17: error: ambiguous implicit values:
 both value factory of type => ContainerFactory[List]
 and value factory2 of type => ContainerFactory[Set]
 match expected type ContainerFactory[M]
       var j: List[Int] =  wrapItemInContainer(9)

非常感谢任何想法或提示!

-克里斯

您需要使 M 中的工厂协变。参见 https://groups.google.com/forum/#!topic/scala-language/dQEomVCH3CI and https://issues.scala-lang.org/browse/SI-7332

这样编译:

import language.higherKinds

trait Factory[+M[_], A] { def put(x: A): M[A] }

implicit def factory1[A] =
  new Factory[List, A] { def put(x: A) = List(x) }
implicit def factory2[A] =
  new Factory[Set, A] { def put(x: A) = Set(x) }

def wrap[M[_], A](a: A)(
    implicit factory: Factory[M, A]): M[A] =
  factory.put(a)

val j: Set[String] = wrap("foo")
val k: List[Int] = wrap(9)

我冒昧地缩短了你的名字。我还按照您在 收到的建议将 A 设为工厂本身的类型参数。