为什么Scala中的匹配是作为关键字而不是作为方法实现的?

时间:2013-10-27 15:22:01

标签: scala pattern-matching scala-macros

让我思考的是为什么以下

<something> match { ... }

无法重写为

<something>.match({ ... })  # error: identifier expected but 'match' found.

我想这是因为将match作为常规方法实现是不可能的,但我不确定。或许这是出于性能原因。

此外,既然宏已经可用,那么match是否可以使用宏来实现? (不是应该这样做,而只是假设)

编辑:这似乎与例如What is scala's experimental virtual pattern matcher?;感谢@ om-nom-nom指出它。

2 个答案:

答案 0 :(得分:2)

通过将其作为关键字,它不需要与Any类型相关联,因此编译器可以自由地推断(部分)函数的输入类型。如果它是Any的方法,则将函数[A,B]作为参数。

实际意义是

3 match { case "foo" => "bar" }

导致编译错误'类型不匹配'

但是a(type paramerterless)matchMethod;

3.matchMethod { case "foo" => "bar" }

导致运行时异常'scala.MatchError'

那么即使我们详细地明确地对这些类型进行了参数化,我们仍然不会因以下情况而出现编译错误:

"foo".matchMethod[Int, String] { case 3 => "bar" }

相反,我们会得到运行时异常'java.lang.ClassCastException',因为我们必须使用.asInstanceOf。

另一个好处是语法高亮,匹配跳出代码比另一种方法更多,我相信它应该得到,因为模式匹配是Scala的一个关键部分,值得特别关注。

附加:出于类似的原因,您希望try catch是一个关键字构造,而不是一个将两个函数作为参数的函数。匹配与catch一致,这也与Java一致。

这个答案是Martin Odersky的扩展,TravisBrown首先指出

答案 1 :(得分:2)

samthebest的答案可能是真正的原因(我不知道,这不是我熟悉的事情),但是还有另一种方式来看待它与更普遍的函数式编程有关。

众所周知,函数式编程中的匹配可以在没有任何特殊语言特性的情况下完成(Church Encoding

trait List[+T] {
    def mmatch[R](nil: => R, cons: (T, List[T]) => R): R
}
object Nil extends List[Nothing] {
    def mmatch[R](nil: => R, cons: (Nothing, List[Nothing]) => R) = nil
}
class Cons[+T](head: T, tail: List[T]) extends List[T] {
    def mmatch[R](nil: => R, cons: (T, List[T]) => R) = cons(head, tail)
}

def sum(l: List[Int]): Int = l mmatch (
    nil = 0,
    cons = (x, xs) => x + sum(xs)
)

val list = new Cons(1, new Cons(2, Nil))

println(sum(list))

在此解释中,当你写

sealed trait List[+T]
case object Nil extends List[Nothing]
case class Cons[+T](head: T, tail: List[T]) extends List[T]

单词sealed是提供match函数的值/术语。

因此,阅读问题的一种方法是,为什么不这样做呢?为什么不用其他基本语言功能构建匹配而不是提供句法匹配?

原因是句法match提供了一些人们喜欢的语法糖:

  • 重叠匹配功能:

    sealed trait A
    sealed trait B
    case object X extends A
    case object Y extends A with B
    case object Z extends B
    
  • 嵌套匹配函数:

    (1 :: 2 :: Nil) match {
        case x :: y :: Nil => ???
    }
    

    在没有语法糖的情况下写这是非常尴尬的。你能行的;我在trying to implement monadic extractors时试图探索这种可能性。但肯定不那么漂亮。

  • 自动选择开放与封闭匹配功能。

    也就是说,Scala中的提取器就像开放匹配函数一样,因为任何函数都可能因返回None而失败;编译器不会检查完整的match,但您可以根据需要链接多个,Scala选择第一个。另一方面,sealed特征提供闭合匹配函数,具有完整性检查的好处。这些需要由单独的函数提供,但Scala允许您对两者使用相同的match语法。

就我个人而言,我怀疑上述要求最终不需要为比赛提供特殊的句法支持。我怀疑其他更通用的语言功能最终可以提供相同的好处,特别是在嵌套匹配方面。但是,目前使用特殊的match语法直接解决问题更有意义。