【问题标题】:Type mismatch found Unit, required Future[Customer] on flatmap发现类型不匹配 单位,平面地图上需要 Future[Customer]
【发布时间】:2017-08-02 03:46:44
【问题描述】:

我有以下代码,在我的 findOrCreate() 函数中,我收到一条错误消息,提示 Type mismatch found Unit, required Future[Customer]。在findOrCreate() 内部调用的customerByPhone() 函数也包含期待Futures 的调用,这就是我使用fatmap 的原因。我不知道为什么平面图的结果是Unit。我做错了什么?

override def findOrCreate(phoneNumber: String, creationReason: String): Future[AvroCustomer] =  {

  //query for customer in db
  val avroCustomer: Future[AvroCustomer] = customerByPhone(phoneNumber).flatMap(_ => createUserAndEvent(phoneNumber, creationReason, 1.0))

}

override def customerByPhone(phoneNumber: String): Future[AvroCustomer] = {
  val query = Schema.Customers.byPhoneNumber(phoneNumber)
  val dbAction: DBIO[Option[Schema.Customer]] = query.result.headOption

  db.run(dbAction)
    .map(_.map(AvroConverters.toAvroCustomer).orNull)
}

private def createUserAndEvent(phoneNumber: String, creationReason: String, version: Double): Future[AvroCustomer] = {

  val query = Schema.Customers.byPhoneNumber(phoneNumber)
  val dbAction: DBIO[Option[Schema.Customer]] = query.result.headOption

  val data: JsValue = Json.obj(
    "phone_number" -> phoneNumber,
    "agent_number" -> "placeholder for agent number",
    "creation_reason" -> creationReason
  )
  //empty for now
  val metadata: JsValue = Json.obj()

  //creates user
  val avroCustomer: Future[AvroCustomer] = db.run(dbAction).map(_.map(AvroConverters.toAvroCustomer).orNull)
  avroCustomer.onComplete({
    case Success(null) => {

    }

    //creates event
    case Success(customer) => {
      val uuid: UUID = UUID.fromString(customer.id)
      //create event
      val event: Future[CustomerEvent] = db.run(Schema.CustomerEvents.create(
        uuid,
        "customer_creation",
        version,
        data,
        metadata)
      ).map(AvroConverters.toAvroEvent)
    }
    case Failure(exception) => {

    }
  })

  Future.successful(new AvroCustomer)
}

【问题讨论】:

  • val 语句返回 Unit
  • @Reactormonk 据我所知,val 只是使变量不可变,但您可以返回任何值?
  • val foo: Int = { val bar = 32 } 会给你一个编译错误。
  • val 创建具有指定值的不可变变量,但它不会像在 C 等语言中那样返回该值。

标签: scala functional-programming future flatmap


【解决方案1】:

虽然 Reactormonk 基本上在 cmets 中回答了这个问题,但我实际上会写一个包含一些细节的答案。他关于val 语句产生Unit 的评论基本上是正确的,但我希望一些详细说明会使事情更清楚。

我看到的关键元素是val 是一个声明。 Scala 中的声明是不产生有用值的语句。由于 Scala 的函数性质,它们确实会产生一个值,但它是 Unit,并且由于只有一个 Unit 实例,它没有任何意义。

刚接触 Scala 的程序员经常想做这样的事情的原因是他们不将代码块视为语句,并且经常习惯于在其他语言中使用 return。所以让我们在这里考虑一个简化的函数。

def foo(i: Int): Int = {
  42 * i
}

我包含一个代码块,因为我认为这是导致此错误的关键,尽管这里确实不需要它。代码块的值就是代码块中最后一个表达式的值。这就是我们不必指定return 的原因,但是大多数习惯return 的程序员对块末尾的那种裸露的表达有点不舒服。这就是为什么很想加入val 声明的原因。

def foo(i: Int): Int = {
  val result = 42 * i // Error: type mismatch.
}

当然,如前所述,但val 导致Unit 使此错误。您可以仅使用 result 添加额外的一行,这样可以编译,但它过于冗长且不习惯。

Scala 支持使用return 来留下一个方法/函数并返回一个特定的值,尽管 us 通常不被接受。因此,以下代码有效。

def foo(i: Int): Int = {
  return 42 * i
}

虽然您不应该在 Scala 代码中使用 return,但我觉得想象它的存在有助于理解这里出了什么问题。如果你在val 前面加上return,你会得到如下代码。

def foo(i: Int): Int = {
  return val result = 42 * i // Error: type mismatch.
}

至少对我来说,这段代码显然是不正确的。 val 是一个声明,因此它不适用于return。习惯块作为表达式的功能样式需要一些时间。在您达到这一点之前,它可能会有所帮助,就像在方法末尾有一个 return 而不实际将其放在那里一样。

值得注意的是,在 cmets 中,jwvh 声称在 C 中这样的声明会返回一个值。那是错误的。在大多数 C 系列语言中,赋值会返回分配的值,因此 a = 5 返回值 5,但声明不返回,因此 int a = 5; 不会返回任何值并且可以'不能用作表达式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-24
    • 1970-01-01
    相关资源
    最近更新 更多