Scala 3 — 书籍

上下文参数

语言

Scala 为上下文抽象提供了两个重要功能

  • 上下文参数允许您指定在调用点处可以由程序员省略的参数,并且应由上下文自动提供。
  • 给定实例(在 Scala 3 中)或隐式定义(在 Scala 2 中)是可以由 Scala 编译器用来填充缺失参数的术语。

上下文参数

在设计系统时,通常需要向系统的不同组件提供上下文信息,例如配置或设置。实现此目的的一种常见方法是将配置作为附加参数传递给您的方法。

在以下示例中,我们定义了一个案例类 Config 来对某些网站配置进行建模,并在不同方法中传递它。

case class Config(port: Int, baseUrl: String)

def renderWebsite(path: String, config: Config): String =
  "<html>" + renderWidget(List("cart"), config)  + "</html>"

def renderWidget(items: List[String], config: Config): String = ???

val config = Config(8080, "docs.scala-lang.org")
renderWebsite("/home", config)

让我们假设在大多数代码库中配置不会更改。将 config 传递给每个方法调用(如 renderWidget)变得非常繁琐,并且使我们的程序更难阅读,因为我们需要忽略 config 参数。

将参数标记为上下文

我们可以将某些方法的参数标记为上下文

def renderWebsite(path: String)(implicit config: Config): String =
    "<html>" + renderWidget(List("cart")) + "</html>"
    //                                  ^
    //                   no argument config required anymore

def renderWidget(items: List[String])(implicit config: Config): String = ???
def renderWebsite(path: String)(using config: Config): String =
    "<html>" + renderWidget(List("cart")) + "</html>"
    //                                  ^
    //                   no argument config required anymore

def renderWidget(items: List[String])(using config: Config): String = ???

通过在 Scala 3 中使用关键字 using 或在 Scala 2 中使用 implicit 开始一个参数部分,我们告诉编译器在调用站点它应该自动查找具有正确类型的参数。因此,Scala 编译器执行项推断

在我们对 renderWidget(List("cart")) 的调用中,Scala 编译器将看到作用域中有一个类型为 Config 的项(config),并自动将其提供给 renderWidget。因此,该程序等同于上面的程序。

事实上,由于我们不再需要在 renderWebsite 的实现中引用 config,我们甚至可以在 Scala 3 中在签名中省略其名称。

//        no need to come up with a parameter name
//                             vvvvvvvvvvvvv
def renderWebsite(path: String)(using Config): String =
    "<html>" + renderWidget(List("cart")) + "</html>"

在 Scala 2 中,隐式参数的名称仍然是必需的。

明确提供上下文参数

我们已经了解了如何对上下文参数进行抽象,并且 Scala 编译器可以自动为我们提供参数。但是,我们如何指定在对 renderWebsite 的调用中使用哪个配置?

我们明确提供参数值,就像它是一个常规参数一样。

renderWebsite("/home")(config)

就像我们使用 using 指定参数部分一样,我们还可以使用 using 明确提供上下文参数。

renderWebsite("/home")(using config)

如果作用域中有多个不同的值有意义,并且我们希望确保将正确的值传递给函数,那么明确提供上下文参数会很有用。

对于所有其他情况,正如我们将在下一节中看到的那样,还有一种方法可以将上下文值引入作用域。

给定实例(Scala 2 中的隐式定义)

我们已经看到,我们可以明确地将参数作为上下文参数传递。但是,如果对于特定类型存在单个规范值,还有另一种首选方式可以将其提供给 Scala 编译器:在 Scala 3 中将其标记为 given,在 Scala 2 中标记为 implicit

implicit val config: Config = Config(8080, "docs.scala-lang.org")
//           ^^^^^^
// this is the value the Scala compiler will infer
// as argument to contextual parameters of type Config
val config = Config(8080, "docs.scala-lang.org")

// this is the type that we want to provide the
// canonical value for
//    vvvvvv
given Config = config
//             ^^^^^^
// this is the value the Scala compiler will infer
// as argument to contextual parameters of type Config

在上面的示例中,我们指定每当当前作用域中省略类型为 Config 的上下文参数时,编译器应将 config 推断为参数。

在为类型 Config 定义了规范值后,我们可以按如下方式调用 renderWebsite

renderWebsite("/home")
//                   ^
//   again no argument

有关 Scala 在哪里查找规范值的详细指南,请参阅 常见问题解答

此页面的贡献者