Seq上的Scala隐式转换在这里发生了什么?

时间:2016-11-02 19:09:03

标签: scala implicit-conversion

我正在阅读Debasish Ghosh的新书“功能和反应域建模”,我真的非常喜欢它。

第5章中让我困惑的一件事是下面的行:

Reporting.report(accts).foreach(println _)

可以使用Seq [Account]并将其转换为Seq [Show]。我知道implicits正在发挥作用,但编译器采取了哪些步骤来允许编译?这只是更一般的隐含规则的特定实例吗? 似乎就像编译器将Show trait混合到Account对象中一样。谢谢!

改编自第164页:

import scala.util.Try

trait Show[T] {
  def shows(t: T): Try[String]
}

trait ShowProtocol {
  implicit val showAccount: Show[Account]
  implicit val showCustomer: Show[Customer]
}

trait DomainShowProtocol extends ShowProtocol {
  override implicit val showAccount: Show[Account] = (t: Account) => Try("Account")
  override implicit val showCustomer: Show[Customer] = (t: Customer) => Try("Customer")
}

case class Account()
case class Customer()

object Reporting {
  def report[T: Show](as: Seq[T]): Seq[Try[String]] = as.map(implicitly[Show[T]].shows _)
}

object DomainShowProtocol extends DomainShowProtocol

object Main {
  def main(args: Array[String]): Unit = {
    import DomainShowProtocol._

    val accts: Seq[Account] = Seq(
      Account(),
      Account(),
      Account()
    )

    Reporting.report(accts).foreach(println _)
  }
}

2 个答案:

答案 0 :(得分:3)

这是类型模式的非常简单的使用。所有的"魔法"发生在report函数中。

首先注意类型参数:

def report[T: Show]

这意味着无论T类型是什么,呼叫站点的范围内都必须有隐式Show[T]。在Main中,调用该函数TAccount,因此需要隐式Show[Account]在该行的范围内。由于Main混合在DomainShowProtocol中,隐式val showAccount在范围内,因此满足要求。

现在在report的正文中,我们看到implicitly[Show[T]]的使用。这只是返回对范围内所需的Show[T]的引用,因此在这种情况下它等于showAccount

最后,在隐式返回的值上调用show方法,将Seq的当前元素作为参数传入。这会将每个Account转换为Try[String],从而将Seq转换为整体。

如果我们删除所有隐式魔法,则该方法及其调用如下所示:

//in Reporting
def report[T](as: Seq[T])(show: Show[T]): Seq[Try[String]] = {
  as.map{t => show.shows(t)}
}

//in Main
Reporting.report(accts)(accountsShow).foreach(println _)

答案 1 :(得分:1)

句法糖

def report[T: Show](seq: Seq[T]) 

的语法糖
def report(seq: Seq[T])(implicit evidence: Show[T])

大概你可以假设

[T: Show]

完成

的工作
(implicit evidence: Show[T])

implicitly[Show[T]]

只不过是隐式Show [T]

的引用

特质DomainShowProtocol有隐含证据Show[Account]

object DomainShowProtocol extends DomainShowProtocol

现在使用隐式对象DomainShowProtocol导入范围。

report方法可以将Seq[Account]转换为Seq[Try[String]],因为来自对象implicit的{​​{1}}证据又来自特征{{1} }}

DomainShowProtocol

以上函数是

的语法糖
DomainShowProtocol

此处def report[T: Show](as: Seq[T]): Seq[Try[String]] = as.map(implicitly[Show[T]].shows _) def report(as: Seq[T])(implicit evidence: Show[T]): Seq[Try[String]] = as.map(evidence.shows _) ,隐式证据T来自对象Account。这就是这种转换的可能性。