简体   繁体   中英

Scala - what is this map + case syntax?

I can't seem to find further information on this language feature where you can put a case inside a map like this:

   val sVersion = CrossVersion.partialVersion(scalaVersion)
      .map { case (major:Long, minor: Long) => s"_$major.$minor" } getOrElse ""

I've been starting to use this syntax, but what is it called and where can I find more information on it?

I've been starting to use this syntax, but what is it called […]

It is officially called a Pattern Matching Anonymous Function . The Scala community also sometimes refers to it as a Partial Function Literal .

[…] and where can I find more information on it?

The ultimate source of truth for everything in the Scala Programming Language is the Scala Language Specification (aka SLS ) . You will note that I have linked to version 2.13 of the spec – unfortunately, a single consolidated spec for Scala 3 has not been published yet.

See Scala Language Specification 8.5 Pattern Matching Anonymous Functions for all the gory details. Simply put, the expression

{
  case p1 => e1
  case p2 => e2
  // …
  case pn => en
}

is equivalent to

(x1: S1, x2: S2, /* … */, xn: Sn) => (x1, x2, /* … */, xn) match {
  case p1 => e1
  case p2 => e2
  // …
  case pn => en
}

provided that the result type is SAM-convertible to FunctionN[S1, S2, /* … */, Sn, R] , or as a special casePartialFunction1[S1, R] (which is where the name Partial Function Literal comes from.)

In Scala 3, there is a new feature which can make this code even more concise: For a very long time, the Scala Designers wanted to unify Tuples and Argument Lists . In other words, they wanted to make it so that methods in Scala only ever take one argument, and that argument is a tuple. Unfortunately, it turned out that a) this massively breaks backwards-compatibility and b) massively breaks platform interoperability.

Now, Scala 3 was an opportunity to ignore problem a), but you cannot ignore problem b) since one of the major design goals of Scala is to have seamless, tight, good, performant integration with the underlying host platform (eg .NET in the case of the now-abandoned Scala.NET, the ECMASCript / HTML5 / DOM / WebAPI platform in the case of Scala.js, the native Operating System in the case of Scala-native, or the Java platform (JRE, JDK, JVM, J2SE, J2EE, Java, Kotlin, Clojure, etc.) in the case of Scala-JVM).

However, the Scala designers managed to find a compromise, where arguments and tuples are not the same thing, but parameters can be easily converted to tuples and tuples can be easily converted to arguments.

This is called Parameter Untupling , and it basically means that a function of type FunctionN[S1, S2, /* … */, Sn, R] can be automatically converted to a function of type Function1[(S1, S2, /* … */, Sn), R] which is syntactic sugar for Function1[TupleN[S1, S2, /* … */, Sn], R] .

Simply put,

(p1: S1, p2: S2, /* … */, pn: Sn) => e: R

can automatically be converted to

(x: (S1, S2, /* … */, Sn)) => {
  val p1: S1 = x._1
  val p2: S2 = x._2
  // …
  val pn: Sn = x._n

  e
}

With this, your code would look like:

val sVersion = CrossVersion.partialVersion(scalaVersion)
  .map { (major: Long, minor: Long) => s"_$major.$minor" } getOrElse ""

Note: unfortunately, there is no comprehensive specification of Scala 3 yet. There is a partial Language Reference , which however only describes differences to Scala 2. So, you typically have to bounce back and forth between the SLS and the Scala 3 docs.

In Scala 2, it's called pattern-matching decomposition

see: https://docs.scala-lang.org/scala3/reference/other-new-features/parameter-untupling.html

in Scala 3, you can just

val sVersion = CrossVersion.partialVersion(scalaVersion)
      .map { (major:Long, minor: Long) => s"_$major.$minor" } getOrElse ""

This is usually called "destructuring" or "destructuring bind" (ex: in Clojure https://clojure.org/guides/destructuring ). Scala uses pattern matching to achieve this.

In your example, you use shortened pattern matching to destructure the Option which contains the version. You could also skip the explicit Long type declaration.

Good reads on this topic:

https://www.oreilly.com/library/view/programming-scala-2nd/9781491950135/ch04.html https://docs.scala-lang.org/cheatsheets/index.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM