【问题标题】:How to enforce a context bound on a wildcard in Scala?如何在Scala中强制使用通配符绑定上下文?
【发布时间】:2016-12-09 19:34:59
【问题描述】:

我有一个这样设置的隐式助手:

trait Helper[T] {
  def help(entry: T): Unit
}

object Helpers {
  implicit object XHelper extends Helper[X] {
    override def help(entry: X): Unit = {println("x")}
  }
  implicit object YHelper extends Helper[Y] {
    override def help(entry: Y): Unit = {println("y")}
  }

  def help[T](entry: T)(implicit helper: Helper[T]): Unit = {
    helper.help(entry)
  }
}

我想设置一个元素集合并在每个元素上运行help。但是,由于我们不能保证所有元素都匹配Helpers,因此下面给出了编译器错误:

val data = Seq[_](new X(), new Y())
data.foreach(entry => Helpers.help(entry))

如果我们有一个泛型类型T,我们可以使用[T: Helper] 对其强制执行隐式约束,但这不适用于_。如何强制data 的每个元素都有匹配的Helper

【问题讨论】:

  • 你能扩展类型 XY 一个新的 trait,比如 trait HasHelp
  • 不,很遗憾,该代码不在我的控制范围内。

标签: scala generics wildcard implicit


【解决方案1】:

在 Scala 上下文中,class A[T: Typeclass] 之类的绑定只是 class A[T](implicit ev: Typeclass[T]) 的语法糖。与T <: BaseT >: Super 不同,上下文绑定实际上并不是类型签名的一部分,因此您不能拥有像val b: Box[T: Typeclass] 这样的签名。

如果你想对某个容器的元素运行 typeclass 操作,你必须将相关的 typeclass 实例与容器中的值打包在一起。

一个可能的实现可能如下所示:

import language.higherKinds
import language.implicitConversions

// Class that packs values with typeclass instances
class WithTC[T, TC[_]](t: T)(implicit tc: TC[T]) {
  // Some helper methods to simplify executing typeclass operations
  // You may just make `t` and `tc` public, if you wish.
  def apply[U](op: (TC[T], T) => U) = op(tc, t)
  def apply[U](op: T => TC[T] => U) = op(t)(tc)
}
object WithTC {
  // Implicit conversion to automatically wrap values into `WithTC`
  implicit def apply[T, TC[_]](t: T)(implicit tc: TC[T]): WithTC[T, TC] =
    new WithTC(t)(tc)
}

然后你可以用存在类型的元素创建一个序列:

import Helpers._
val data: Seq[(T WithTC Helper) forSome { type T }] = Seq(new X(), new Y())

并对序列元素执行类型类操作:

// The following lines produce equivalent results
data.foreach(_(_ help _))
data.foreach(_(t => implicit tc => Helpers.help(t)))
data.foreach(_(t => Helpers.help(t)(_)))

【讨论】:

    【解决方案2】:

    Seq 这样的类型是不可能的,因为它只针对所有元素通用的一种元素类型进行参数化。

    但是,您可以使用Shapeless HLists 和多态函数(Poly)来实现这一点:

    class X
    class Y
    
    trait Helper[T] {
      def help(entry: T): Unit
    }
    
    object Helpers {
      implicit object XHelper extends Helper[X] {
        override def help(entry: X): Unit = println("x")
      }
    
      implicit object YHelper extends Helper[Y] {
        override def help(entry: Y): Unit = println("y")
      }
    }
    
    import shapeless._
    
    object helper extends Poly1 {
      implicit def tCase[T: Helper]: Case.Aux[T, Unit] = 
        at(implicitly[Helper[T]].help(_))
    }
    
    val hlist = new X :: new Y :: HNil
    hlist.map(helper)
    
    // Output:
    x
    y
    

    【讨论】:

    • 令我惊讶的是这是不可能的,因为我可以做Seq[_ <: SomeType]之类的事情。感觉应该有一个运营商,还是没有一个运营商的原因?
    • T: Helper 这样的上下文绑定只是方法中(implicit ev: Helper[T]) 的语法糖,所以我不确定它如何处理值的参数化。
    猜你喜欢
    • 2021-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-26
    • 2023-04-01
    • 2011-02-28
    • 2011-04-14
    • 1970-01-01
    相关资源
    最近更新 更多