隐式参数不适用于unapply。如何隐藏提取器中无处不在的参数?

时间:2012-09-09 10:55:01

标签: scala pattern-matching implicit extractor

在提取器对象中显然不应用/ unapplySeq不支持隐式参数。假设这里有一个有趣的参数a,以及一个令人不安的无处不在的参数b,当提取c时会很好地隐藏起来。

[编辑]:我的intellij / scala-plugin安装中出现了一些问题,导致了这种情况。我无法解释。我最近和intellij有很多奇怪的问题。重新安装后,我再也不能重新解决我的问题了。确认unapply / unapplySeq允许隐式参数!谢谢你的帮助。

这不起作用(**编辑:是的,确实如此):**

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,作为元组形式的unapply的正常参数:

object A1{ 
    def unapply(a:(A,B)):Option[C] = Option(a._2.getC(a._1)) }

在客户端代码中:

 val c1 = (a,b) match { case A1(c) => c1 }

我不喜欢它,因为有更多的噪音偏离,解构为c在这里很重要。此外,由于必须确信实际使用此scala代码的java人员面临一个额外的合成新颖性(元组括号)。他们可能会得到反斯卡拉的侵略“这是什么?...为什么不首先使用正常的方法并检查是否?”。

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-parameter的提取器,而不直接向提取器提供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 ...

进一步评论:

  • 正如this answer中所建议的那样,“查看边界”可能有助于提取器使用隐式转换,但这对隐式参数没有帮助。出于某种原因,我不想使用隐式转换来解决这个问题。
  • 查看“上下文界限”,但它们似乎有相同的限制,不是吗?

2 个答案:

答案 0 :(得分:4)

你的第一行代码在什么意义上不起作用?对于提取器方法,隐式参数列表当然没有任意禁止。

考虑以下设置(我使用简单的旧类而不是案例类来表明这里没有额外的魔法发生):

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

正如我们所期望的那样。我不明白为什么你需要一个解决方法。

答案 1 :(得分:1)

你遇到的问题是隐式参数是编译时(静态)约束,而模式匹配是运行时(动态)方法。

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(...)
}