提取器对象中的显然unapply/unapplySeq不支持隐式参数。这里假设一个有趣的参数 a,以及一个令人不安的无处不在的参数 b,在提取 c 时隐藏起来会很好。
[编辑]:我的智能/scala插件安装中似乎有什么东西被破坏了,导致了这种情况。我无法解释。我最近在我的智力上遇到了许多奇怪的问题。重新安装后,我无法再回复我的问题。确认取消应用/取消应用Seq确实允许隐式参数!感谢您的帮助。
这不起作用(**编辑:是的,它确实如此(:**
trait A; trait C; trait B { def getC(a: A): C }
def unapply(a:A)(implicit b:B):Option[C] = Option(b.getC(a))
在我对理想提取器应该是什么样子的理解中,Java人员的意图也直观地清楚,这种限制基本上禁止依赖于附加参数的提取器对象。
您通常如何处理此限制?
到目前为止,我有这四种可能的解决方案:
1(我想改进的最简单的解决方案:不要隐藏b,提供参数b和a,作为以元组形式取消应用的正常参数:
object A1{
def unapply(a:(A,B)):Option[C] = Option(a._2.getC(a._1)) }
在客户端代码中:
val c1 = (a,b) match { case A1(c) => c1 }
我不喜欢它,因为有更多的噪音偏离了将 a 解构为 c 在这里很重要。此外,由于必须说服Java人员实际使用这个scala代码,他们面临着一个额外的合成新颖性(元组大括号(。他们可能会受到反斯卡拉的攻击"这都是什么?...那为什么不首先使用普通方法并检查是否?
2( 在封装对特定 B 的依赖关系的类中定义提取器,导入该实例的提取器。在导入站点对于java人来说有点不寻常,但是在模式匹配站点b隐藏得很好,并且直观地可以看到会发生什么。我的最爱。我错过了一些缺点?
class BDependent(b:B){
object A2{
def unapply(a:A):Option[C] = Option(b.getC(a))
} }
客户端代码中的用法:
val bDeps = new BDependent(someB)
import bDeps.A2
val a:A = ...
val c2 = a match { case A2(c) => c }
}
3( 在客户端代码范围内声明提取器对象。b 是隐藏的,因为它可以在局部范围内使用"b"。阻碍代码重用,严重污染客户端代码(此外,必须在代码使用它之前说明(。
4( 取消应用返回函数 B 的选项 => C。这允许导入和使用依赖于 ubitious 参数的提取器,而无需直接向提取器提供 b,而是在使用时提供结果。Java 人员可能会对函数值的使用感到困惑,b 没有隐藏:
object A4{
def unapply[A,C](a:A):Option[B => C] = Option((_:B).getC(a))
}
然后在客户端代码中:
val b:B = ...
val soonAC: B => C = a match { case A4(x) => x }
val d = soonAC(b).getD ...
进一步备注:
- 如本答案所示,"视图边界"可能有助于提取器使用隐式转换,但这对隐式参数没有帮助。出于某种原因,我宁愿不使用隐式转换来解决。
- 研究了"上下文边界",但它们似乎具有相同的限制,不是吗?
你的第一行代码在什么意义上不起作用?当然,不会任意禁止提取器方法的隐式参数列表。
考虑以下设置(我使用普通的旧类而不是案例类来表明这里没有额外的魔术发生(:
class A(val i: Int)
class C(val x: String)
class B(pre: String) { def getC(a: A) = new C(pre + a.i.toString) }
现在我们定义一个隐式B
值,并使用 unapply
方法创建一个提取器对象:
implicit val b = new B("prefix: ")
object D {
def unapply(a: A)(implicit b: B): Option[C] = Option(b getC a)
}
我们可以像这样使用:
scala> val D(c) = new A(42)
c: C = C@52394fb3
scala> c.x
res0: String = prefix: 42
完全符合我们的预期。我不明白为什么您需要在这里解决方法。
您遇到的问题是隐式参数是编译时(静态(约束,而模式匹配是一种运行时(动态(方法。
trait A; trait C; trait B { def getC(a: A): C }
object Extractor {
def unapply(a: A)(implicit b: B): Option[C] = Some(b.getC(a))
}
// compiles (implicit is statically provided)
def withImplicit(a: A)(implicit b: B) : Option[C] = a match {
case Extractor(c) => Some(c)
case _ => None
}
// does not compile
def withoutImplicit(a: A) : Option[C] = a match {
case Extractor(c) => Some(c)
case _ => None
}
所以这是一个概念性问题,解决方案取决于你实际想要实现的目标。如果需要类似于可选隐式的内容,可以使用以下内容:
sealed trait FallbackNone {
implicit object None extends Optional[Nothing] {
def toOption = scala.None
}
}
object Optional extends FallbackNone {
implicit def some[A](implicit a: A) = Some(a)
final case class Some[A](a: A) extends Optional[A] {
def toOption = scala.Some(a)
}
}
sealed trait Optional[+A] { def toOption: Option[A]}
然后你implicit b: B
的地方,你将有implicit b: Optional[B]
:
object Extractor {
def unapply(a:A)(implicit b: Optional[B]):Option[C] =
b.toOption.map(_.getC(a))
}
def test(a: A)(implicit b: Optional[B]) : Option[C] = a match {
case Extractor(c) => Some(c)
case _ => None
}
以下两者都编译:
test(new A {}) // None
{
implicit object BImpl extends B { def getC(a: A) = new C {} }
test(new A {}) // Some(...)
}