【问题标题】:Typeclass ops : Enable to find implicit value for parameter类型类操作:启用以查找参数的隐式值
【发布时间】:2021-02-09 14:15:23
【问题描述】:

我是 typeclass 模式的新手,并且在 scala 中是隐式的,下面是我为测试此模式所做的代码 sn-p,但我不知道为什么 compareB 方法无法找到参数的隐式值 wrapper ,即使我声明了一个 IntWrapper[Int] 类型的隐式字段。

有人知道 typeclass 中的隐式是如何解决的吗?为什么下面的代码不能编译?

谢谢

trait IntWrapper[T]{
  def test: Boolean
}

trait ProductComparator[T] extends Serializable{
  def compare(p1 : T, p2: T): Boolean
}

object ProductComparator{

  def apply[A](implicit cmp: ProductComparator[A]) : ProductComparator[A] = cmp


  implicit def w: IntWrapper[Int] =
    new IntWrapper[Int] {
      override def test: Boolean = true
    }

  implicit def CompareB[T <: Product, Repr <: HList, KRepr <: HList](implicit gen: LabelledGeneric.Aux[T, Repr], keys: Keys.Aux[Repr, KRepr], wrapper:  IntWrapper[Int]) : ProductComparator[T] =
      new ProductComparator[T] {
        override def compare(p1: T, p2: T): Boolean = {
          p1.productArity == p2.productArity
        }
      }

}


case class Emp(a: Int, b: Int)

object Demo extends App{
  val comparator = implicitly[ProductComparator[Emp]]
}

【问题讨论】:

  • CompareB 中,您实际上并没有使用任何隐式gen: LabelledGeneric.Aux[T, Repr], keys: Keys.Aux[Repr, KRepr], wrapper: IntWrapper[Int]。所以我想这只是一个例子。对吗?

标签: scala typeclass implicit shapeless


【解决方案1】:

问题是范围。行内:

val comparator = implicitly[ProductComparator[Emp]]

方法:

def apply[A](implicit cmp: ProductComparator[A]): ProductComparator[A] = cmp

将被调用,其中AEmp。可以创建它的隐式是CompareBCompareB 需要更多暗示。前2个,来自进口。所以他们在范围内。变量w 是在object ProductComparator 上定义的,因此它不在您定义val comparator 的范围内。

要将其添加到作用域,您有几个选择:

  1. 通过以下方式导入:

    import ProductComparator.w
    
  2. implicit def w: IntWrapper[Int] 移动到与trait IntWrapper[T] 相同的范围内,这使得:

import shapeless.ops.record.Keys
import shapeless.{HList, LabelledGeneric}

trait IntWrapper[T]{
  def test: Boolean
}

implicit def w: IntWrapper[Int] =
  new IntWrapper[Int] {
    override def test: Boolean = true
  }

trait ProductComparator[T] extends Serializable{
  def compare(p1 : T, p2: T): Boolean
}

object ProductComparator{

  def apply[A](implicit cmp: ProductComparator[A]) : ProductComparator[A] = cmp

        implicit def CompareB[T <: Product, Repr <: HList, KRepr <: HList](implicit gen: LabelledGeneric.Aux[T, Repr], keys: Keys.Aux[Repr, KRepr], wrapper:  IntWrapper[Int]) : ProductComparator[T] =
          new ProductComparator[T] {
            override def compare(p1: T, p2: T): Boolean = {
              p1.productArity == p2.productArity
            }
          }

}

case class Emp(a: Int, b: Int)

object Demo extends App{
  val comparator = implicitly[ProductComparator[Emp]]
}

要了解更多关于 Scala 在哪里寻找隐式的信息,您可以阅读Daniels brilliant post

【讨论】:

  • 我不想手动创建 ProductComparator[Emp],实际上隐式 def CompareB 会为我创建它,我的问题是,为什么 compareB 无法找到包装器:IntWrapper[Int] 参数她的范围?请注意,当我删除此参数时,代码会编译
  • 要删除的参数是 {wrapper: IntWrapper[Int]}
  • 对。所以你真的需要它吗?这里的问题是范围界定。您需要在您的主应用程序中import ProductComparator.w,然后它会找到它。
  • @TomerShetah “前 2 个来自进口。所以它们在范围内。” 实际上,隐含 LabelledGenericKeys不要来自进口。 LabelledGenericKeys 的导入将它们的名称带到本地范围,而不是隐含本身。 LabelledGenericKeys 的隐含在 隐式范围 中,因为它们是在 LabelledGenericKeys 的伴随对象中定义的。
【解决方案2】:

通常,TC[A] 类型的隐式被放入 TC 的伴随对象或 A 的伴随对象。

所以转移

implicit def w: IntWrapper[Int] =
  new IntWrapper[Int] {
    override def test: Boolean = true
  }

ProductComparator的伴生对象到IntWrapper的伴生对象

object IntWrapper {
  implicit def w: IntWrapper[Int] =
    new IntWrapper[Int] {
      override def test: Boolean = true
    }
}

然后代码应该可以编译了。

或者,您可以按照 @TomerShetah 的建议导入 w

Where does Scala look for implicits?

当你定义时

implicit val foo: Foo = ???

def bar(implicit foo1: Foo) = ???

(在您的示例中,foowbarCompareBFooIntWrapper[Int]) 你通常不应该假设foo1foofoo 定义在当前范围内,foo1 将在 bar 调用站点范围内解析。这可以是foo,如果它在一个范围内或其他一些隐含的地方。

When doing implicit resolution with type parameters, why does val placement matter?

Setting abstract type based on typeclass

Reverse HList and convert to class?

【讨论】:

    猜你喜欢
    • 2018-03-16
    • 2013-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-18
    • 1970-01-01
    相关资源
    最近更新 更多