【问题标题】:Scala - add unapply to IntScala - 添加不适用于 Int
【发布时间】:2012-03-15 19:59:46
【问题描述】:

我希望能够做到这一点:

scala> val Int(i) = "1"
i: Int = 1

但是Int 没有unapply 方法。

我找到了this answer,它提供了有关如何向现有对象隐式添加方法的说明,所以我尝试了一下。他们提供的解决方案有效,但不幸的是不适用于模式匹配。这是我所拥有的:

object UnapplyInt {
  val IntRE = """^(\d+)$""".r
  def unapply(v: String): Option[Int] = v match {
    case IntRE(s) => Some(s.toInt)
    case _ => None
  }
}
implicit def int2unapplyInt(objA: Int.type) = UnapplyInt

这些测试用例都很好:

val UnapplyInt(i) = "1"       // pattern matching with unapply is fine
val i = Int.unapply("1").get  // implicit conversion is fine

但我想要的失败了:

scala> val Int(i) = "1"
<console>:10: error: object Int is not a case class constructor, nor does it have an unapply/unapplySeq method
       val Int(i) = "1"
           ^

如果隐式转换有效并且与unapply 的模式匹配有效,为什么Scala 不将这两个东西放在一起进行隐式模式匹配?

【问题讨论】:

    标签: scala implicit-conversion unapply


    【解决方案1】:

    edit 所以我最初的推理并不好。真正的原因来自Section 8.1.8 of the Scala language spec

    Syntax:
        SimplePattern ::= StableId ‘(’ [Patterns] ‘)’
    

    也就是说,提取器对象必须是稳定的,而隐式转换是不稳定的。没有解释为什么提取器必须稳定;我怀疑这是因为 Scala 不想将提取器视为 表达式,因为这很快就会变得模棱两可:

    ... match {
        foo(bar)(baz)
    }
    

    现在哪个是构造函数,哪个是模式变量?

    幸运的是,您可以这样做并且效果很好(尽管正如您评论的那样,这会带来其他问题):

    object Int {
        def unapply(v: String) = try Some(v.toInt)
            catch { case _: NumberFormatException => None }
    }
    
    val Int(i) = "5"
    

    因为Int 类型和Int 对象在不同的​​命名空间中。

    【讨论】:

    • 哈!您提出的解决方案就是我目前正在使用的解决方案。我对此并不完全满意,因为它需要有两个不同的命名空间,这意味着我不能在同一个地方拥有 Int.MaxValueval Int(i) = "5"
    • @dhg 是的。现在我想得更多,我认为我的回答中的推理并不完全正确。真正的原因是模式表达式不能像正则表达式那样,因为 Scala 必须识别什么是构造函数,什么是变量......
    • 你在最后一部分失去了我。
    • 感谢您的回答。这似乎很常见,我实际上期待一个库函数..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-05
    • 2012-06-29
    • 2023-01-11
    相关资源
    最近更新 更多